evm, server: configurable tracer (#434)

* server: update server and enable configurable tracer

* config validation

* fix import cycle

* fix start

* update fields

* c++

* c++
This commit is contained in:
Federico Kunze Küllmer 2021-08-16 05:45:10 -04:00 committed by GitHub
parent 353455def9
commit cc3b2ff8e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 292 additions and 168 deletions

View File

@ -39,6 +39,8 @@ Ref: https://keepachangelog.com/en/1.0.0/
### State Machine Breaking ### State Machine Breaking
* (app, evm) [tharsis#434](https://github.com/tharsis/ethermint/pull/434) EVM `Keeper` struct and `NewEVM` function now have a new `trace` field to define
the Tracer type used to collect execution traces from the EVM transaction execution.
* (evm) [tharsis#175](https://github.com/tharsis/ethermint/issues/175) The msg `TxData` field is now represented as a `*proto.Any`. * (evm) [tharsis#175](https://github.com/tharsis/ethermint/issues/175) The msg `TxData` field is now represented as a `*proto.Any`.
* (evm) [tharsis#84](https://github.com/tharsis/ethermint/pull/84) Remove `journal`, `CommitStateDB` and `stateObjects`. * (evm) [tharsis#84](https://github.com/tharsis/ethermint/pull/84) Remove `journal`, `CommitStateDB` and `stateObjects`.
* (rpc, evm) [tharsis#81](https://github.com/tharsis/ethermint/pull/81) Remove tx `Receipt` from store and replace it with fields obtained from the Tendermint RPC client. * (rpc, evm) [tharsis#81](https://github.com/tharsis/ethermint/pull/81) Remove tx `Receipt` from store and replace it with fields obtained from the Tendermint RPC client.
@ -53,6 +55,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
### API Breaking ### API Breaking
* (server) [tharsis#434](https://github.com/tharsis/ethermint/pull/434) `evm-rpc` flags and app config have been renamed to `json-rpc`.
* (proto, evm) [tharsis#207](https://github.com/tharsis/ethermint/issues/207) Replace `big.Int` in favor of `sdk.Int` for `TxData` fields * (proto, evm) [tharsis#207](https://github.com/tharsis/ethermint/issues/207) Replace `big.Int` in favor of `sdk.Int` for `TxData` fields
* (proto, evm) [tharsis#81](https://github.com/tharsis/ethermint/pull/81) gRPC Query and Tx service changes: * (proto, evm) [tharsis#81](https://github.com/tharsis/ethermint/pull/81) gRPC Query and Tx service changes:
* The `TxReceipt`, `TxReceiptsByBlockHeight` endpoints have been removed from the Query service. * The `TxReceipt`, `TxReceiptsByBlockHeight` endpoints have been removed from the Query service.
@ -65,6 +68,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
### Improvements ### Improvements
* (evm) [tharsis#434](https://github.com/tharsis/ethermint/pull/434) Support different `Tracer` types for the EVM.
* (deps) [tharsis#427](https://github.com/tharsis/ethermint/pull/427) Bump ibc-go to [`v1.0.0`](https://github.com/cosmos/ibc-go/releases/tag/v1.0.0) * (deps) [tharsis#427](https://github.com/tharsis/ethermint/pull/427) Bump ibc-go to [`v1.0.0`](https://github.com/cosmos/ibc-go/releases/tag/v1.0.0)
* (gRPC) [tharsis#239](https://github.com/tharsis/ethermint/pull/239) Query `ChainConfig` via gRPC. * (gRPC) [tharsis#239](https://github.com/tharsis/ethermint/pull/239) Query `ChainConfig` via gRPC.
* (rpc) [tharsis#181](https://github.com/tharsis/ethermint/pull/181) Use evm denomination for params on tx fee. * (rpc) [tharsis#181](https://github.com/tharsis/ethermint/pull/181) Use evm denomination for params on tx fee.

View File

@ -27,7 +27,7 @@ type EVMKeeper interface {
WithContext(ctx sdk.Context) WithContext(ctx sdk.Context)
ResetRefundTransient(ctx sdk.Context) ResetRefundTransient(ctx sdk.Context)
GetCoinbaseAddress() (common.Address, error) GetCoinbaseAddress() (common.Address, error)
NewEVM(msg core.Message, config *params.ChainConfig, params evmtypes.Params, coinbase common.Address) *vm.EVM NewEVM(msg core.Message, config *params.ChainConfig, params evmtypes.Params, coinbase common.Address, tracer string) *vm.EVM
GetCodeHash(addr common.Address) common.Hash GetCodeHash(addr common.Address) common.Hash
} }
@ -389,8 +389,8 @@ func (ctd CanTransferDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate
) )
} }
// NOTE: pass in an empty coinbase address as we don't need it for the check below // NOTE: pass in an empty coinbase address and tracer as we don't need them for the check below
evm := ctd.evmKeeper.NewEVM(coreMsg, ethCfg, params, common.Address{}) evm := ctd.evmKeeper.NewEVM(coreMsg, ethCfg, params, common.Address{}, "")
// check that caller has enough balance to cover asset transfer for **topmost** call // check that caller has enough balance to cover asset transfer for **topmost** call
// NOTE: here the gas consumed is from the context with the infinite gas meter // NOTE: here the gas consumed is from the context with the infinite gas meter

View File

@ -96,6 +96,7 @@ import (
_ "github.com/tharsis/ethermint/client/docs/statik" _ "github.com/tharsis/ethermint/client/docs/statik"
"github.com/tharsis/ethermint/app/ante" "github.com/tharsis/ethermint/app/ante"
srvflags "github.com/tharsis/ethermint/server/flags"
ethermint "github.com/tharsis/ethermint/types" ethermint "github.com/tharsis/ethermint/types"
"github.com/tharsis/ethermint/x/evm" "github.com/tharsis/ethermint/x/evm"
evmrest "github.com/tharsis/ethermint/x/evm/client/rest" evmrest "github.com/tharsis/ethermint/x/evm/client/rest"
@ -333,11 +334,13 @@ func NewEthermintApp(
app.AuthzKeeper = authzkeeper.NewKeeper(keys[authzkeeper.StoreKey], appCodec, app.BaseApp.MsgServiceRouter()) app.AuthzKeeper = authzkeeper.NewKeeper(keys[authzkeeper.StoreKey], appCodec, app.BaseApp.MsgServiceRouter())
tracer := cast.ToString(appOpts.Get(srvflags.EVMTracer))
// Create Ethermint keepers // Create Ethermint keepers
app.EvmKeeper = evmkeeper.NewKeeper( app.EvmKeeper = evmkeeper.NewKeeper(
appCodec, keys[evmtypes.StoreKey], tkeys[evmtypes.TransientKey], app.GetSubspace(evmtypes.ModuleName), appCodec, keys[evmtypes.StoreKey], tkeys[evmtypes.TransientKey], app.GetSubspace(evmtypes.ModuleName),
app.AccountKeeper, app.BankKeeper, app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper,
bApp.Trace(), // debug EVM based on Baseapp options tracer, bApp.Trace(), // debug EVM based on Baseapp options
) )
// Create IBC Keeper // Create IBC Keeper
@ -388,7 +391,7 @@ func NewEthermintApp(
// NOTE: we may consider parsing `appOpts` inside module constructors. For the moment // NOTE: we may consider parsing `appOpts` inside module constructors. For the moment
// we prefer to be more strict in what arguments the modules expect. // we prefer to be more strict in what arguments the modules expect.
var skipGenesisInvariants = cast.ToBool(appOpts.Get(crisis.FlagSkipGenesisInvariants)) skipGenesisInvariants := cast.ToBool(appOpts.Get(crisis.FlagSkipGenesisInvariants))
// NOTE: Any module instantiated in the module manager that is later modified // NOTE: Any module instantiated in the module manager that is later modified
// must be passed by reference here. // must be passed by reference here.

View File

@ -38,6 +38,7 @@ import (
"github.com/tharsis/ethermint/encoding" "github.com/tharsis/ethermint/encoding"
"github.com/tharsis/ethermint/server" "github.com/tharsis/ethermint/server"
servercfg "github.com/tharsis/ethermint/server/config" servercfg "github.com/tharsis/ethermint/server/config"
srvflags "github.com/tharsis/ethermint/server/flags"
ethermint "github.com/tharsis/ethermint/types" ethermint "github.com/tharsis/ethermint/types"
) )
@ -115,7 +116,7 @@ func NewRootCmd() (*cobra.Command, params.EncodingConfig) {
txCommand(), txCommand(),
ethermintclient.KeyCommands(app.DefaultNodeHome), ethermintclient.KeyCommands(app.DefaultNodeHome),
) )
rootCmd = server.AddTxFlags(rootCmd) rootCmd = srvflags.AddTxFlags(rootCmd)
// add rosetta // add rosetta
rootCmd.AddCommand(sdkserver.RosettaCommand(encodingConfig.InterfaceRegistry, encodingConfig.Marshaler)) rootCmd.AddCommand(sdkserver.RosettaCommand(encodingConfig.InterfaceRegistry, encodingConfig.Marshaler))

View File

@ -14,7 +14,7 @@ build:
init: init:
home: "$HOME/.ethermintd" home: "$HOME/.ethermintd"
app: app:
evm-rpc: json-rpc:
address: "0.0.0.0:8545" # change the JSON-RPC address and port address: "0.0.0.0:8545" # change the JSON-RPC address and port
ws-address: "0.0.0.0:8546" # change the JSON-RPC websocket address and port ws-address: "0.0.0.0:8546" # change the JSON-RPC websocket address and port
genesis: genesis:

View File

@ -111,11 +111,11 @@ compatibility for websockets of the [Ethereum's
PubSubAPI](https://geth.ethereum.org/docs/rpc/pubsub), Ethermint needs to cast the Tendermint PubSubAPI](https://geth.ethereum.org/docs/rpc/pubsub), Ethermint needs to cast the Tendermint
responses retreived into the Ethereum types. responses retreived into the Ethereum types.
You can start a connection with the Ethereum websocket using the `--evm-rpc.ws-address` flag when starting You can start a connection with the Ethereum websocket using the `--json-rpc.ws-address` flag when starting
the node (default `"0.0.0.0:8546"`): the node (default `"0.0.0.0:8546"`):
```bash ```bash
ethermintd start --evm-rpc.address"0.0.0.0:8545" --evm-rpc.ws-address="0.0.0.0:8546" --evm.rpc.api="eth,web3,net,txpool,debug" --evm-rpc.enable ethermintd start --json-rpc.address"0.0.0.0:8545" --json-rpc.ws-address="0.0.0.0:8546" --evm.rpc.api="eth,web3,net,txpool,debug" --json-rpc.enable
``` ```
Then, start a websocket subscription with [`ws`](https://github.com/hashrocket/ws) Then, start a websocket subscription with [`ws`](https://github.com/hashrocket/ws)

View File

@ -11,15 +11,15 @@ Learn how to run and setup the JSON-RPC server on Ethermint. {synopsis}
To enable RPC server use the following flag (set to true by default). To enable RPC server use the following flag (set to true by default).
```bash ```bash
ethermintd start --evm-rpc.enable ethermintd start --json-rpc.enable
``` ```
## Defining Namespaces ## Defining Namespaces
`Eth`,`Net` and `Web3` [namespaces](./namespaces) are enabled by default. In order to enable other namespaces use flag `--evm-rpc.api`. `Eth`,`Net` and `Web3` [namespaces](./namespaces) are enabled by default. In order to enable other namespaces use flag `--json-rpc.api`.
```bash ```bash
ethermintd start --evm-rpc.api eth,txpool,personal,net,debug,web3,miner ethermintd start --json-rpc.api eth,txpool,personal,net,debug,web3,miner
``` ```
### CORS ### CORS
@ -27,5 +27,5 @@ ethermintd start --evm-rpc.api eth,txpool,personal,net,debug,web3,miner
If accessing the RPC from a browser, CORS will need to be enabled with the appropriate domain set. Otherwise, JavaScript calls are limit by the same-origin policy and requests will fail: If accessing the RPC from a browser, CORS will need to be enabled with the appropriate domain set. Otherwise, JavaScript calls are limit by the same-origin policy and requests will fail:
```bash ```bash
ethermintd start --evm-rpc.enable-unsafe-cors ethermintd start --json-rpc.enable-unsafe-cors
``` ```

View File

@ -24,6 +24,6 @@ APIs](./../api/JSON-RPC/running_server) to connect with existing web3 tooling.
See the list of supported JSON-RPC API [endpoints](./../api/JSON-RPC/endpoints) and [namespaces](./../api/JSON-RPC/namespaces). See the list of supported JSON-RPC API [endpoints](./../api/JSON-RPC/endpoints) and [namespaces](./../api/JSON-RPC/namespaces).
::: :::
To connect to the JSON-PRC server, start the node with the `--evm-rpc.enable=true` flag and define the namespaces that you would like to run using the `--evm.rpc.api` flag (e.g. `"txpool,eth,web3,net,personal"`. Then, you can point any Ethereum development tooling to `http://localhost:8545` or whatever port you choose with the listen address flag (`--evm-rpc.address`). To connect to the JSON-PRC server, start the node with the `--json-rpc.enable=true` flag and define the namespaces that you would like to run using the `--evm.rpc.api` flag (e.g. `"txpool,eth,web3,net,personal"`. Then, you can point any Ethereum development tooling to `http://localhost:8545` or whatever port you choose with the listen address flag (`--json-rpc.address`).
<!-- TODO: add Rosetta --> <!-- TODO: add Rosetta -->

View File

@ -38,7 +38,7 @@ The instructions for setting up a brand new full node from scratch are the the s
To start your node, just type: To start your node, just type:
```bash ```bash
ethermintd start --evm-rpc.enable=true --evm-rpc.api="eth,web3,net,txpool,debug" ethermintd start --json-rpc.enable=true --json-rpc.api="eth,web3,net,txpool,debug"
``` ```
## Key Management ## Key Management

View File

@ -86,4 +86,4 @@ if [[ $1 == "pending" ]]; then
fi fi
# Start the node (remove the --pruning=nothing flag if historical queries are not needed) # Start the node (remove the --pruning=nothing flag if historical queries are not needed)
ethermintd start --pruning=nothing $TRACE --log_level $LOGLEVEL --minimum-gas-prices=0.0001aphoton --evm-rpc.api eth,txpool,personal,net,debug,web3,miner ethermintd start --pruning=nothing $TRACE --log_level $LOGLEVEL --minimum-gas-prices=0.0001aphoton --json-rpc.api eth,txpool,personal,net,debug,web3,miner

View File

@ -35,7 +35,7 @@ cat $HOME/.ethermint/config/genesis.json | jq '.app_state["mint"]["params"]["min
"$PWD"/build/ethermintd validate-genesis "$PWD"/build/ethermintd validate-genesis
# Start the node (remove the --pruning=nothing flag if historical queries are not needed) in background and log to file # Start the node (remove the --pruning=nothing flag if historical queries are not needed) in background and log to file
"$PWD"/build/ethermintd start --pruning=nothing --rpc.unsafe --evm-rpc.address="0.0.0.0:8545" --keyring-backend test > ethermintd.log 2>&1 & "$PWD"/build/ethermintd start --pruning=nothing --rpc.unsafe --json-rpc.address="0.0.0.0:8545" --keyring-backend test > ethermintd.log 2>&1 &
# Give ethermintd node enough time to launch # Give ethermintd node enough time to launch
sleep 5 sleep 5

View File

@ -106,7 +106,7 @@ start_func() {
echo "starting ethermint node $i in background ..." echo "starting ethermint node $i in background ..."
"$PWD"/build/ethermintd start --pruning=nothing --rpc.unsafe \ "$PWD"/build/ethermintd start --pruning=nothing --rpc.unsafe \
--p2p.laddr tcp://$IP_ADDR:$NODE_P2P_PORT"$i" --address tcp://$IP_ADDR:$NODE_PORT"$i" --rpc.laddr tcp://$IP_ADDR:$NODE_RPC_PORT"$i" \ --p2p.laddr tcp://$IP_ADDR:$NODE_P2P_PORT"$i" --address tcp://$IP_ADDR:$NODE_PORT"$i" --rpc.laddr tcp://$IP_ADDR:$NODE_RPC_PORT"$i" \
--evm-rpc.address=$IP_ADDR:$RPC_PORT"$i" \ --json-rpc.address=$IP_ADDR:$RPC_PORT"$i" \
--keyring-backend test --home "$DATA_DIR$i" \ --keyring-backend test --home "$DATA_DIR$i" \
>"$DATA_DIR"/node"$i".log 2>&1 & disown >"$DATA_DIR"/node"$i".log 2>&1 & disown

View File

@ -92,7 +92,7 @@ start_func() {
echo "starting ethermint node $i in background ..." echo "starting ethermint node $i in background ..."
"$PWD"/build/ethermintd start --pruning=nothing --rpc.unsafe \ "$PWD"/build/ethermintd start --pruning=nothing --rpc.unsafe \
--p2p.laddr tcp://$IP_ADDR:$NODE_P2P_PORT"$i" --address tcp://$IP_ADDR:$NODE_PORT"$i" --rpc.laddr tcp://$IP_ADDR:$NODE_RPC_PORT"$i" \ --p2p.laddr tcp://$IP_ADDR:$NODE_P2P_PORT"$i" --address tcp://$IP_ADDR:$NODE_PORT"$i" --rpc.laddr tcp://$IP_ADDR:$NODE_RPC_PORT"$i" \
--evm-rpc.address=$IP_ADDR:$RPC_PORT"$i" \ --json-rpc.address=$IP_ADDR:$RPC_PORT"$i" \
--keyring-backend test --home "$DATA_DIR$i" \ --keyring-backend test --home "$DATA_DIR$i" \
>"$DATA_DIR"/node"$i".log 2>&1 & disown >"$DATA_DIR"/node"$i".log 2>&1 & disown

View File

@ -1,19 +1,33 @@
package config package config
import ( import (
"github.com/cosmos/cosmos-sdk/server/config" "fmt"
"github.com/spf13/viper" "github.com/spf13/viper"
"github.com/tendermint/tendermint/libs/strings"
"github.com/cosmos/cosmos-sdk/server/config"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
) )
const ( const (
// DefaultGRPCAddress is the default address the gRPC server binds to. // DefaultGRPCAddress is the default address the gRPC server binds to.
DefaultGRPCAddress = "0.0.0.0:9900" DefaultGRPCAddress = "0.0.0.0:9900"
// DefaultEVMAddress is the default address the EVM JSON-RPC server binds to. // DefaultJSONRPCAddress is the default address the JSON-RPC server binds to.
DefaultEVMAddress = "0.0.0.0:8545" DefaultJSONRPCAddress = "0.0.0.0:8545"
// DefaultEVMWSAddress is the default address the EVM WebSocket server binds to. // DefaultJSONRPCWsAddress is the default address the JSON-RPC WebSocket server binds to.
DefaultEVMWSAddress = "0.0.0.0:8546" DefaultJSONRPCWsAddress = "0.0.0.0:8546"
// DefaultEVMTracer is the default vm.Tracer type
DefaultEVMTracer = "json"
)
var (
evmTracers = []string{DefaultEVMTracer, "markdown", "struct", "access_list"}
) )
// GetDefaultAPINamespaces returns the default list of JSON-RPC namespaces that should be enabled // GetDefaultAPINamespaces returns the default list of JSON-RPC namespaces that should be enabled
@ -45,8 +59,9 @@ func AppConfig(denom string) (string, interface{}) {
} }
customAppConfig := Config{ customAppConfig := Config{
Config: *srvCfg, Config: *srvCfg,
EVMRPC: *DefaultEVMConfig(), EVM: *DefaultEVMConfig(),
JSONRPC: *DefaultJSONRPCConfig(),
} }
customAppTemplate := config.DefaultConfigTemplate + DefaultConfigTemplate customAppTemplate := config.DefaultConfigTemplate + DefaultConfigTemplate
@ -57,15 +72,39 @@ func AppConfig(denom string) (string, interface{}) {
// DefaultConfig returns server's default configuration. // DefaultConfig returns server's default configuration.
func DefaultConfig() *Config { func DefaultConfig() *Config {
return &Config{ return &Config{
Config: *config.DefaultConfig(), Config: *config.DefaultConfig(),
EVMRPC: *DefaultEVMConfig(), EVM: *DefaultEVMConfig(),
JSONRPC: *DefaultJSONRPCConfig(),
} }
} }
// EVMRPCConfig defines configuration for the EVM RPC server. // EVMConfig defines the application configuration values for the EVM.
type EVMRPCConfig struct { type EVMConfig struct {
// RPCAddress defines the HTTP server to listen on // Tracer defines vm.Tracer type that the EVM will use if the node is run in
RPCAddress string `mapstructure:"address"` // trace mode. Default: 'json'.
Tracer string `mapstructure:"tracer"`
}
// DefaultEVMConfig returns the default EVM configuration
func DefaultEVMConfig() *EVMConfig {
return &EVMConfig{
Tracer: DefaultEVMTracer,
}
}
// Validate returns an error if the tracer type is invalid.
func (c EVMConfig) Validate() error {
if !strings.StringInSlice(c.Tracer, evmTracers) {
return fmt.Errorf("invalid tracer type %s, available types: %v", c.Tracer, evmTracers)
}
return nil
}
// JSONRPCConfig defines configuration for the EVM RPC server.
type JSONRPCConfig struct {
// Address defines the HTTP server to listen on
Address string `mapstructure:"address"`
// WsAddress defines the WebSocket server to listen on // WsAddress defines the WebSocket server to listen on
WsAddress string `mapstructure:"ws-address"` WsAddress string `mapstructure:"ws-address"`
// API defines a list of JSON-RPC namespaces that should be enabled // API defines a list of JSON-RPC namespaces that should be enabled
@ -76,13 +115,13 @@ type EVMRPCConfig struct {
EnableUnsafeCORS bool `mapstructure:"enable-unsafe-cors"` EnableUnsafeCORS bool `mapstructure:"enable-unsafe-cors"`
} }
// DefaultEVMConfig returns an EVM config with the JSON-RPC API enabled by default // DefaultJSONRPCConfig returns an EVM config with the JSON-RPC API enabled by default
func DefaultEVMConfig() *EVMRPCConfig { func DefaultJSONRPCConfig() *JSONRPCConfig {
return &EVMRPCConfig{ return &JSONRPCConfig{
Enable: true, Enable: true,
API: GetDefaultAPINamespaces(), API: GetDefaultAPINamespaces(),
RPCAddress: DefaultEVMAddress, Address: DefaultJSONRPCAddress,
WsAddress: DefaultEVMWSAddress, WsAddress: DefaultJSONRPCWsAddress,
EnableUnsafeCORS: false, EnableUnsafeCORS: false,
} }
} }
@ -92,7 +131,8 @@ func DefaultEVMConfig() *EVMRPCConfig {
type Config struct { type Config struct {
config.Config config.Config
EVMRPC EVMRPCConfig `mapstructure:"evm-rpc"` EVM EVMConfig `mapstructure:"evm"`
JSONRPC JSONRPCConfig `mapstructure:"json-rpc"`
} }
// GetConfig returns a fully parsed Config object. // GetConfig returns a fully parsed Config object.
@ -101,12 +141,26 @@ func GetConfig(v *viper.Viper) Config {
return Config{ return Config{
Config: cfg, Config: cfg,
EVMRPC: EVMRPCConfig{ EVM: EVMConfig{
Enable: v.GetBool("evm-rpc.enable"), Tracer: v.GetString("evm.tracer"),
API: v.GetStringSlice("evm-rpc.api"), },
RPCAddress: v.GetString("evm-rpc.address"), JSONRPC: JSONRPCConfig{
WsAddress: v.GetString("evm-rpc.ws-address"), Enable: v.GetBool("json-rpc.enable"),
EnableUnsafeCORS: v.GetBool("evm-rpc.enable-unsafe-cors"), API: v.GetStringSlice("json-rpc.api"),
Address: v.GetString("json-rpc.address"),
WsAddress: v.GetString("json-rpc.ws-address"),
EnableUnsafeCORS: v.GetBool("json-rpc.enable-unsafe-cors"),
}, },
} }
} }
// ValidateBasic returns an error any of the application configuration fields are invalid
func (c Config) ValidateBasic() error {
if err := c.EVM.Validate(); err != nil {
return sdkerrors.Wrapf(sdkerrors.ErrAppConfig, "invalid evm config value: %s", err.Error())
}
// TODO: validate JSON-RPC APIs
return c.Config.ValidateBasic()
}

View File

@ -7,8 +7,8 @@ import (
) )
func TestDefaultConfig(t *testing.T) { func TestDefaultConfig(t *testing.T) {
cfg := DefaultEVMConfig() cfg := DefaultConfig()
require.True(t, cfg.Enable) require.True(t, cfg.JSONRPC.Enable)
require.Equal(t, cfg.RPCAddress, DefaultEVMAddress) require.Equal(t, cfg.JSONRPC.Address, DefaultJSONRPCAddress)
require.Equal(t, cfg.WsAddress, DefaultEVMWSAddress) require.Equal(t, cfg.JSONRPC.WsAddress, DefaultJSONRPCWsAddress)
} }

View File

@ -3,24 +3,35 @@ package config
// DefaultConfigTemplate defines the configuration template for the EVM RPC configuration // DefaultConfigTemplate defines the configuration template for the EVM RPC configuration
const DefaultConfigTemplate = ` const DefaultConfigTemplate = `
############################################################################### ###############################################################################
### EVM RPC Configuration ### ### EVM Configuration ###
############################################################################### ###############################################################################
[evm-rpc] [evm]
# Tracer defines the 'vm.Tracer' type that the EVM will use when the node is run in
# debug mode. To enable tracing use the '--trace' flag when starting your node.
# Valid types are: json|struct|access_list|markdown
tracer = "{{ .EVM.Tracer }}"
###############################################################################
### JSON RPC Configuration ###
###############################################################################
[json-rpc]
# Enable defines if the gRPC server should be enabled. # Enable defines if the gRPC server should be enabled.
enable = {{ .EVMRPC.Enable }} enable = {{ .JSONRPC.Enable }}
# Address defines the EVM RPC HTTP server address to bind to. # Address defines the EVM RPC HTTP server address to bind to.
address = "{{ .EVMRPC.RPCAddress }}" address = "{{ .JSONRPC.Address }}"
# Address defines the EVM WebSocket server address to bind to. # Address defines the EVM WebSocket server address to bind to.
ws-address = "{{ .EVMRPC.WsAddress }}" ws-address = "{{ .JSONRPC.WsAddress }}"
# API defines a list of JSON-RPC namespaces that should be enabled # API defines a list of JSON-RPC namespaces that should be enabled
# Example: "eth,txpool,personal,net,debug,web3" # Example: "eth,txpool,personal,net,debug,web3"
api = "{{range $index, $elmt := .EVMRPC.API}}{{if $index}},{{$elmt}}{{else}}{{$elmt}}{{end}}{{end}}" api = "{{range $index, $elmt := .JSONRPC.API}}{{if $index}},{{$elmt}}{{else}}{{$elmt}}{{end}}{{end}}"
# EnableUnsafeCORS defines if CORS should be enabled (unsafe - use it at your own risk) # EnableUnsafeCORS defines if CORS should be enabled (unsafe - use it at your own risk)
enable-unsafe-cors = "{{ .EVMRPC.EnableUnsafeCORS }}" enable-unsafe-cors = "{{ .JSONRPC.EnableUnsafeCORS }}"
` `

View File

@ -1,4 +1,4 @@
package server package flags
import ( import (
"github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/flags"
@ -7,6 +7,37 @@ import (
"github.com/spf13/viper" "github.com/spf13/viper"
) )
// Tendermint full-node start flags
const (
WithTendermint = "with-tendermint"
Address = "address"
Transport = "transport"
TraceStore = "trace-store"
CPUProfile = "cpu-profile"
)
// GRPC-related flags.
const (
GRPCEnable = "grpc.enable"
GRPCAddress = "grpc.address"
GRPCWebEnable = "grpc-web.enable"
GRPCWebAddress = "grpc-web.address"
)
// JSON-RPC flags
const (
JSONRPCEnable = "json-rpc.enable"
JSONRPCAPI = "json-rpc.api"
JSONRPCAddress = "json-rpc.address"
JSONWsAddress = "json-rpc.ws-address"
JSONEnableUnsafeCORS = "json-rpc.enable-unsafe-cors"
)
// EVM flags
const (
EVMTracer = "evm.tracer"
)
// AddTxFlags adds common flags for commands to post tx // AddTxFlags adds common flags for commands to post tx
func AddTxFlags(cmd *cobra.Command) *cobra.Command { func AddTxFlags(cmd *cobra.Command) *cobra.Command {
cmd.PersistentFlags().String(flags.FlagChainID, "testnet", "Specify Chain ID for sending Tx") cmd.PersistentFlags().String(flags.FlagChainID, "testnet", "Specify Chain ID for sending Tx")

View File

@ -17,19 +17,19 @@ import (
"github.com/tharsis/ethermint/server/config" "github.com/tharsis/ethermint/server/config"
) )
// StartEVMRPC start evm rpc server // StartJSONRPC starts the JSON-RPC server
func StartEVMRPC(ctx *server.Context, clientCtx client.Context, tmRPCAddr string, tmEndpoint string, config config.Config) (*http.Server, chan struct{}, error) { func StartJSONRPC(ctx *server.Context, clientCtx client.Context, tmRPCAddr string, tmEndpoint string, config config.Config) (*http.Server, chan struct{}, error) {
tmWsClient := ConnectTmWS(tmRPCAddr, tmEndpoint) tmWsClient := ConnectTmWS(tmRPCAddr, tmEndpoint)
rpcServer := ethrpc.NewServer() rpcServer := ethrpc.NewServer()
rpcAPIArr := config.EVMRPC.API rpcAPIArr := config.JSONRPC.API
apis := rpc.GetRPCAPIs(ctx, clientCtx, tmWsClient, rpcAPIArr) apis := rpc.GetRPCAPIs(ctx, clientCtx, tmWsClient, rpcAPIArr)
for _, api := range apis { for _, api := range apis {
if err := rpcServer.RegisterName(api.Namespace, api.Service); err != nil { if err := rpcServer.RegisterName(api.Namespace, api.Service); err != nil {
ctx.Logger.Error( ctx.Logger.Error(
"failed to register service in EVM RPC namespace", "failed to register service in JSON RPC namespace",
"namespace", api.Namespace, "namespace", api.Namespace,
"service", api.Service, "service", api.Service,
) )
@ -41,43 +41,43 @@ func StartEVMRPC(ctx *server.Context, clientCtx client.Context, tmRPCAddr string
r.HandleFunc("/", rpcServer.ServeHTTP).Methods("POST") r.HandleFunc("/", rpcServer.ServeHTTP).Methods("POST")
handlerWithCors := cors.Default() handlerWithCors := cors.Default()
if config.EVMRPC.EnableUnsafeCORS { if config.JSONRPC.EnableUnsafeCORS {
handlerWithCors = cors.AllowAll() handlerWithCors = cors.AllowAll()
} }
httpSrv := &http.Server{ httpSrv := &http.Server{
Addr: config.EVMRPC.RPCAddress, Addr: config.JSONRPC.Address,
Handler: handlerWithCors.Handler(r), Handler: handlerWithCors.Handler(r),
} }
httpSrvDone := make(chan struct{}, 1) httpSrvDone := make(chan struct{}, 1)
errCh := make(chan error) errCh := make(chan error)
go func() { go func() {
ctx.Logger.Info("Starting EVM RPC server", "address", config.EVMRPC.RPCAddress) ctx.Logger.Info("Starting JSON-RPC server", "address", config.JSONRPC.Address)
if err := httpSrv.ListenAndServe(); err != nil { if err := httpSrv.ListenAndServe(); err != nil {
if err == http.ErrServerClosed { if err == http.ErrServerClosed {
close(httpSrvDone) close(httpSrvDone)
return return
} }
ctx.Logger.Error("failed to start EVM RPC server", "error", err.Error()) ctx.Logger.Error("failed to start JSON-RPC server", "error", err.Error())
errCh <- err errCh <- err
} }
}() }()
select { select {
case err := <-errCh: case err := <-errCh:
ctx.Logger.Error("failed to boot EVM RPC server", "error", err.Error()) ctx.Logger.Error("failed to boot JSON-RPC server", "error", err.Error())
return nil, nil, err return nil, nil, err
case <-time.After(types.ServerStartTime): // assume EVM RPC server started successfully case <-time.After(types.ServerStartTime): // assume JSON RPC server started successfully
} }
ctx.Logger.Info("Starting EVM WebSocket server", "address", config.EVMRPC.WsAddress) ctx.Logger.Info("Starting JSON WebSocket server", "address", config.JSONRPC.WsAddress)
_, port, _ := net.SplitHostPort(config.EVMRPC.RPCAddress) _, port, _ := net.SplitHostPort(config.JSONRPC.Address)
// allocate separate WS connection to Tendermint // allocate separate WS connection to Tendermint
tmWsClient = ConnectTmWS(tmRPCAddr, tmEndpoint) tmWsClient = ConnectTmWS(tmRPCAddr, tmEndpoint)
wsSrv := rpc.NewWebsocketsServer(ctx.Logger, tmWsClient, "localhost:"+port, config.EVMRPC.WsAddress) wsSrv := rpc.NewWebsocketsServer(ctx.Logger, tmWsClient, "localhost:"+port, config.JSONRPC.WsAddress)
wsSrv.Start() wsSrv.Start()
return httpSrv, httpSrvDone, nil return httpSrv, httpSrvDone, nil
} }

View File

@ -8,6 +8,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"runtime/pprof" "runtime/pprof"
"strings"
"time" "time"
"github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec"
@ -42,48 +43,7 @@ import (
ethdebug "github.com/tharsis/ethermint/ethereum/rpc/namespaces/debug" ethdebug "github.com/tharsis/ethermint/ethereum/rpc/namespaces/debug"
"github.com/tharsis/ethermint/server/config" "github.com/tharsis/ethermint/server/config"
) srvflags "github.com/tharsis/ethermint/server/flags"
// Tendermint full-node start flags
const (
flagWithTendermint = "with-tendermint"
flagAddress = "address"
flagTransport = "transport"
flagTraceStore = "trace-store"
flagCPUProfile = "cpu-profile"
FlagMinGasPrices = "minimum-gas-prices"
FlagHaltHeight = "halt-height"
FlagHaltTime = "halt-time"
FlagInterBlockCache = "inter-block-cache"
FlagUnsafeSkipUpgrades = "unsafe-skip-upgrades"
FlagTrace = "trace"
FlagInvCheckPeriod = "inv-check-period"
FlagPruning = "pruning"
FlagPruningKeepRecent = "pruning-keep-recent"
FlagPruningKeepEvery = "pruning-keep-every"
FlagPruningInterval = "pruning-interval"
FlagIndexEvents = "index-events"
FlagMinRetainBlocks = "min-retain-blocks"
)
// GRPC-related flags.
const (
flagGRPCEnable = "grpc.enable"
flagGRPCAddress = "grpc.address"
flagEVMRPCEnable = "evm-rpc.enable"
flagEVMRPCAPI = "evm-rpc.api"
flagEVMRPCAddress = "evm-rpc.address"
flagEVMWSAddress = "evm-rpc.ws-address"
flagEVMEnableUnsafeCORS = "evm-rpc.enable-unsafe-cors"
flagGRPCWebEnable = "grpc-web.enable"
flagGRPCWebAddress = "grpc-web.address"
)
// State sync-related flags.
const (
FlagStateSyncSnapshotInterval = "state-sync.snapshot-interval"
FlagStateSyncSnapshotKeepRecent = "state-sync.snapshot-keep-recent"
) )
// StartCmd runs the service passed in, either stand-alone or in-process with // StartCmd runs the service passed in, either stand-alone or in-process with
@ -134,7 +94,7 @@ which accepts a path for the resulting pprof file.
return err return err
} }
withTM, _ := cmd.Flags().GetBool(flagWithTendermint) withTM, _ := cmd.Flags().GetBool(srvflags.WithTendermint)
if !withTM { if !withTM {
serverCtx.Logger.Info("starting ABCI without Tendermint") serverCtx.Logger.Info("starting ABCI without Tendermint")
return startStandAlone(serverCtx, appCreator) return startStandAlone(serverCtx, appCreator)
@ -155,38 +115,39 @@ which accepts a path for the resulting pprof file.
} }
cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory") cmd.Flags().String(flags.FlagHome, defaultNodeHome, "The application home directory")
cmd.Flags().Bool(flagWithTendermint, true, "Run abci app embedded in-process with tendermint") cmd.Flags().Bool(srvflags.WithTendermint, true, "Run abci app embedded in-process with tendermint")
cmd.Flags().String(flagAddress, "tcp://0.0.0.0:26658", "Listen address") cmd.Flags().String(srvflags.Address, "tcp://0.0.0.0:26658", "Listen address")
cmd.Flags().String(flagTransport, "socket", "Transport protocol: socket, grpc") cmd.Flags().String(srvflags.Transport, "socket", "Transport protocol: socket, grpc")
cmd.Flags().String(flagTraceStore, "", "Enable KVStore tracing to an output file") cmd.Flags().String(srvflags.TraceStore, "", "Enable KVStore tracing to an output file")
cmd.Flags().String(FlagMinGasPrices, "", "Minimum gas prices to accept for transactions; Any fee in a tx must meet this minimum (e.g. 0.01photino;0.0001stake)") cmd.Flags().String(server.FlagMinGasPrices, "", "Minimum gas prices to accept for transactions; Any fee in a tx must meet this minimum (e.g. 0.01photino;0.0001stake)")
cmd.Flags().IntSlice(FlagUnsafeSkipUpgrades, []int{}, "Skip a set of upgrade heights to continue the old binary") cmd.Flags().IntSlice(server.FlagUnsafeSkipUpgrades, []int{}, "Skip a set of upgrade heights to continue the old binary")
cmd.Flags().Uint64(FlagHaltHeight, 0, "Block height at which to gracefully halt the chain and shutdown the node") cmd.Flags().Uint64(server.FlagHaltHeight, 0, "Block height at which to gracefully halt the chain and shutdown the node")
cmd.Flags().Uint64(FlagHaltTime, 0, "Minimum block time (in Unix seconds) at which to gracefully halt the chain and shutdown the node") cmd.Flags().Uint64(server.FlagHaltTime, 0, "Minimum block time (in Unix seconds) at which to gracefully halt the chain and shutdown the node")
cmd.Flags().Bool(FlagInterBlockCache, true, "Enable inter-block caching") cmd.Flags().Bool(server.FlagInterBlockCache, true, "Enable inter-block caching")
cmd.Flags().String(flagCPUProfile, "", "Enable CPU profiling and write to the provided file") cmd.Flags().String(srvflags.CPUProfile, "", "Enable CPU profiling and write to the provided file")
cmd.Flags().Bool(FlagTrace, false, "Provide full stack traces for errors in ABCI Log") cmd.Flags().Bool(server.FlagTrace, false, "Provide full stack traces for errors in ABCI Log")
cmd.Flags().String(FlagPruning, storetypes.PruningOptionDefault, "Pruning strategy (default|nothing|everything|custom)") cmd.Flags().String(server.FlagPruning, storetypes.PruningOptionDefault, "Pruning strategy (default|nothing|everything|custom)")
cmd.Flags().Uint64(FlagPruningKeepRecent, 0, "Number of recent heights to keep on disk (ignored if pruning is not 'custom')") cmd.Flags().Uint64(server.FlagPruningKeepRecent, 0, "Number of recent heights to keep on disk (ignored if pruning is not 'custom')")
cmd.Flags().Uint64(FlagPruningKeepEvery, 0, "Offset heights to keep on disk after 'keep-every' (ignored if pruning is not 'custom')") cmd.Flags().Uint64(server.FlagPruningKeepEvery, 0, "Offset heights to keep on disk after 'keep-every' (ignored if pruning is not 'custom')")
cmd.Flags().Uint64(FlagPruningInterval, 0, "Height interval at which pruned heights are removed from disk (ignored if pruning is not 'custom')") cmd.Flags().Uint64(server.FlagPruningInterval, 0, "Height interval at which pruned heights are removed from disk (ignored if pruning is not 'custom')")
cmd.Flags().Uint(FlagInvCheckPeriod, 0, "Assert registered invariants every N blocks") cmd.Flags().Uint(server.FlagInvCheckPeriod, 0, "Assert registered invariants every N blocks")
cmd.Flags().Uint64(FlagMinRetainBlocks, 0, "Minimum block height offset during ABCI commit to prune Tendermint blocks") cmd.Flags().Uint64(server.FlagMinRetainBlocks, 0, "Minimum block height offset during ABCI commit to prune Tendermint blocks")
cmd.Flags().Bool(flagGRPCEnable, true, "Define if the gRPC server should be enabled") cmd.Flags().Bool(srvflags.GRPCEnable, true, "Define if the gRPC server should be enabled")
cmd.Flags().String(flagGRPCAddress, serverconfig.DefaultGRPCAddress, "the gRPC server address to listen on") cmd.Flags().String(srvflags.GRPCAddress, serverconfig.DefaultGRPCAddress, "the gRPC server address to listen on")
cmd.Flags().Bool(srvflags.GRPCWebEnable, true, "Define if the gRPC-Web server should be enabled. (Note: gRPC must also be enabled.)")
cmd.Flags().String(srvflags.GRPCWebAddress, serverconfig.DefaultGRPCWebAddress, "The gRPC-Web server address to listen on")
cmd.Flags().Bool(flagGRPCWebEnable, true, "Define if the gRPC-Web server should be enabled. (Note: gRPC must also be enabled.)") cmd.Flags().Bool(srvflags.JSONRPCEnable, true, "Define if the gRPC server should be enabled")
cmd.Flags().String(flagGRPCWebAddress, serverconfig.DefaultGRPCWebAddress, "The gRPC-Web server address to listen on") cmd.Flags().StringSlice(srvflags.JSONRPCAPI, config.GetDefaultAPINamespaces(), "Defines a list of JSON-RPC namespaces that should be enabled")
cmd.Flags().String(srvflags.JSONRPCAddress, config.DefaultJSONRPCAddress, "the JSON-RPC server address to listen on")
cmd.Flags().String(srvflags.JSONWsAddress, config.DefaultJSONRPCWsAddress, "the JSON-RPC WS server address to listen on")
cmd.Flags().Bool(srvflags.JSONEnableUnsafeCORS, false, "Define if the JSON-RPC server should enabled CORS (unsafe - use it at your own risk)")
cmd.Flags().Bool(flagEVMRPCEnable, true, "Define if the gRPC server should be enabled") cmd.Flags().String(srvflags.EVMTracer, config.DefaultEVMTracer, "the EVM tracer type to collect execution traces from the EVM transaction execution (json|struct|access_list|markdown)")
cmd.Flags().StringSlice(flagEVMRPCAPI, config.GetDefaultAPINamespaces(), "Defines a list of JSON-RPC namespaces that should be enabled")
cmd.Flags().String(flagEVMRPCAddress, config.DefaultEVMAddress, "the EVM RPC server address to listen on")
cmd.Flags().String(flagEVMWSAddress, config.DefaultEVMWSAddress, "the EVM WS server address to listen on")
cmd.Flags().Bool(flagEVMEnableUnsafeCORS, false, "Define if the EVM RPC server should enabled CORS (unsafe - use it at your own risk)")
cmd.Flags().Uint64(FlagStateSyncSnapshotInterval, 0, "State sync snapshot interval") cmd.Flags().Uint64(server.FlagStateSyncSnapshotInterval, 0, "State sync snapshot interval")
cmd.Flags().Uint32(FlagStateSyncSnapshotKeepRecent, 2, "State sync snapshot to keep") cmd.Flags().Uint32(server.FlagStateSyncSnapshotKeepRecent, 2, "State sync snapshot to keep")
// add support for all Tendermint-specific command line options // add support for all Tendermint-specific command line options
tcmd.AddNodeFlags(cmd) tcmd.AddNodeFlags(cmd)
@ -194,8 +155,8 @@ which accepts a path for the resulting pprof file.
} }
func startStandAlone(ctx *server.Context, appCreator types.AppCreator) error { func startStandAlone(ctx *server.Context, appCreator types.AppCreator) error {
addr := ctx.Viper.GetString(flagAddress) addr := ctx.Viper.GetString(srvflags.Address)
transport := ctx.Viper.GetString(flagTransport) transport := ctx.Viper.GetString(srvflags.Transport)
home := ctx.Viper.GetString(flags.FlagHome) home := ctx.Viper.GetString(flags.FlagHome)
db, err := openDB(home) db, err := openDB(home)
@ -203,7 +164,7 @@ func startStandAlone(ctx *server.Context, appCreator types.AppCreator) error {
return err return err
} }
traceWriterFile := ctx.Viper.GetString(flagTraceStore) traceWriterFile := ctx.Viper.GetString(srvflags.TraceStore)
traceWriter, err := openTraceWriter(traceWriterFile) traceWriter, err := openTraceWriter(traceWriterFile)
if err != nil { if err != nil {
return err return err
@ -240,7 +201,7 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, appCreator ty
logger := ctx.Logger logger := ctx.Logger
var cpuProfileCleanup func() var cpuProfileCleanup func()
if cpuProfile := ctx.Viper.GetString(flagCPUProfile); cpuProfile != "" { if cpuProfile := ctx.Viper.GetString(srvflags.CPUProfile); cpuProfile != "" {
f, err := os.Create(ethdebug.ExpandHome(cpuProfile)) f, err := os.Create(ethdebug.ExpandHome(cpuProfile))
if err != nil { if err != nil {
return err return err
@ -258,7 +219,7 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, appCreator ty
} }
} }
traceWriterFile := ctx.Viper.GetString(flagTraceStore) traceWriterFile := ctx.Viper.GetString(srvflags.TraceStore)
db, err := openDB(home) db, err := openDB(home)
if err != nil { if err != nil {
logger.Error("failed to open DB", "error", err.Error()) logger.Error("failed to open DB", "error", err.Error())
@ -272,10 +233,17 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, appCreator ty
} }
config := config.GetConfig(ctx.Viper) config := config.GetConfig(ctx.Viper)
if err := config.ValidateBasic(); err != nil { if err := config.ValidateBasic(); err != nil {
ctx.Logger.Error("WARNING: The minimum-gas-prices config in app.toml is set to the empty string. " + if strings.Contains(err.Error(), "set min gas price in app.toml or flag or env variable") {
"This defaults to 0 in the current version, but will error in the next version " + ctx.Logger.Error(
"(SDK v0.44). Please explicitly put the desired minimum-gas-prices in your app.toml.") "WARNING: The minimum-gas-prices config in app.toml is set to the empty string. " +
"This defaults to 0 in the current version, but will error in the next version " +
"(SDK v0.44). Please explicitly put the desired minimum-gas-prices in your app.toml.",
)
} else {
return err
}
} }
app := appCreator(ctx.Logger, db, traceWriter, ctx.Viper) app := appCreator(ctx.Logger, db, traceWriter, ctx.Viper)
@ -405,7 +373,7 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, appCreator ty
httpSrv *http.Server httpSrv *http.Server
httpSrvDone chan struct{} httpSrvDone chan struct{}
) )
if config.EVMRPC.Enable { if config.JSONRPC.Enable {
genDoc, err := genDocProvider() genDoc, err := genDocProvider()
if err != nil { if err != nil {
return err return err
@ -415,7 +383,7 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, appCreator ty
tmEndpoint := "/websocket" tmEndpoint := "/websocket"
tmRPCAddr := cfg.RPC.ListenAddress tmRPCAddr := cfg.RPC.ListenAddress
httpSrv, httpSrvDone, err = StartEVMRPC(ctx, clientCtx, tmRPCAddr, tmEndpoint, config) httpSrv, httpSrvDone, err = StartJSONRPC(ctx, clientCtx, tmRPCAddr, tmEndpoint, config)
if err != nil { if err != nil {
return err return err
} }

View File

@ -43,4 +43,4 @@ ethermintd collect-gentxs
ethermintd validate-genesis ethermintd validate-genesis
# Start the node (remove the --pruning=nothing flag if historical queries are not needed) # Start the node (remove the --pruning=nothing flag if historical queries are not needed)
ethermintd start --pruning=nothing --rpc.unsafe --keyring-backend test --trace --log_level info --evm-rpc.api eth,txpool,personal,net,debug,web3 ethermintd start --pruning=nothing --rpc.unsafe --keyring-backend test --trace --log_level info --json-rpc.api eth,txpool,personal,net,debug,web3

View File

@ -230,7 +230,7 @@ func New(t *testing.T, cfg Config) *Network {
jsonRPCAddr := "" jsonRPCAddr := ""
tmCfg.RPC.ListenAddress = "" tmCfg.RPC.ListenAddress = ""
appCfg.GRPC.Enable = false appCfg.GRPC.Enable = false
appCfg.EVMRPC.Enable = false appCfg.JSONRPC.Enable = false
if i == 0 { if i == 0 {
apiListenAddr, _, err := server.FreeTCPAddr() apiListenAddr, _, err := server.FreeTCPAddr()
@ -244,8 +244,8 @@ func New(t *testing.T, cfg Config) *Network {
jsonRPCListenAddr, _, err := server.FreeTCPAddr() jsonRPCListenAddr, _, err := server.FreeTCPAddr()
require.NoError(t, err) require.NoError(t, err)
t.Log(jsonRPCListenAddr) t.Log(jsonRPCListenAddr)
appCfg.EVMRPC.RPCAddress = jsonRPCListenAddr appCfg.JSONRPC.Address = jsonRPCListenAddr
appCfg.EVMRPC.Enable = true appCfg.JSONRPC.Enable = true
jsonRPCAPIURL, err := url.Parse(jsonRPCListenAddr) jsonRPCAPIURL, err := url.Parse(jsonRPCListenAddr)
require.NoError(t, err) require.NoError(t, err)

View File

@ -114,14 +114,14 @@ func startInProcess(cfg Config, val *Validator) error {
val.grpc = grpcSrv val.grpc = grpcSrv
} }
if val.AppConfig.EVMRPC.Enable { if val.AppConfig.JSONRPC.Enable {
tmEndpoint := "/websocket" tmEndpoint := "/websocket"
tmRPCAddr := val.Ctx.Config.RPC.ListenAddress tmRPCAddr := val.Ctx.Config.RPC.ListenAddress
tmWsClient := ethsrv.ConnectTmWS(tmRPCAddr, tmEndpoint) tmWsClient := ethsrv.ConnectTmWS(tmRPCAddr, tmEndpoint)
val.jsonRPC = jsonrpc.NewServer() val.jsonRPC = jsonrpc.NewServer()
rpcAPIArr := val.AppConfig.EVMRPC.API rpcAPIArr := val.AppConfig.JSONRPC.API
apis := rpc.GetRPCAPIs(val.Ctx, val.ClientCtx, tmWsClient, rpcAPIArr) apis := rpc.GetRPCAPIs(val.Ctx, val.ClientCtx, tmWsClient, rpcAPIArr)
for _, api := range apis { for _, api := range apis {
@ -153,8 +153,8 @@ func startInProcess(cfg Config, val *Validator) error {
}) })
httpSrv := &http.Server{ httpSrv := &http.Server{
Addr: strings.TrimPrefix(val.AppConfig.EVMRPC.RPCAddress, "tcp://"), // FIXME: timeouts Addr: strings.TrimPrefix(val.AppConfig.JSONRPC.Address, "tcp://"), // FIXME: timeouts
// Addr: val.AppConfig.EVMRPC.RPCAddress, // FIXME: address has too many colons // Addr: val.AppConfig.JSONRPC.RPCAddress, // FIXME: address has too many colons
Handler: handlerWithCors.Handler(r), Handler: handlerWithCors.Handler(r),
} }

View File

@ -378,7 +378,8 @@ func (k Keeper) EthCall(c context.Context, req *types.EthCallRequest) (*types.Ms
return nil, status.Error(codes.Internal, err.Error()) return nil, status.Error(codes.Internal, err.Error())
} }
evm := k.NewEVM(msg, ethCfg, params, coinbase) evm := k.NewEVM(msg, ethCfg, params, coinbase, k.tracer)
// pass true means execute in query mode, which don't do actual gas refund. // pass true means execute in query mode, which don't do actual gas refund.
res, err := k.ApplyMessage(evm, msg, ethCfg, true) res, err := k.ApplyMessage(evm, msg, ethCfg, true)
k.ctxStack.RevertAll() k.ctxStack.RevertAll()
@ -448,7 +449,7 @@ func (k Keeper) EstimateGas(c context.Context, req *types.EthCallRequest) (*type
k.WithContext(ctx) k.WithContext(ctx)
msg := args.ToMessage(req.GasCap) msg := args.ToMessage(req.GasCap)
evm := k.NewEVM(msg, ethCfg, params, coinbase) evm := k.NewEVM(msg, ethCfg, params, coinbase, k.tracer)
// pass true means execute in query mode, which don't do actual gas refund. // pass true means execute in query mode, which don't do actual gas refund.
rsp, err := k.ApplyMessage(evm, msg, ethCfg, true) rsp, err := k.ApplyMessage(evm, msg, ethCfg, true)

View File

@ -48,6 +48,9 @@ type Keeper struct {
// chain ID number obtained from the context's chain id // chain ID number obtained from the context's chain id
eip155ChainID *big.Int eip155ChainID *big.Int
// Tracer used to collect execution traces from the EVM transaction execution
tracer string
// trace EVM state transition execution. This value is obtained from the `--trace` flag. // trace EVM state transition execution. This value is obtained from the `--trace` flag.
// For more info check https://geth.ethereum.org/docs/dapp/tracing // For more info check https://geth.ethereum.org/docs/dapp/tracing
debug bool debug bool
@ -58,7 +61,7 @@ func NewKeeper(
cdc codec.BinaryCodec, cdc codec.BinaryCodec,
storeKey, transientKey sdk.StoreKey, paramSpace paramtypes.Subspace, storeKey, transientKey sdk.StoreKey, paramSpace paramtypes.Subspace,
ak types.AccountKeeper, bankKeeper types.BankKeeper, sk types.StakingKeeper, ak types.AccountKeeper, bankKeeper types.BankKeeper, sk types.StakingKeeper,
debug bool, tracer string, debug bool,
) *Keeper { ) *Keeper {
// ensure evm module account is set // ensure evm module account is set
@ -80,6 +83,7 @@ func NewKeeper(
stakingKeeper: sk, stakingKeeper: sk,
storeKey: storeKey, storeKey: storeKey,
transientKey: transientKey, transientKey: transientKey,
tracer: tracer,
debug: debug, debug: debug,
} }
} }

View File

@ -2,7 +2,6 @@ package keeper
import ( import (
"math/big" "math/big"
"os"
"time" "time"
"github.com/palantir/stacktrace" "github.com/palantir/stacktrace"
@ -28,7 +27,13 @@ import (
// (ChainConfig and module Params). It additionally sets the validator operator address as the // (ChainConfig and module Params). It additionally sets the validator operator address as the
// coinbase address to make it available for the COINBASE opcode, even though there is no // coinbase address to make it available for the COINBASE opcode, even though there is no
// beneficiary of the coinbase transaction (since we're not mining). // beneficiary of the coinbase transaction (since we're not mining).
func (k *Keeper) NewEVM(msg core.Message, config *params.ChainConfig, params types.Params, coinbase common.Address) *vm.EVM { func (k *Keeper) NewEVM(
msg core.Message,
config *params.ChainConfig,
params types.Params,
coinbase common.Address,
tracer string,
) *vm.EVM {
blockCtx := vm.BlockContext{ blockCtx := vm.BlockContext{
CanTransfer: core.CanTransfer, CanTransfer: core.CanTransfer,
Transfer: core.Transfer, Transfer: core.Transfer,
@ -41,18 +46,20 @@ func (k *Keeper) NewEVM(msg core.Message, config *params.ChainConfig, params typ
} }
txCtx := core.NewEVMTxContext(msg) txCtx := core.NewEVMTxContext(msg)
vmConfig := k.VMConfig(params) vmConfig := k.VMConfig(msg, params, tracer)
return vm.NewEVM(blockCtx, txCtx, k, config, vmConfig) return vm.NewEVM(blockCtx, txCtx, k, config, vmConfig)
} }
// VMConfig creates an EVM configuration from the debug setting and the extra EIPs enabled on the // VMConfig creates an EVM configuration from the debug setting and the extra EIPs enabled on the
// module parameters. The config generated uses the default JumpTable from the EVM. // module parameters. The config generated uses the default JumpTable from the EVM.
func (k Keeper) VMConfig(params types.Params) vm.Config { func (k Keeper) VMConfig(msg core.Message, params types.Params, tracer string) vm.Config {
cfg := params.ChainConfig.EthereumConfig(k.eip155ChainID)
return vm.Config{ return vm.Config{
Debug: k.debug, Debug: k.debug,
Tracer: vm.NewJSONLogger(&vm.LogConfig{Debug: k.debug}, os.Stderr), // TODO: consider using the Struct Logger too Tracer: types.NewTracer(tracer, msg, cfg, k.Ctx().BlockHeight(), k.debug),
NoRecursion: false, // TODO: consider disabling recursion though params NoRecursion: false, // TODO: consider disabling recursion though params
ExtraEips: params.EIPs(), ExtraEips: params.EIPs(),
} }
} }
@ -154,7 +161,7 @@ func (k *Keeper) ApplyTransaction(tx *ethtypes.Transaction) (*types.MsgEthereumT
} }
// create an ethereum EVM instance and run the message // create an ethereum EVM instance and run the message
evm := k.NewEVM(msg, ethCfg, params, coinbase) evm := k.NewEVM(msg, ethCfg, params, coinbase, k.tracer)
txHash := tx.Hash() txHash := tx.Hash()

40
x/evm/types/tracer.go Normal file
View File

@ -0,0 +1,40 @@
package types
import (
"math/big"
"os"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params"
)
const (
TracerAccessList = "access_list"
TracerJSON = "json"
TracerStruct = "struct"
TracerMarkdown = "markdown"
)
// NewTracer creates a new Logger tracer to collect execution traces from an
// EVM transaction.
func NewTracer(tracer string, msg core.Message, cfg *params.ChainConfig, height int64, debug bool) vm.Tracer {
// TODO: enable additional log configuration
logCfg := &vm.LogConfig{
Debug: debug,
}
switch tracer {
case TracerAccessList:
precompiles := vm.ActivePrecompiles(cfg.Rules(big.NewInt(height)))
return vm.NewAccessListTracer(msg.AccessList(), msg.From(), *msg.To(), precompiles)
case TracerJSON:
return vm.NewJSONLogger(logCfg, os.Stderr)
case TracerMarkdown:
return vm.NewMarkdownLogger(logCfg, os.Stdout) // TODO: Stderr ?
case TracerStruct:
return vm.NewStructLogger(logCfg)
default:
return nil
}
}