diff --git a/app/app.go b/app/app.go index ab582fe..61fcf1f 100644 --- a/app/app.go +++ b/app/app.go @@ -503,7 +503,7 @@ func NewMinitiaApp( packetForwardMiddleware, ibchooks.NewICS4Middleware( nil, /* ics4wrapper: not used */ - ibcevmhooks.NewEVMHooks(app.EVMKeeper, ac), + ibcevmhooks.NewEVMHooks(appCodec, ac, app.EVMKeeper), ), app.IBCHooksKeeper, ) diff --git a/app/ibc-hooks/ack.go b/app/ibc-hooks/ack.go index d980336..21f0b0c 100644 --- a/app/ibc-hooks/ack.go +++ b/app/ibc-hooks/ack.go @@ -34,10 +34,11 @@ func (h EVMHooks) onAckIcs20Packet( if allowed, err := h.checkACL(im, ctx, callback.ContractAddress); err != nil { return err } else if !allowed { + // just return nil here to avoid packet stuck due to hook acl. return nil } - inputBz, err := h.asyncCallbackABI.Pack(functionNameAck, callback.Id, !isAckError(acknowledgement)) + inputBz, err := h.asyncCallbackABI.Pack(functionNameAck, callback.Id, !isAckError(h.codec, acknowledgement)) if err != nil { return err } diff --git a/app/ibc-hooks/ack_test.go b/app/ibc-hooks/ack_test.go new file mode 100644 index 0000000..769ec74 --- /dev/null +++ b/app/ibc-hooks/ack_test.go @@ -0,0 +1,119 @@ +package evm_hooks_test + +import ( + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "strings" + "testing" + + transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" + "github.com/ethereum/go-ethereum/common" + "github.com/holiman/uint256" + "github.com/initia-labs/minievm/x/evm/contracts/counter" + "github.com/stretchr/testify/require" +) + +func Test_onAckIcs20Packet_noMemo(t *testing.T) { + ctx, input := createDefaultTestInput(t) + _, _, addr := keyPubAddr() + _, _, addr2 := keyPubAddr() + + data := transfertypes.FungibleTokenPacketData{ + Denom: "foo", + Amount: "10000", + Sender: addr.String(), + Receiver: addr2.String(), + Memo: "", + } + + dataBz, err := json.Marshal(&data) + require.NoError(t, err) + + ackBz, err := json.Marshal(channeltypes.NewResultAcknowledgement([]byte{byte(1)})) + require.NoError(t, err) + + err = input.IBCHooksMiddleware.OnAcknowledgementPacket(ctx, channeltypes.Packet{ + Data: dataBz, + }, ackBz, addr) + require.NoError(t, err) +} + +func Test_onAckIcs20Packet_memo(t *testing.T) { + ctx, input := createDefaultTestInput(t) + _, _, addr := keyPubAddr() + evmAddr := common.BytesToAddress(addr.Bytes()) + + codeBz, err := hex.DecodeString(strings.TrimPrefix(counter.CounterBin, "0x")) + require.NoError(t, err) + + _, contractAddr, err := input.EVMKeeper.EVMCreate(ctx, evmAddr, codeBz) + require.NoError(t, err) + + abi, err := counter.CounterMetaData.GetAbi() + require.NoError(t, err) + + data := transfertypes.FungibleTokenPacketData{ + Denom: "foo", + Amount: "10000", + Sender: addr.String(), + Receiver: contractAddr.Hex(), + Memo: fmt.Sprintf(`{ + "evm": { + "async_callback": { + "id": 99, + "contract_address": "%s" + } + } + }`, contractAddr.Hex()), + } + + dataBz, err := json.Marshal(&data) + require.NoError(t, err) + + successAckBz := channeltypes.NewResultAcknowledgement([]byte{byte(1)}).Acknowledgement() + failedAckBz := channeltypes.NewErrorAcknowledgement(errors.New("failed")).Acknowledgement() + + // hook should not be called to due to acl + err = input.IBCHooksMiddleware.OnAcknowledgementPacket(ctx, channeltypes.Packet{ + Data: dataBz, + }, successAckBz, addr) + require.NoError(t, err) + + // check the contract state + queryInputBz, err := abi.Pack("count") + require.NoError(t, err) + queryRes, logs, err := input.EVMKeeper.EVMCall(ctx, evmAddr, contractAddr, queryInputBz) + require.NoError(t, err) + require.Equal(t, uint256.NewInt(0).Bytes32(), [32]byte(queryRes)) + require.Empty(t, logs) + + // set acl + require.NoError(t, input.IBCHooksKeeper.SetAllowed(ctx, contractAddr[:], true)) + + // success with success ack + err = input.IBCHooksMiddleware.OnAcknowledgementPacket(ctx, channeltypes.Packet{ + Data: dataBz, + }, successAckBz, addr) + require.NoError(t, err) + + // check the contract state; increased by 99 if ack is success + queryRes, logs, err = input.EVMKeeper.EVMCall(ctx, evmAddr, contractAddr, queryInputBz) + require.NoError(t, err) + require.Equal(t, uint256.NewInt(99).Bytes32(), [32]byte(queryRes)) + require.Empty(t, logs) + + // success with failed ack + err = input.IBCHooksMiddleware.OnAcknowledgementPacket(ctx, channeltypes.Packet{ + Data: dataBz, + }, failedAckBz, addr) + require.NoError(t, err) + + // check the contract state; increased by 1 if ack is failed + queryRes, logs, err = input.EVMKeeper.EVMCall(ctx, evmAddr, contractAddr, queryInputBz) + require.NoError(t, err) + require.Equal(t, uint256.NewInt(100).Bytes32(), [32]byte(queryRes)) + require.Empty(t, logs) +} diff --git a/app/ibc-hooks/common_test.go b/app/ibc-hooks/common_test.go index 44f1e42..0f7211c 100644 --- a/app/ibc-hooks/common_test.go +++ b/app/ibc-hooks/common_test.go @@ -41,6 +41,7 @@ import ( "github.com/cosmos/gogoproto/proto" capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" + ibc "github.com/cosmos/ibc-go/v8/modules/core" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" porttypes "github.com/cosmos/ibc-go/v8/modules/core/05-port/types" @@ -52,7 +53,7 @@ import ( evmhooks "github.com/initia-labs/minievm/app/ibc-hooks" "github.com/initia-labs/minievm/x/evm" - EVMConfig "github.com/initia-labs/minievm/x/evm/config" + evmconfig "github.com/initia-labs/minievm/x/evm/config" evmkeeper "github.com/initia-labs/minievm/x/evm/keeper" evmtypes "github.com/initia-labs/minievm/x/evm/types" ) @@ -62,6 +63,7 @@ var ModuleBasics = module.NewBasicManager( bank.AppModuleBasic{}, ibchooks.AppModuleBasic{}, evm.AppModuleBasic{}, + ibc.AppModuleBasic{}, ) var ( @@ -277,6 +279,7 @@ func _createTestInput( authtypes.NewModuleAddress(govtypes.ModuleName).String(), ac, ) + ibcHooksKeeper.Params.Set(ctx, ibchookstypes.DefaultParams()) communityPoolKeeper := &MockCommunityPoolKeeper{} evmKeeper := evmkeeper.NewKeeper( @@ -286,7 +289,7 @@ func _createTestInput( accountKeeper, communityPoolKeeper, authtypes.NewModuleAddress(govtypes.ModuleName).String(), - EVMConfig.DefaultEVMConfig(), + evmconfig.DefaultEVMConfig(), ) evmParams := evmtypes.DefaultParams() require.NoError(t, evmKeeper.Params.Set(ctx, evmParams)) @@ -299,7 +302,7 @@ func _createTestInput( // ibc middleware setup mockIBCMiddleware := mockIBCMiddleware{} - evmHooks := evmhooks.NewEVMHooks(evmKeeper, ac) + evmHooks := evmhooks.NewEVMHooks(appCodec, ac, evmKeeper) middleware := ibchooks.NewICS4Middleware(mockIBCMiddleware, evmHooks) ibcHookMiddleware := ibchooks.NewIBCMiddleware(mockIBCMiddleware, middleware, ibcHooksKeeper) diff --git a/app/ibc-hooks/hooks.go b/app/ibc-hooks/hooks.go index 3a5eb92..21f92b3 100644 --- a/app/ibc-hooks/hooks.go +++ b/app/ibc-hooks/hooks.go @@ -2,6 +2,7 @@ package evm_hooks import ( "cosmossdk.io/core/address" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported" @@ -20,20 +21,22 @@ var ( ) type EVMHooks struct { - evmKeeper *evmkeeper.Keeper + codec codec.Codec ac address.Codec + evmKeeper *evmkeeper.Keeper asyncCallbackABI *abi.ABI } -func NewEVMHooks(evmKeeper *evmkeeper.Keeper, ac address.Codec) *EVMHooks { +func NewEVMHooks(codec codec.Codec, ac address.Codec, evmKeeper *evmkeeper.Keeper) *EVMHooks { abi, err := i_ibc_async_callback.IIbcAsyncCallbackMetaData.GetAbi() if err != nil { panic(err) } return &EVMHooks{ - evmKeeper: evmKeeper, + codec: codec, ac: ac, + evmKeeper: evmKeeper, asyncCallbackABI: abi, } } diff --git a/app/ibc-hooks/receive.go b/app/ibc-hooks/receive.go index dac85ac..14ae671 100644 --- a/app/ibc-hooks/receive.go +++ b/app/ibc-hooks/receive.go @@ -34,7 +34,7 @@ func (h EVMHooks) onRecvIcs20Packet( if allowed, err := h.checkACL(im, ctx, msg.ContractAddr); err != nil { return newEmitErrorAcknowledgement(err) } else if !allowed { - return im.App.OnRecvPacket(ctx, packet, relayer) + return newEmitErrorAcknowledgement(fmt.Errorf("contract `%s` not allowed to be used in ibchooks", msg.ContractAddr)) } // Validate whether the receiver is correctly specified or not. @@ -63,7 +63,6 @@ func (h EVMHooks) onRecvIcs20Packet( return ack } - fmt.Println("SIBONG") msg.Sender = intermediateSender _, err = h.execMsg(ctx, msg) if err != nil { diff --git a/app/ibc-hooks/receive_test.go b/app/ibc-hooks/receive_test.go index 68a63f5..a6b5cb8 100644 --- a/app/ibc-hooks/receive_test.go +++ b/app/ibc-hooks/receive_test.go @@ -22,7 +22,6 @@ func Test_onReceiveIcs20Packet_noMemo(t *testing.T) { _, _, addr := keyPubAddr() _, _, addr2 := keyPubAddr() - // don't have any memo data := transfertypes.FungibleTokenPacketData{ Denom: "foo", Amount: "10000", @@ -58,7 +57,6 @@ func Test_onReceiveIcs20Packet_memo(t *testing.T) { inputBz, err := abi.Pack("increase") require.NoError(t, err) - // don't have any memo data := transfertypes.FungibleTokenPacketData{ Denom: "foo", Amount: "10000", diff --git a/app/ibc-hooks/timeout.go b/app/ibc-hooks/timeout.go index a106823..889f604 100644 --- a/app/ibc-hooks/timeout.go +++ b/app/ibc-hooks/timeout.go @@ -34,6 +34,7 @@ func (h EVMHooks) onTimeoutIcs20Packet( if allowed, err := h.checkACL(im, ctx, callback.ContractAddress); err != nil { return err } else if !allowed { + // just return nil here to avoid packet stuck due to hook acl. return nil } diff --git a/app/ibc-hooks/timeout_test.go b/app/ibc-hooks/timeout_test.go new file mode 100644 index 0000000..dc3ae3d --- /dev/null +++ b/app/ibc-hooks/timeout_test.go @@ -0,0 +1,100 @@ +package evm_hooks_test + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "strings" + "testing" + + transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" + channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" + "github.com/ethereum/go-ethereum/common" + "github.com/holiman/uint256" + "github.com/initia-labs/minievm/x/evm/contracts/counter" + "github.com/stretchr/testify/require" +) + +func Test_onTimeoutIcs20Packet_noMemo(t *testing.T) { + ctx, input := createDefaultTestInput(t) + _, _, addr := keyPubAddr() + _, _, addr2 := keyPubAddr() + + data := transfertypes.FungibleTokenPacketData{ + Denom: "foo", + Amount: "10000", + Sender: addr.String(), + Receiver: addr2.String(), + Memo: "", + } + + dataBz, err := json.Marshal(&data) + require.NoError(t, err) + + err = input.IBCHooksMiddleware.OnTimeoutPacket(ctx, channeltypes.Packet{ + Data: dataBz, + }, addr) + require.NoError(t, err) +} + +func Test_onTimeoutIcs20Packet_memo(t *testing.T) { + ctx, input := createDefaultTestInput(t) + _, _, addr := keyPubAddr() + evmAddr := common.BytesToAddress(addr.Bytes()) + + codeBz, err := hex.DecodeString(strings.TrimPrefix(counter.CounterBin, "0x")) + require.NoError(t, err) + + _, contractAddr, err := input.EVMKeeper.EVMCreate(ctx, evmAddr, codeBz) + require.NoError(t, err) + + abi, err := counter.CounterMetaData.GetAbi() + require.NoError(t, err) + + data := transfertypes.FungibleTokenPacketData{ + Denom: "foo", + Amount: "10000", + Sender: addr.String(), + Receiver: contractAddr.Hex(), + Memo: fmt.Sprintf(`{ + "evm": { + "async_callback": { + "id": 99, + "contract_address": "%s" + } + } + }`, contractAddr.Hex()), + } + + dataBz, err := json.Marshal(&data) + require.NoError(t, err) + + // hook should not be called to due to acl + err = input.IBCHooksMiddleware.OnTimeoutPacket(ctx, channeltypes.Packet{ + Data: dataBz, + }, addr) + require.NoError(t, err) + + // check the contract state + queryInputBz, err := abi.Pack("count") + require.NoError(t, err) + queryRes, logs, err := input.EVMKeeper.EVMCall(ctx, evmAddr, contractAddr, queryInputBz) + require.NoError(t, err) + require.Equal(t, uint256.NewInt(0).Bytes32(), [32]byte(queryRes)) + require.Empty(t, logs) + + // set acl + require.NoError(t, input.IBCHooksKeeper.SetAllowed(ctx, contractAddr[:], true)) + + // success + err = input.IBCHooksMiddleware.OnTimeoutPacket(ctx, channeltypes.Packet{ + Data: dataBz, + }, addr) + require.NoError(t, err) + + // check the contract state; increased by 99 + queryRes, logs, err = input.EVMKeeper.EVMCall(ctx, evmAddr, contractAddr, queryInputBz) + require.NoError(t, err) + require.Equal(t, uint256.NewInt(99).Bytes32(), [32]byte(queryRes)) + require.Empty(t, logs) +} diff --git a/app/ibc-hooks/util.go b/app/ibc-hooks/util.go index 8092d93..83789c7 100644 --- a/app/ibc-hooks/util.go +++ b/app/ibc-hooks/util.go @@ -6,6 +6,7 @@ import ( "strings" "cosmossdk.io/errors" + "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/address" @@ -124,11 +125,11 @@ func newEmitErrorAcknowledgement(err error) channeltypes.Acknowledgement { } // isAckError checks an IBC acknowledgement to see if it's an error. -// This is a replacement for ack.Success() which is currently not working on some circumstances -func isAckError(acknowledgement []byte) bool { - var ackErr channeltypes.Acknowledgement_Error - if err := json.Unmarshal(acknowledgement, &ackErr); err == nil && len(ackErr.Error) > 0 { +func isAckError(appCodec codec.Codec, acknowledgement []byte) bool { + var ack channeltypes.Acknowledgement + if err := appCodec.UnmarshalJSON(acknowledgement, &ack); err == nil && !ack.Success() { return true } + return false } diff --git a/go.mod b/go.mod index d5e98b9..8a636ff 100644 --- a/go.mod +++ b/go.mod @@ -247,7 +247,9 @@ replace ( // TODO: remove it: https://github.com/cosmos/cosmos-sdk/issues/13134 github.com/dgrijalva/jwt-go => github.com/golang-jwt/jwt/v4 v4.4.2 - github.com/ethereum/go-ethereum => github.com/initia-labs/go-ethereum v0.0.0-20240326070025-3f6a7e5cd315 + github.com/ethereum/go-ethereum => ../go-ethereum + + // github.com/ethereum/go-ethereum => github.com/initia-labs/go-ethereum v0.0.0-20240326070025-3f6a7e5cd315 // Fix upstream GHSA-h395-qcrw-5vmq vulnerability. // TODO Remove it: https://github.com/cosmos/cosmos-sdk/issues/10409 diff --git a/go.sum b/go.sum index 0e41841..8f7d099 100644 --- a/go.sum +++ b/go.sum @@ -774,8 +774,6 @@ github.com/initia-labs/cometbft v0.0.0-20240104081544-34081fc84daf h1:7k3u0huB7O github.com/initia-labs/cometbft v0.0.0-20240104081544-34081fc84daf/go.mod h1:PIi48BpzwlHqtV3mzwPyQgOyOnU94BNBimLS2ebBHOg= github.com/initia-labs/cosmos-sdk v0.0.0-20240228122102-ee5ec2e05aea h1:MMBuEk0sxBEVPEfIA0YieIsRc0cqngflS9Q75tiewe4= github.com/initia-labs/cosmos-sdk v0.0.0-20240228122102-ee5ec2e05aea/go.mod h1:UbShFs6P8Ly29xxJvkNGaNaL/UGj5a686NRtb1Cqra0= -github.com/initia-labs/go-ethereum v0.0.0-20240326070025-3f6a7e5cd315 h1:Ks+iqkMEOIIdxGk+4/iqER6hUXKFPvOAtdvRLO7JueU= -github.com/initia-labs/go-ethereum v0.0.0-20240326070025-3f6a7e5cd315/go.mod h1:x2gtBG0WHLgY08FE97lfhjtpcR5vcSAZbi34JnrsBbQ= github.com/initia-labs/iavl v0.0.0-20240208034922-5d81c449d4c0 h1:GQ7/UD5mB6q104roqZK5jxb6ff9sBk0/uwFxgERQIaU= github.com/initia-labs/iavl v0.0.0-20240208034922-5d81c449d4c0/go.mod h1:CmTGqMnRnucjxbjduneZXT+0vPgNElYvdefjX2q9tYc= github.com/initia-labs/initia v0.2.3 h1:btg3UoX6oy7LOcsy03rZaYmxDk5RvOyBY+hQ4EtuxUg= diff --git a/proto/minievm/evm/v1/query.proto b/proto/minievm/evm/v1/query.proto index ecba905..3dcf44c 100644 --- a/proto/minievm/evm/v1/query.proto +++ b/proto/minievm/evm/v1/query.proto @@ -84,15 +84,18 @@ message QueryCallRequest { string contract_addr = 2; // hex encoded call input string input = 3; + // whether to trace the call + bool with_trace = 4; } // QueryCallResponse is the response type for the Query/Call RPC // method message QueryCallResponse { // hex encoded response bytes. - string response = 1; - uint64 used_gas = 2; - repeated Log logs = 3 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true]; + string response = 1; + uint64 used_gas = 2; + repeated Log logs = 3 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true]; + string trace_output = 4; } // QueryContractAddrByDenomRequest is the request type for the Query/ContractAddrByDenom RPC @@ -106,7 +109,7 @@ message QueryContractAddrByDenomRequest { message QueryContractAddrByDenomResponse { option (gogoproto.equal) = true; // 0x prefixed hex address - string address = 1; + string address = 1; } // QueryDenomRequest is the request type for the Query/Denom RPC diff --git a/x/bank/keeper/genesis_test.go b/x/bank/keeper/genesis_test.go index 27bd060..8549acc 100644 --- a/x/bank/keeper/genesis_test.go +++ b/x/bank/keeper/genesis_test.go @@ -1,7 +1,6 @@ package keeper_test import ( - "fmt" "testing" "cosmossdk.io/math" @@ -33,7 +32,6 @@ func TestExportGenesis(t *testing.T) { exportGenesis := input.BankKeeper.ExportGenesis(ctx) - fmt.Println(expectedBalances, exportGenesis.Balances) require.Len(t, exportGenesis.Params.SendEnabled, 0) require.Equal(t, types.DefaultParams().DefaultSendEnabled, exportGenesis.Params.DefaultSendEnabled) require.Equal(t, totalSupply, exportGenesis.Supply) diff --git a/x/evm/config/config.go b/x/evm/config/config.go index bddb77b..269ca2a 100644 --- a/x/evm/config/config.go +++ b/x/evm/config/config.go @@ -16,12 +16,20 @@ const DefaultContractSimulationGasLimit = uint64(3_000_000) const ( flagContractSimulationGasLimit = "evm.contract-simulation-gas-limit" flagContractQueryGasLimit = "evm.contract-query-gas-limit" + flagTracingEnableMemory = "evm.tracing-enable-memory" + flagTracingEnableStack = "evm.tracing-enable-stack" + flagTracingEnableStorage = "evm.tracing-enable-storage" + flagTracingEnableReturnData = "evm.tracing-enable-return-data" ) -// EVMConfig is the extra config required for move +// EVMConfig is the extra config required for evm type EVMConfig struct { ContractSimulationGasLimit uint64 `mapstructure:"contract-simulation-gas-limit"` ContractQueryGasLimit uint64 `mapstructure:"contract-query-gas-limit"` + TracingEnableMemory bool `mapstructure:"tracing-enable-memory"` + TracingEnableStack bool `mapstructure:"tracing-enable-stack"` + TracingEnableStorage bool `mapstructure:"tracing-enable-storage"` + TracingEnableReturnData bool `mapstructure:"tracing-enable-return-data"` } // DefaultEVMConfig returns the default settings for EVMConfig @@ -29,6 +37,10 @@ func DefaultEVMConfig() EVMConfig { return EVMConfig{ ContractSimulationGasLimit: DefaultContractSimulationGasLimit, ContractQueryGasLimit: DefaultContractQueryGasLimit, + TracingEnableMemory: false, + TracingEnableStack: false, + TracingEnableStorage: false, + TracingEnableReturnData: false, } } @@ -37,22 +49,30 @@ func GetConfig(appOpts servertypes.AppOptions) EVMConfig { return EVMConfig{ ContractSimulationGasLimit: cast.ToUint64(appOpts.Get(flagContractSimulationGasLimit)), ContractQueryGasLimit: cast.ToUint64(appOpts.Get(flagContractQueryGasLimit)), + TracingEnableMemory: cast.ToBool(appOpts.Get(flagTracingEnableMemory)), + TracingEnableStack: cast.ToBool(appOpts.Get(flagTracingEnableStack)), + TracingEnableStorage: cast.ToBool(appOpts.Get(flagTracingEnableStorage)), + TracingEnableReturnData: cast.ToBool(appOpts.Get(flagTracingEnableReturnData)), } } // AddConfigFlags implements servertypes.EVMConfigFlags interface. func AddConfigFlags(startCmd *cobra.Command) { - startCmd.Flags().Uint64(flagContractSimulationGasLimit, DefaultContractSimulationGasLimit, "Set the max simulation gas for move contract execution") + startCmd.Flags().Uint64(flagContractSimulationGasLimit, DefaultContractSimulationGasLimit, "Set the max simulation gas for evm contract execution") startCmd.Flags().Uint64(flagContractQueryGasLimit, DefaultContractQueryGasLimit, "Set the max gas that can be spent on executing a query with a Move contract") + startCmd.Flags().Bool(flagTracingEnableMemory, false, "Enable memory tracing at query/Call") + startCmd.Flags().Bool(flagTracingEnableStack, false, "Enable stack tracing at query/Call") + startCmd.Flags().Bool(flagTracingEnableStorage, false, "Enable storage tracing at query/Call") + startCmd.Flags().Bool(flagTracingEnableReturnData, false, "Enable return data tracing at query/Call") } -// DefaultConfigTemplate default config template for move module +// DefaultConfigTemplate default config template for evm const DefaultConfigTemplate = ` ############################################################################### -### Move ### +### EVM ### ############################################################################### -[move] +[evm] # The maximum gas amount can be used in a tx simulation call. contract-simulation-gas-limit = "{{ .EVMConfig.ContractSimulationGasLimit }}" @@ -61,4 +81,16 @@ contract-simulation-gas-limit = "{{ .EVMConfig.ContractSimulationGasLimit }}" # The contract query will invoke contract execution vm, # so we need to restrict the max usage to prevent DoS attack contract-query-gas-limit = "{{ .EVMConfig.ContractQueryGasLimit }}" + +# Enable memory tracing at query/Call +tracing-enable-memory = {{ .EVMConfig.TracingEnableMemory }} + +# Enable stack tracing at query/Call +tracing-enable-stack = {{ .EVMConfig.TracingEnableStack }} + +# Enable storage tracing at query/Call +tracing-enable-storage = {{ .EVMConfig.TracingEnableStorage }} + +# Enable return data tracing at query/Call +tracing-enable-return-data = {{ .EVMConfig.TracingEnableReturnData }} ` diff --git a/x/evm/contracts/counter/Counter.go b/x/evm/contracts/counter/Counter.go index 1f9a172..9079f5a 100644 --- a/x/evm/contracts/counter/Counter.go +++ b/x/evm/contracts/counter/Counter.go @@ -31,8 +31,8 @@ var ( // CounterMetaData contains all meta data concerning the Counter contract. var CounterMetaData = &bind.MetaData{ - ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldCount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newCount\",\"type\":\"uint256\"}],\"name\":\"increased\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"count\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"increase\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", - Bin: "0x608060405234801561000f575f80fd5b506101f98061001d5f395ff3fe608060405234801561000f575f80fd5b5060043610610034575f3560e01c806306661abd14610038578063e8927fbc14610056575b5f80fd5b610040610060565b60405161004d91906100dc565b60405180910390f35b61005e610065565b005b5f5481565b5f8081548092919061007690610122565b91905055507f61996fe196f72cb598c483e896a1221263a28bb630480aa89495f737d4a8e3df60015f546100aa9190610169565b5f546040516100ba92919061019c565b60405180910390a1565b5f819050919050565b6100d6816100c4565b82525050565b5f6020820190506100ef5f8301846100cd565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61012c826100c4565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361015e5761015d6100f5565b5b600182019050919050565b5f610173826100c4565b915061017e836100c4565b9250828203905081811115610196576101956100f5565b5b92915050565b5f6040820190506101af5f8301856100cd565b6101bc60208301846100cd565b939250505056fea26469706673582212205af05190048c7917b0ee5ebf1492cb1d07ff48b691d3fabc63d863923408e7a864736f6c63430008180033", + ABI: "[{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"oldCount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"newCount\",\"type\":\"uint256\"}],\"name\":\"increased\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"count\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"callback_id\",\"type\":\"uint64\"},{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"name\":\"ibc_ack\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"callback_id\",\"type\":\"uint64\"}],\"name\":\"ibc_timeout\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"increase\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x608060405234801561000f575f80fd5b506103c48061001d5f395ff3fe608060405234801561000f575f80fd5b506004361061004a575f3560e01c806306661abd1461004e5780630d4f1f9d1461006c57806331a503f014610088578063e8927fbc146100a4575b5f80fd5b6100566100ae565b6040516100639190610195565b60405180910390f35b61008660048036038101906100819190610224565b6100b3565b005b6100a2600480360381019061009d9190610262565b6100fa565b005b6100ac61011e565b005b5f5481565b80156100df578167ffffffffffffffff165f808282546100d391906102ba565b925050819055506100f6565b5f808154809291906100f0906102ed565b91905055505b5050565b8067ffffffffffffffff165f8082825461011491906102ba565b9250508190555050565b5f8081548092919061012f906102ed565b91905055507f61996fe196f72cb598c483e896a1221263a28bb630480aa89495f737d4a8e3df60015f546101639190610334565b5f54604051610173929190610367565b60405180910390a1565b5f819050919050565b61018f8161017d565b82525050565b5f6020820190506101a85f830184610186565b92915050565b5f80fd5b5f67ffffffffffffffff82169050919050565b6101ce816101b2565b81146101d8575f80fd5b50565b5f813590506101e9816101c5565b92915050565b5f8115159050919050565b610203816101ef565b811461020d575f80fd5b50565b5f8135905061021e816101fa565b92915050565b5f806040838503121561023a576102396101ae565b5b5f610247858286016101db565b925050602061025885828601610210565b9150509250929050565b5f60208284031215610277576102766101ae565b5b5f610284848285016101db565b91505092915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f6102c48261017d565b91506102cf8361017d565b92508282019050808211156102e7576102e661028d565b5b92915050565b5f6102f78261017d565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036103295761032861028d565b5b600182019050919050565b5f61033e8261017d565b91506103498361017d565b92508282039050818111156103615761036061028d565b5b92915050565b5f60408201905061037a5f830185610186565b6103876020830184610186565b939250505056fea264697066735822122026639416d05d33f26bc2c230b981d417c89827f78d1bd9c1b9df6fd689735b7364736f6c63430008180033", } // CounterABI is the input ABI used to generate the binding from. @@ -233,6 +233,48 @@ func (_Counter *CounterCallerSession) Count() (*big.Int, error) { return _Counter.Contract.Count(&_Counter.CallOpts) } +// IbcAck is a paid mutator transaction binding the contract method 0x0d4f1f9d. +// +// Solidity: function ibc_ack(uint64 callback_id, bool success) returns() +func (_Counter *CounterTransactor) IbcAck(opts *bind.TransactOpts, callback_id uint64, success bool) (*types.Transaction, error) { + return _Counter.contract.Transact(opts, "ibc_ack", callback_id, success) +} + +// IbcAck is a paid mutator transaction binding the contract method 0x0d4f1f9d. +// +// Solidity: function ibc_ack(uint64 callback_id, bool success) returns() +func (_Counter *CounterSession) IbcAck(callback_id uint64, success bool) (*types.Transaction, error) { + return _Counter.Contract.IbcAck(&_Counter.TransactOpts, callback_id, success) +} + +// IbcAck is a paid mutator transaction binding the contract method 0x0d4f1f9d. +// +// Solidity: function ibc_ack(uint64 callback_id, bool success) returns() +func (_Counter *CounterTransactorSession) IbcAck(callback_id uint64, success bool) (*types.Transaction, error) { + return _Counter.Contract.IbcAck(&_Counter.TransactOpts, callback_id, success) +} + +// IbcTimeout is a paid mutator transaction binding the contract method 0x31a503f0. +// +// Solidity: function ibc_timeout(uint64 callback_id) returns() +func (_Counter *CounterTransactor) IbcTimeout(opts *bind.TransactOpts, callback_id uint64) (*types.Transaction, error) { + return _Counter.contract.Transact(opts, "ibc_timeout", callback_id) +} + +// IbcTimeout is a paid mutator transaction binding the contract method 0x31a503f0. +// +// Solidity: function ibc_timeout(uint64 callback_id) returns() +func (_Counter *CounterSession) IbcTimeout(callback_id uint64) (*types.Transaction, error) { + return _Counter.Contract.IbcTimeout(&_Counter.TransactOpts, callback_id) +} + +// IbcTimeout is a paid mutator transaction binding the contract method 0x31a503f0. +// +// Solidity: function ibc_timeout(uint64 callback_id) returns() +func (_Counter *CounterTransactorSession) IbcTimeout(callback_id uint64) (*types.Transaction, error) { + return _Counter.Contract.IbcTimeout(&_Counter.TransactOpts, callback_id) +} + // Increase is a paid mutator transaction binding the contract method 0xe8927fbc. // // Solidity: function increase() returns() diff --git a/x/evm/contracts/counter/Counter.sol b/x/evm/contracts/counter/Counter.sol index 8a5e601..51339ac 100644 --- a/x/evm/contracts/counter/Counter.sol +++ b/x/evm/contracts/counter/Counter.sol @@ -1,7 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; -contract Counter { +import "../i_ibc_async_callback/IIBCAsyncCallback.sol"; + +contract Counter is IIBCAsyncCallback { uint256 public count; event increased(uint256 oldCount, uint256 newCount); @@ -11,4 +13,16 @@ contract Counter { emit increased(count-1, count); } + + function ibc_ack(uint64 callback_id, bool success) external { + if (success) { + count+=callback_id; + } else { + count++; + } + } + + function ibc_timeout(uint64 callback_id) external { + count+=callback_id; + } } diff --git a/x/evm/keeper/context.go b/x/evm/keeper/context.go index f72ec92..54ddbb4 100644 --- a/x/evm/keeper/context.go +++ b/x/evm/keeper/context.go @@ -11,6 +11,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/tracing" coretypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" @@ -67,7 +68,8 @@ func (k Keeper) buildTxContext(_ context.Context, caller common.Address) vm.TxCo } } -func (k Keeper) createEVM(ctx context.Context, caller common.Address) (*vm.EVM, error) { +// createEVM creates a new EVM instance. +func (k Keeper) createEVM(ctx context.Context, caller common.Address, tracer *tracing.Hooks) (*vm.EVM, error) { extraEIPs, err := k.ExtraEIPs(ctx) if err != nil { return nil, err @@ -80,26 +82,58 @@ func (k Keeper) createEVM(ctx context.Context, caller common.Address) (*vm.EVM, return nil, err } + vmConfig := vm.Config{ + Tracer: tracer, + ExtraEips: extraEIPs, + ContractCreatedHook: k.contractCreatedHook(ctx), + } + return vm.NewEVMWithPrecompiles( blockContext, txContext, stateDB, types.DefaultChainConfig(), - vm.Config{ExtraEips: extraEIPs}, - // vm.Config{ExtraEips: extraEIPs, - // Tracer: logger.NewJSONLogger(&logger.Config{ - // EnableMemory: false, - // DisableStack: true, - // DisableStorage: true, - // EnableReturnData: true, - // }, os.Stderr), - // }, + vmConfig, k.precompiles.toMap(ctx), ), nil } +// contractCreatedHook returns a callback function that is called when a contract is created. +// +// It converts a normal account to a contract account if the account is empty and create +// creates a contract account if the account does not exist. +func (k Keeper) contractCreatedHook(ctx context.Context) vm.ContractCreatedHook { + return func(contractAddr common.Address) error { + if k.accountKeeper.HasAccount(ctx, sdk.AccAddress(contractAddr.Bytes())) { + account := k.accountKeeper.GetAccount(ctx, sdk.AccAddress(contractAddr.Bytes())) + _, isModuleAccount := account.(sdk.ModuleAccountI) + if isModuleAccount || account.GetPubKey() != nil { + return types.ErrAddressAlreadyExists.Wrap(contractAddr.String()) + } + + // convert normal account to contract account only if this account is empty + contractAccount := types.NewContractAccountWithAddress(contractAddr.Bytes()) + contractAccount.AccountNumber = account.GetAccountNumber() + k.accountKeeper.SetAccount(ctx, contractAccount) + } else { + // create contract account + contractAccount := types.NewContractAccountWithAddress(contractAddr.Bytes()) + contractAccount.AccountNumber = k.accountKeeper.NextAccountNumber(ctx) + k.accountKeeper.SetAccount(ctx, contractAccount) + } + + return nil + } +} + +// EVMStaticCall executes an EVM call with the given input data in static mode. func (k Keeper) EVMStaticCall(ctx context.Context, caller common.Address, contractAddr common.Address, inputBz []byte) ([]byte, error) { - evm, err := k.createEVM(ctx, caller) + return k.EVMStaticCallWithTracer(ctx, caller, contractAddr, inputBz, nil) +} + +// EVMStaticCallWithTracer executes an EVM call with the given input data and tracer in static mode. +func (k Keeper) EVMStaticCallWithTracer(ctx context.Context, caller common.Address, contractAddr common.Address, inputBz []byte, tracer *tracing.Hooks) ([]byte, error) { + evm, err := k.createEVM(ctx, caller, tracer) if err != nil { return nil, err } @@ -124,8 +158,14 @@ func (k Keeper) EVMStaticCall(ctx context.Context, caller common.Address, contra return retBz, nil } +// EVMCall executes an EVM call with the given input data. func (k Keeper) EVMCall(ctx context.Context, caller common.Address, contractAddr common.Address, inputBz []byte) ([]byte, types.Logs, error) { - evm, err := k.createEVM(ctx, caller) + return k.EVMCallWithTracer(ctx, caller, contractAddr, inputBz, nil) +} + +// EVMCallWithTracer executes an EVM call with the given input data and tracer. +func (k Keeper) EVMCallWithTracer(ctx context.Context, caller common.Address, contractAddr common.Address, inputBz []byte, tracer *tracing.Hooks) ([]byte, types.Logs, error) { + evm, err := k.createEVM(ctx, caller, tracer) if err != nil { return nil, nil, err } @@ -192,8 +232,14 @@ func (k Keeper) EVMCall(ctx context.Context, caller common.Address, contractAddr return retBz, logs, nil } +// EVMCreate creates a new contract with the given code. func (k Keeper) EVMCreate(ctx context.Context, caller common.Address, codeBz []byte) ([]byte, common.Address, error) { - evm, err := k.createEVM(ctx, caller) + return k.EVMCreateWithTracer(ctx, caller, codeBz, nil) +} + +// EVMCreateWithTracer creates a new contract with the given code and tracer. +func (k Keeper) EVMCreateWithTracer(ctx context.Context, caller common.Address, codeBz []byte, tracer *tracing.Hooks) ([]byte, common.Address, error) { + evm, err := k.createEVM(ctx, caller, tracer) if err != nil { return nil, common.Address{}, err } @@ -246,6 +292,7 @@ func (k Keeper) EVMCreate(ctx context.Context, caller common.Address, codeBz []b return retBz, contractAddr, nil } +// Initialize deploy genesis contracts. func (k Keeper) Initialize(ctx context.Context) error { bin, err := hex.DecodeString(strings.TrimPrefix(factory.FactoryBin, "0x")) if err != nil { @@ -260,6 +307,7 @@ func (k Keeper) Initialize(ctx context.Context) error { return nil } +// NextContractAddress returns the next contract address which will be created by the given caller. func (k Keeper) NextContractAddress(ctx context.Context, caller common.Address) (common.Address, error) { stateDB, err := k.newStateDB(ctx) if err != nil { diff --git a/x/evm/keeper/erc20_stores.go b/x/evm/keeper/erc20_stores.go index dfae3d1..80eff25 100644 --- a/x/evm/keeper/erc20_stores.go +++ b/x/evm/keeper/erc20_stores.go @@ -31,23 +31,6 @@ func (k ERC20StoresKeeper) RegisterStore(ctx context.Context, addr sdk.AccAddres } func (k ERC20StoresKeeper) Register(ctx context.Context, contractAddr common.Address) error { - if k.accountKeeper.HasAccount(ctx, sdk.AccAddress(contractAddr.Bytes())) { - account := k.accountKeeper.GetAccount(ctx, sdk.AccAddress(contractAddr.Bytes())) - if account.GetPubKey() != nil { - return types.ErrAddressAlreadyExists.Wrap(contractAddr.String()) - } - - // convert normal account to contract account - contractAccount := types.NewContractAccountWithAddress(contractAddr.Bytes()) - contractAccount.AccountNumber = account.GetAccountNumber() - k.accountKeeper.SetAccount(ctx, contractAccount) - } else { - // create contract account - contractAccount := types.NewContractAccountWithAddress(contractAddr.Bytes()) - contractAccount.AccountNumber = k.accountKeeper.NextAccountNumber(ctx) - k.accountKeeper.SetAccount(ctx, contractAccount) - } - if found, err := k.ERC20DenomsByContractAddr.Has(ctx, contractAddr.Bytes()); err != nil { return err } else if !found { diff --git a/x/evm/keeper/query_server.go b/x/evm/keeper/query_server.go index c8fb128..f0c0dd1 100644 --- a/x/evm/keeper/query_server.go +++ b/x/evm/keeper/query_server.go @@ -4,12 +4,15 @@ import ( "context" "encoding/hex" "fmt" + "strings" errorsmod "cosmossdk.io/errors" storetypes "cosmossdk.io/store/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/tracing" + "github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/initia-labs/minievm/x/evm/types" ) @@ -48,18 +51,30 @@ func (qs *queryServerImpl) Call(ctx context.Context, req *types.QueryCallRequest return nil, err } + var tracer *tracing.Hooks + tracerOutput := new(strings.Builder) + if req.WithTrace { + tracer = logger.NewJSONLogger(&logger.Config{ + EnableMemory: qs.config.TracingEnableMemory, + DisableStack: !qs.config.TracingEnableStack, + DisableStorage: !qs.config.TracingEnableStorage, + EnableReturnData: qs.config.TracingEnableReturnData, + }, tracerOutput) + } + // use cache context to rollback writes sdkCtx, _ = sdkCtx.CacheContext() caller := common.BytesToAddress(sender) - retBz, logs, err := qs.EVMCall(sdkCtx, caller, contractAddr, inputBz) + retBz, logs, err := qs.EVMCallWithTracer(sdkCtx, caller, contractAddr, inputBz, tracer) if err != nil { return nil, err } return &types.QueryCallResponse{ - Response: common.Bytes2Hex(retBz), - UsedGas: sdkCtx.GasMeter().GasConsumedToLimit(), - Logs: logs, + Response: common.Bytes2Hex(retBz), + UsedGas: sdkCtx.GasMeter().GasConsumedToLimit(), + Logs: logs, + TraceOutput: tracerOutput.String(), }, nil } diff --git a/x/evm/types/query.pb.go b/x/evm/types/query.pb.go index 0773d5b..675937e 100644 --- a/x/evm/types/query.pb.go +++ b/x/evm/types/query.pb.go @@ -201,6 +201,8 @@ type QueryCallRequest struct { ContractAddr string `protobuf:"bytes,2,opt,name=contract_addr,json=contractAddr,proto3" json:"contract_addr,omitempty"` // hex encoded call input Input string `protobuf:"bytes,3,opt,name=input,proto3" json:"input,omitempty"` + // whether to trace the call + WithTrace bool `protobuf:"varint,4,opt,name=with_trace,json=withTrace,proto3" json:"with_trace,omitempty"` } func (m *QueryCallRequest) Reset() { *m = QueryCallRequest{} } @@ -240,9 +242,10 @@ var xxx_messageInfo_QueryCallRequest proto.InternalMessageInfo // method type QueryCallResponse struct { // hex encoded response bytes. - Response string `protobuf:"bytes,1,opt,name=response,proto3" json:"response,omitempty"` - UsedGas uint64 `protobuf:"varint,2,opt,name=used_gas,json=usedGas,proto3" json:"used_gas,omitempty"` - Logs []Log `protobuf:"bytes,3,rep,name=logs,proto3" json:"logs"` + Response string `protobuf:"bytes,1,opt,name=response,proto3" json:"response,omitempty"` + UsedGas uint64 `protobuf:"varint,2,opt,name=used_gas,json=usedGas,proto3" json:"used_gas,omitempty"` + Logs []Log `protobuf:"bytes,3,rep,name=logs,proto3" json:"logs"` + TraceOutput string `protobuf:"bytes,4,opt,name=trace_output,json=traceOutput,proto3" json:"trace_output,omitempty"` } func (m *QueryCallResponse) Reset() { *m = QueryCallResponse{} } @@ -530,53 +533,56 @@ func init() { func init() { proto.RegisterFile("minievm/evm/v1/query.proto", fileDescriptor_2bf9e0bee7cfd1a4) } var fileDescriptor_2bf9e0bee7cfd1a4 = []byte{ - // 724 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x95, 0xcd, 0x6e, 0xd3, 0x40, - 0x10, 0xc7, 0xe3, 0xe6, 0xa3, 0xed, 0xb6, 0xa0, 0x76, 0x1b, 0x55, 0xa9, 0x85, 0x9c, 0xb0, 0xe5, - 0xa3, 0x8a, 0x68, 0xdc, 0x86, 0x4a, 0x45, 0xbd, 0x11, 0x90, 0x90, 0xa0, 0x07, 0x08, 0x37, 0x2e, - 0xd5, 0x26, 0x5e, 0x19, 0xab, 0x8e, 0x37, 0xf5, 0x3a, 0x11, 0xa1, 0x6a, 0x0f, 0x88, 0x07, 0x40, - 0xf0, 0x02, 0x1c, 0x38, 0xf4, 0xc8, 0x63, 0xf4, 0x58, 0x89, 0x0b, 0x27, 0x04, 0x29, 0x12, 0x3c, - 0x06, 0xda, 0xf5, 0xb8, 0x8d, 0x9d, 0x0f, 0x95, 0x43, 0xa2, 0x9d, 0xdd, 0x99, 0xf9, 0xfd, 0x77, - 0x3c, 0x63, 0x23, 0xbd, 0xe5, 0x78, 0x0e, 0xeb, 0xb6, 0x4c, 0xf9, 0xeb, 0x6e, 0x9a, 0x07, 0x1d, - 0xe6, 0xf7, 0x2a, 0x6d, 0x9f, 0x07, 0x1c, 0x5f, 0x87, 0xb3, 0x8a, 0xfc, 0x75, 0x37, 0xf5, 0x45, - 0xda, 0x72, 0x3c, 0x6e, 0xaa, 0xff, 0xd0, 0x45, 0xcf, 0xdb, 0xdc, 0xe6, 0x6a, 0x69, 0xca, 0x15, - 0xec, 0xde, 0xb0, 0x39, 0xb7, 0x5d, 0x66, 0xd2, 0xb6, 0x63, 0x52, 0xcf, 0xe3, 0x01, 0x0d, 0x1c, - 0xee, 0x09, 0x38, 0x4d, 0x22, 0x83, 0x5e, 0x9b, 0xc1, 0x19, 0xd9, 0x46, 0x0b, 0x2f, 0xa4, 0x82, - 0x47, 0xdc, 0x62, 0x75, 0x76, 0xd0, 0x61, 0x22, 0xc0, 0xab, 0xe8, 0x5a, 0x93, 0x7b, 0x81, 0x4f, - 0x9b, 0xc1, 0x1e, 0xb5, 0x2c, 0xbf, 0xa0, 0x95, 0xb4, 0xb5, 0xd9, 0xfa, 0x7c, 0xb4, 0xf9, 0xd0, - 0xb2, 0x7c, 0xb2, 0x8e, 0x16, 0x07, 0x02, 0x45, 0x9b, 0x7b, 0x82, 0x61, 0x8c, 0x32, 0x4d, 0x6e, - 0x31, 0x15, 0x30, 0x5f, 0x57, 0xeb, 0x9d, 0xcc, 0xdf, 0xcf, 0x45, 0x8d, 0x3c, 0x05, 0xf7, 0x97, - 0x01, 0x0d, 0xfe, 0x0b, 0x84, 0x17, 0x50, 0x7a, 0x9f, 0xf5, 0x0a, 0x53, 0xea, 0x48, 0x2e, 0xc9, - 0x06, 0xc2, 0x83, 0xb9, 0x80, 0x9d, 0x47, 0xd9, 0x2e, 0x75, 0x3b, 0x0c, 0x92, 0x84, 0x06, 0xd0, - 0x59, 0x74, 0x4b, 0xea, 0xba, 0x11, 0x7c, 0x19, 0xe5, 0x04, 0xf3, 0x2c, 0x16, 0x51, 0xc1, 0x1a, - 0x16, 0x35, 0x35, 0x42, 0x54, 0x1e, 0x65, 0x1d, 0xaf, 0xdd, 0x09, 0x0a, 0xe9, 0x10, 0xa6, 0x0c, - 0x72, 0x1c, 0xd5, 0x44, 0x61, 0x40, 0x97, 0x8e, 0x66, 0x7c, 0x58, 0x03, 0xe9, 0xc2, 0xc6, 0x2b, - 0x68, 0xa6, 0x23, 0x98, 0xb5, 0x67, 0x53, 0xa1, 0x30, 0x99, 0xfa, 0xb4, 0xb4, 0x9f, 0x50, 0x81, - 0xab, 0x28, 0xe3, 0x72, 0x5b, 0x14, 0xd2, 0xa5, 0xf4, 0xda, 0x5c, 0x75, 0xa9, 0x12, 0x6f, 0x8d, - 0xca, 0x2e, 0xb7, 0x6b, 0xb3, 0xa7, 0x3f, 0x8a, 0xa9, 0x93, 0x3f, 0x5f, 0xcb, 0x5a, 0x5d, 0xf9, - 0x92, 0x6d, 0x54, 0x84, 0x67, 0x72, 0x29, 0xb5, 0xd6, 0x7b, 0xcc, 0x3c, 0xde, 0x8a, 0x6e, 0x9d, - 0x47, 0x59, 0x4b, 0xda, 0x51, 0x95, 0x94, 0x41, 0x6a, 0xa8, 0x34, 0x3e, 0x10, 0xb4, 0x16, 0xd0, - 0xb4, 0x2c, 0x07, 0x13, 0x02, 0x62, 0x23, 0x13, 0x6a, 0xfc, 0x00, 0x2e, 0x1f, 0xc3, 0x5d, 0xa9, - 0x95, 0xa2, 0xe7, 0x19, 0xe7, 0x8d, 0x54, 0x0a, 0xac, 0x3c, 0x44, 0x3c, 0xa7, 0x3e, 0x6d, 0x09, - 0x80, 0x91, 0x67, 0x68, 0x29, 0xb6, 0x0b, 0x89, 0xb6, 0x50, 0xae, 0xad, 0x76, 0x54, 0xa6, 0xb9, - 0xea, 0x72, 0xb2, 0x96, 0xa1, 0x7f, 0x2d, 0x23, 0xcb, 0x59, 0x07, 0xdf, 0xea, 0xc7, 0x1c, 0xca, - 0xaa, 0x6c, 0xf8, 0x2d, 0xca, 0xc8, 0x26, 0xc7, 0xa5, 0x64, 0x5c, 0x72, 0x70, 0xf4, 0x9b, 0x13, - 0x3c, 0x42, 0x31, 0x64, 0xfd, 0xdd, 0xb7, 0xdf, 0x9f, 0xa6, 0xee, 0xe2, 0xdb, 0x66, 0x62, 0x28, - 0xe5, 0xac, 0x08, 0xf3, 0x30, 0x56, 0xad, 0x23, 0xfc, 0x5e, 0x43, 0x59, 0xd5, 0xe6, 0x78, 0x74, - 0xee, 0xc1, 0x71, 0xd2, 0xc9, 0x24, 0x17, 0xe0, 0x6f, 0x29, 0x7e, 0x05, 0xdf, 0x4b, 0xf2, 0x85, - 0x74, 0x1b, 0x12, 0x60, 0x1e, 0xee, 0xb3, 0xde, 0x11, 0xfe, 0xa2, 0xa1, 0xa5, 0x11, 0xbd, 0x81, - 0xcd, 0x31, 0x17, 0x1e, 0xd7, 0x7e, 0xfa, 0xc6, 0xd5, 0x03, 0x40, 0x70, 0x59, 0x09, 0xbe, 0x85, - 0xc9, 0x70, 0xc1, 0xc2, 0x20, 0x61, 0x36, 0x7a, 0x7b, 0xaa, 0x39, 0xf0, 0x31, 0xca, 0x86, 0xba, - 0x46, 0x17, 0x2b, 0xa6, 0x84, 0x4c, 0x72, 0x01, 0x76, 0x45, 0xb1, 0xd7, 0xf0, 0x9d, 0x24, 0x5b, - 0xe1, 0x86, 0x9f, 0x96, 0x8b, 0x32, 0x72, 0xf4, 0xc7, 0x75, 0xca, 0xe5, 0xcb, 0x67, 0x5c, 0xa7, - 0x0c, 0xbc, 0x37, 0x48, 0x51, 0xc1, 0x57, 0x76, 0xb4, 0x32, 0xc9, 0x0f, 0xdd, 0x5d, 0x52, 0x0e, - 0x50, 0x2e, 0xec, 0x5c, 0x3c, 0xfa, 0x2e, 0xb1, 0xe1, 0xd0, 0x57, 0x27, 0xfa, 0x00, 0xd3, 0x50, - 0xcc, 0x02, 0x5e, 0x4e, 0x02, 0xc3, 0xa1, 0xa8, 0xed, 0x9e, 0xfe, 0x32, 0x52, 0x27, 0x7d, 0x23, - 0x75, 0xda, 0x37, 0xb4, 0xb3, 0xbe, 0xa1, 0xfd, 0xec, 0x1b, 0xda, 0x87, 0x73, 0x23, 0x75, 0x76, - 0x6e, 0xa4, 0xbe, 0x9f, 0x1b, 0xa9, 0x57, 0x65, 0xdb, 0x09, 0x5e, 0x77, 0x1a, 0x95, 0x26, 0x6f, - 0x99, 0x8e, 0xe7, 0x04, 0x0e, 0x5d, 0x77, 0x69, 0x43, 0x5c, 0xe4, 0x7b, 0xa3, 0x32, 0xaa, 0x2f, - 0x50, 0x23, 0xa7, 0x3e, 0x41, 0xf7, 0xff, 0x05, 0x00, 0x00, 0xff, 0xff, 0x52, 0xf4, 0x54, 0x5c, - 0x13, 0x07, 0x00, 0x00, + // 770 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x55, 0x4f, 0x6f, 0xd3, 0x4a, + 0x10, 0x8f, 0x1b, 0x27, 0x6d, 0xa7, 0x7d, 0x4f, 0xed, 0x36, 0xaa, 0x52, 0xeb, 0x3d, 0x27, 0x75, + 0xdf, 0x83, 0x28, 0xa2, 0x71, 0x1b, 0x2a, 0x15, 0xf5, 0x46, 0x40, 0x42, 0x82, 0x4a, 0x40, 0xe0, + 0xc4, 0x25, 0xda, 0xc4, 0x2b, 0xd7, 0x6a, 0xe2, 0x4d, 0xb3, 0x9b, 0x40, 0xa8, 0xca, 0x01, 0xf5, + 0x03, 0x20, 0xf8, 0x02, 0x20, 0x71, 0xe8, 0x91, 0x8f, 0xd1, 0x63, 0x25, 0x2e, 0x9c, 0x10, 0xa4, + 0x48, 0xf0, 0x31, 0xd0, 0xae, 0xd7, 0x6d, 0xe2, 0xfc, 0x51, 0x39, 0x24, 0xda, 0x99, 0xfd, 0xcd, + 0xfc, 0x7e, 0x33, 0x9e, 0xb1, 0xc1, 0x68, 0x78, 0xbe, 0x47, 0x3a, 0x0d, 0x5b, 0xfc, 0x3a, 0x9b, + 0xf6, 0x41, 0x9b, 0xb4, 0xba, 0x85, 0x66, 0x8b, 0x72, 0x8a, 0xfe, 0x56, 0x77, 0x05, 0xf1, 0xeb, + 0x6c, 0x1a, 0x8b, 0xb8, 0xe1, 0xf9, 0xd4, 0x96, 0xff, 0x01, 0xc4, 0x48, 0xb9, 0xd4, 0xa5, 0xf2, + 0x68, 0x8b, 0x93, 0xf2, 0xfe, 0xe3, 0x52, 0xea, 0xd6, 0x89, 0x8d, 0x9b, 0x9e, 0x8d, 0x7d, 0x9f, + 0x72, 0xcc, 0x3d, 0xea, 0x33, 0x75, 0x1b, 0xa5, 0xe4, 0xdd, 0x26, 0x51, 0x77, 0xd6, 0x36, 0x2c, + 0x3c, 0x16, 0x0a, 0xee, 0x50, 0x87, 0x94, 0xc9, 0x41, 0x9b, 0x30, 0x8e, 0xd6, 0xe0, 0xaf, 0x1a, + 0xf5, 0x79, 0x0b, 0xd7, 0x78, 0x05, 0x3b, 0x4e, 0x2b, 0xad, 0x65, 0xb5, 0xdc, 0x6c, 0x79, 0x3e, + 0x74, 0xde, 0x76, 0x9c, 0x96, 0xb5, 0x0e, 0x8b, 0x7d, 0x81, 0xac, 0x49, 0x7d, 0x46, 0x10, 0x02, + 0xbd, 0x46, 0x1d, 0x22, 0x03, 0xe6, 0xcb, 0xf2, 0xbc, 0xa3, 0xff, 0x7a, 0x9f, 0xd1, 0xac, 0xfb, + 0x0a, 0xfe, 0x84, 0x63, 0xfe, 0x47, 0x44, 0x68, 0x01, 0xe2, 0xfb, 0xa4, 0x9b, 0x9e, 0x92, 0x57, + 0xe2, 0x68, 0x6d, 0x00, 0xea, 0xcf, 0xa5, 0xb8, 0x53, 0x90, 0xe8, 0xe0, 0x7a, 0x9b, 0xa8, 0x24, + 0x81, 0xa1, 0xd8, 0x8f, 0xb5, 0xb0, 0x4c, 0x5c, 0xaf, 0x87, 0xec, 0xcb, 0x90, 0x64, 0xc4, 0x77, + 0x48, 0x48, 0xab, 0xac, 0x61, 0x55, 0x53, 0x23, 0x54, 0xa5, 0x20, 0xe1, 0xf9, 0xcd, 0x36, 0x4f, + 0xc7, 0x03, 0x36, 0x69, 0xa0, 0x7f, 0x01, 0x9e, 0x7b, 0x7c, 0xaf, 0x22, 0x70, 0x24, 0xad, 0x67, + 0xb5, 0xdc, 0x4c, 0x79, 0x56, 0x78, 0x9e, 0x0a, 0x87, 0xf5, 0x41, 0x0b, 0x9b, 0x26, 0x65, 0x28, + 0xe1, 0x06, 0xcc, 0xb4, 0xd4, 0x59, 0x29, 0xb9, 0xb0, 0xd1, 0x0a, 0xcc, 0xb4, 0x19, 0x71, 0x2a, + 0x2e, 0x66, 0x52, 0x86, 0x5e, 0x9e, 0x16, 0xf6, 0x3d, 0xcc, 0x50, 0x11, 0xf4, 0x3a, 0x75, 0x59, + 0x3a, 0x9e, 0x8d, 0xe7, 0xe6, 0x8a, 0x4b, 0x85, 0xc1, 0xd9, 0x29, 0xec, 0x52, 0xb7, 0x34, 0x7b, + 0xfa, 0x35, 0x13, 0x3b, 0xf9, 0xf9, 0x29, 0xaf, 0x95, 0x25, 0x16, 0xad, 0xc2, 0xbc, 0x94, 0x56, + 0xa1, 0x6d, 0x2e, 0xc4, 0xeb, 0x92, 0x6e, 0x4e, 0xfa, 0x1e, 0x4a, 0x97, 0xb5, 0x0d, 0x19, 0xf5, + 0x5c, 0x2f, 0xab, 0x2d, 0x75, 0xef, 0x12, 0x9f, 0x36, 0xc2, 0xc6, 0xa5, 0x20, 0xe1, 0x08, 0x3b, + 0xec, 0xb4, 0x34, 0xac, 0x12, 0x64, 0xc7, 0x07, 0xaa, 0x72, 0xd2, 0x30, 0x2d, 0x3a, 0x4a, 0x18, + 0x53, 0xb1, 0xa1, 0xa9, 0x9e, 0xd3, 0x2d, 0xd5, 0x9f, 0x01, 0xba, 0x2b, 0x8d, 0x63, 0x38, 0x13, + 0x83, 0x7c, 0x23, 0x95, 0x2a, 0xae, 0x94, 0x8a, 0x78, 0x84, 0x5b, 0xb8, 0xc1, 0x14, 0x99, 0xf5, + 0x00, 0x96, 0x06, 0xbc, 0x2a, 0xd1, 0x16, 0x24, 0x9b, 0xd2, 0x23, 0x33, 0xcd, 0x15, 0x97, 0xa3, + 0xed, 0x0e, 0xf0, 0x25, 0x5d, 0x74, 0xbc, 0xac, 0xb0, 0xc5, 0xb7, 0x49, 0x48, 0xc8, 0x6c, 0xe8, + 0x25, 0xe8, 0x62, 0x51, 0x50, 0x36, 0x1a, 0x17, 0x5d, 0x3e, 0x63, 0x75, 0x02, 0x22, 0x10, 0x63, + 0xad, 0xbf, 0xfe, 0xfc, 0xe3, 0xdd, 0xd4, 0x75, 0xf4, 0xbf, 0x1d, 0x59, 0x6c, 0xb1, 0x6f, 0xcc, + 0x3e, 0x1c, 0xe8, 0xd6, 0x11, 0x3a, 0xd6, 0x20, 0x21, 0x57, 0x05, 0x8d, 0xce, 0xdd, 0xbf, 0x92, + 0x86, 0x35, 0x09, 0xa2, 0xf8, 0xb7, 0x24, 0x7f, 0x01, 0xdd, 0x88, 0xf2, 0x33, 0x01, 0x1b, 0x12, + 0x60, 0x1f, 0xee, 0x93, 0xee, 0x11, 0xfa, 0xa8, 0xc1, 0xd2, 0x88, 0xd9, 0x40, 0xf6, 0x98, 0x82, + 0xc7, 0x8d, 0x9f, 0xb1, 0x71, 0xf5, 0x00, 0x25, 0x38, 0x2f, 0x05, 0xff, 0x87, 0xac, 0xe1, 0x86, + 0x05, 0x41, 0xcc, 0xae, 0x76, 0x2b, 0x72, 0x38, 0xd0, 0x2b, 0x48, 0x04, 0xba, 0x46, 0x37, 0x6b, + 0x40, 0x89, 0x35, 0x09, 0xa2, 0xb8, 0x0b, 0x92, 0x3b, 0x87, 0xae, 0x45, 0xb9, 0x25, 0xdd, 0xf0, + 0xd3, 0xaa, 0x83, 0x2e, 0xde, 0x0e, 0xe3, 0x26, 0xe5, 0xf2, 0xfd, 0x35, 0x6e, 0x52, 0xfa, 0x5e, + 0x2d, 0x56, 0x46, 0x92, 0xaf, 0xec, 0x68, 0x79, 0x2b, 0x35, 0x54, 0xbb, 0x60, 0x39, 0x80, 0x64, + 0x30, 0xb9, 0x68, 0x74, 0x2d, 0x03, 0xcb, 0x61, 0xac, 0x4d, 0xc4, 0x28, 0x4e, 0x53, 0x72, 0xa6, + 0xd1, 0x72, 0x94, 0x30, 0x58, 0x8a, 0xd2, 0xee, 0xe9, 0x77, 0x33, 0x76, 0xd2, 0x33, 0x63, 0xa7, + 0x3d, 0x53, 0x3b, 0xeb, 0x99, 0xda, 0xb7, 0x9e, 0xa9, 0xbd, 0x39, 0x37, 0x63, 0x67, 0xe7, 0x66, + 0xec, 0xcb, 0xb9, 0x19, 0x7b, 0x96, 0x77, 0x3d, 0xbe, 0xd7, 0xae, 0x16, 0x6a, 0xb4, 0x61, 0x7b, + 0xbe, 0xc7, 0x3d, 0xbc, 0x5e, 0xc7, 0x55, 0x76, 0x91, 0xef, 0x85, 0xcc, 0x28, 0xbf, 0x62, 0xd5, + 0xa4, 0xfc, 0x8c, 0xdd, 0xfc, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x33, 0x83, 0x04, 0xf5, 0x57, 0x07, + 0x00, 0x00, } func (this *QueryCodeResponse) Equal(that interface{}) bool { @@ -1091,6 +1097,16 @@ func (m *QueryCallRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.WithTrace { + i-- + if m.WithTrace { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } if len(m.Input) > 0 { i -= len(m.Input) copy(dAtA[i:], m.Input) @@ -1135,6 +1151,13 @@ func (m *QueryCallResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.TraceOutput) > 0 { + i -= len(m.TraceOutput) + copy(dAtA[i:], m.TraceOutput) + i = encodeVarintQuery(dAtA, i, uint64(len(m.TraceOutput))) + i-- + dAtA[i] = 0x22 + } if len(m.Logs) > 0 { for iNdEx := len(m.Logs) - 1; iNdEx >= 0; iNdEx-- { { @@ -1425,6 +1448,9 @@ func (m *QueryCallRequest) Size() (n int) { if l > 0 { n += 1 + l + sovQuery(uint64(l)) } + if m.WithTrace { + n += 2 + } return n } @@ -1447,6 +1473,10 @@ func (m *QueryCallResponse) Size() (n int) { n += 1 + l + sovQuery(uint64(l)) } } + l = len(m.TraceOutput) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } return n } @@ -2015,6 +2045,26 @@ func (m *QueryCallRequest) Unmarshal(dAtA []byte) error { } m.Input = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field WithTrace", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.WithTrace = bool(v != 0) default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) @@ -2150,6 +2200,38 @@ func (m *QueryCallResponse) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field TraceOutput", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.TraceOutput = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:])