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
* (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#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.
@ -53,6 +55,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
### 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#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.
@ -65,6 +68,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
### 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)
* (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.

View File

@ -27,7 +27,7 @@ type EVMKeeper interface {
WithContext(ctx sdk.Context)
ResetRefundTransient(ctx sdk.Context)
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
}
@ -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
evm := ctd.evmKeeper.NewEVM(coreMsg, ethCfg, params, common.Address{})
// 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{}, "")
// 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

View File

@ -96,6 +96,7 @@ import (
_ "github.com/tharsis/ethermint/client/docs/statik"
"github.com/tharsis/ethermint/app/ante"
srvflags "github.com/tharsis/ethermint/server/flags"
ethermint "github.com/tharsis/ethermint/types"
"github.com/tharsis/ethermint/x/evm"
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())
tracer := cast.ToString(appOpts.Get(srvflags.EVMTracer))
// Create Ethermint keepers
app.EvmKeeper = evmkeeper.NewKeeper(
appCodec, keys[evmtypes.StoreKey], tkeys[evmtypes.TransientKey], app.GetSubspace(evmtypes.ModuleName),
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
@ -388,7 +391,7 @@ func NewEthermintApp(
// NOTE: we may consider parsing `appOpts` inside module constructors. For the moment
// 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
// must be passed by reference here.

View File

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

View File

@ -14,7 +14,7 @@ build:
init:
home: "$HOME/.ethermintd"
app:
evm-rpc:
json-rpc:
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
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
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"`):
```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)

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).
```bash
ethermintd start --evm-rpc.enable
ethermintd start --json-rpc.enable
```
## 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
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
@ -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:
```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).
:::
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 -->

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:
```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

View File

@ -86,4 +86,4 @@ if [[ $1 == "pending" ]]; then
fi
# 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
# 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
sleep 5

View File

@ -106,7 +106,7 @@ start_func() {
echo "starting ethermint node $i in background ..."
"$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" \
--evm-rpc.address=$IP_ADDR:$RPC_PORT"$i" \
--json-rpc.address=$IP_ADDR:$RPC_PORT"$i" \
--keyring-backend test --home "$DATA_DIR$i" \
>"$DATA_DIR"/node"$i".log 2>&1 & disown

View File

@ -92,7 +92,7 @@ start_func() {
echo "starting ethermint node $i in background ..."
"$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" \
--evm-rpc.address=$IP_ADDR:$RPC_PORT"$i" \
--json-rpc.address=$IP_ADDR:$RPC_PORT"$i" \
--keyring-backend test --home "$DATA_DIR$i" \
>"$DATA_DIR"/node"$i".log 2>&1 & disown

View File

@ -1,19 +1,33 @@
package config
import (
"github.com/cosmos/cosmos-sdk/server/config"
"fmt"
"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 (
// DefaultGRPCAddress is the default address the gRPC server binds to.
DefaultGRPCAddress = "0.0.0.0:9900"
// DefaultEVMAddress is the default address the EVM JSON-RPC server binds to.
DefaultEVMAddress = "0.0.0.0:8545"
// DefaultJSONRPCAddress is the default address the JSON-RPC server binds to.
DefaultJSONRPCAddress = "0.0.0.0:8545"
// DefaultEVMWSAddress is the default address the EVM WebSocket server binds to.
DefaultEVMWSAddress = "0.0.0.0:8546"
// DefaultJSONRPCWsAddress is the default address the JSON-RPC WebSocket server binds to.
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
@ -45,8 +59,9 @@ func AppConfig(denom string) (string, interface{}) {
}
customAppConfig := Config{
Config: *srvCfg,
EVMRPC: *DefaultEVMConfig(),
Config: *srvCfg,
EVM: *DefaultEVMConfig(),
JSONRPC: *DefaultJSONRPCConfig(),
}
customAppTemplate := config.DefaultConfigTemplate + DefaultConfigTemplate
@ -57,15 +72,39 @@ func AppConfig(denom string) (string, interface{}) {
// DefaultConfig returns server's default configuration.
func DefaultConfig() *Config {
return &Config{
Config: *config.DefaultConfig(),
EVMRPC: *DefaultEVMConfig(),
Config: *config.DefaultConfig(),
EVM: *DefaultEVMConfig(),
JSONRPC: *DefaultJSONRPCConfig(),
}
}
// EVMRPCConfig defines configuration for the EVM RPC server.
type EVMRPCConfig struct {
// RPCAddress defines the HTTP server to listen on
RPCAddress string `mapstructure:"address"`
// EVMConfig defines the application configuration values for the EVM.
type EVMConfig struct {
// Tracer defines vm.Tracer type that the EVM will use if the node is run in
// 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 string `mapstructure:"ws-address"`
// 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"`
}
// DefaultEVMConfig returns an EVM config with the JSON-RPC API enabled by default
func DefaultEVMConfig() *EVMRPCConfig {
return &EVMRPCConfig{
// DefaultJSONRPCConfig returns an EVM config with the JSON-RPC API enabled by default
func DefaultJSONRPCConfig() *JSONRPCConfig {
return &JSONRPCConfig{
Enable: true,
API: GetDefaultAPINamespaces(),
RPCAddress: DefaultEVMAddress,
WsAddress: DefaultEVMWSAddress,
Address: DefaultJSONRPCAddress,
WsAddress: DefaultJSONRPCWsAddress,
EnableUnsafeCORS: false,
}
}
@ -92,7 +131,8 @@ func DefaultEVMConfig() *EVMRPCConfig {
type Config struct {
config.Config
EVMRPC EVMRPCConfig `mapstructure:"evm-rpc"`
EVM EVMConfig `mapstructure:"evm"`
JSONRPC JSONRPCConfig `mapstructure:"json-rpc"`
}
// GetConfig returns a fully parsed Config object.
@ -101,12 +141,26 @@ func GetConfig(v *viper.Viper) Config {
return Config{
Config: cfg,
EVMRPC: EVMRPCConfig{
Enable: v.GetBool("evm-rpc.enable"),
API: v.GetStringSlice("evm-rpc.api"),
RPCAddress: v.GetString("evm-rpc.address"),
WsAddress: v.GetString("evm-rpc.ws-address"),
EnableUnsafeCORS: v.GetBool("evm-rpc.enable-unsafe-cors"),
EVM: EVMConfig{
Tracer: v.GetString("evm.tracer"),
},
JSONRPC: JSONRPCConfig{
Enable: v.GetBool("json-rpc.enable"),
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) {
cfg := DefaultEVMConfig()
require.True(t, cfg.Enable)
require.Equal(t, cfg.RPCAddress, DefaultEVMAddress)
require.Equal(t, cfg.WsAddress, DefaultEVMWSAddress)
cfg := DefaultConfig()
require.True(t, cfg.JSONRPC.Enable)
require.Equal(t, cfg.JSONRPC.Address, DefaultJSONRPCAddress)
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
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 = {{ .EVMRPC.Enable }}
enable = {{ .JSONRPC.Enable }}
# 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.
ws-address = "{{ .EVMRPC.WsAddress }}"
ws-address = "{{ .JSONRPC.WsAddress }}"
# API defines a list of JSON-RPC namespaces that should be enabled
# 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)
enable-unsafe-cors = "{{ .EVMRPC.EnableUnsafeCORS }}"
enable-unsafe-cors = "{{ .JSONRPC.EnableUnsafeCORS }}"
`

View File

@ -1,4 +1,4 @@
package server
package flags
import (
"github.com/cosmos/cosmos-sdk/client/flags"
@ -7,6 +7,37 @@ import (
"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
func AddTxFlags(cmd *cobra.Command) *cobra.Command {
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"
)
// StartEVMRPC start evm rpc server
func StartEVMRPC(ctx *server.Context, clientCtx client.Context, tmRPCAddr string, tmEndpoint string, config config.Config) (*http.Server, chan struct{}, error) {
// StartJSONRPC starts the JSON-RPC server
func StartJSONRPC(ctx *server.Context, clientCtx client.Context, tmRPCAddr string, tmEndpoint string, config config.Config) (*http.Server, chan struct{}, error) {
tmWsClient := ConnectTmWS(tmRPCAddr, tmEndpoint)
rpcServer := ethrpc.NewServer()
rpcAPIArr := config.EVMRPC.API
rpcAPIArr := config.JSONRPC.API
apis := rpc.GetRPCAPIs(ctx, clientCtx, tmWsClient, rpcAPIArr)
for _, api := range apis {
if err := rpcServer.RegisterName(api.Namespace, api.Service); err != nil {
ctx.Logger.Error(
"failed to register service in EVM RPC namespace",
"failed to register service in JSON RPC namespace",
"namespace", api.Namespace,
"service", api.Service,
)
@ -41,43 +41,43 @@ func StartEVMRPC(ctx *server.Context, clientCtx client.Context, tmRPCAddr string
r.HandleFunc("/", rpcServer.ServeHTTP).Methods("POST")
handlerWithCors := cors.Default()
if config.EVMRPC.EnableUnsafeCORS {
if config.JSONRPC.EnableUnsafeCORS {
handlerWithCors = cors.AllowAll()
}
httpSrv := &http.Server{
Addr: config.EVMRPC.RPCAddress,
Addr: config.JSONRPC.Address,
Handler: handlerWithCors.Handler(r),
}
httpSrvDone := make(chan struct{}, 1)
errCh := make(chan error)
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 == http.ErrServerClosed {
close(httpSrvDone)
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
}
}()
select {
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
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)
_, port, _ := net.SplitHostPort(config.EVMRPC.RPCAddress)
ctx.Logger.Info("Starting JSON WebSocket server", "address", config.JSONRPC.WsAddress)
_, port, _ := net.SplitHostPort(config.JSONRPC.Address)
// allocate separate WS connection to Tendermint
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()
return httpSrv, httpSrvDone, nil
}

View File

@ -8,6 +8,7 @@ import (
"os"
"path/filepath"
"runtime/pprof"
"strings"
"time"
"github.com/cosmos/cosmos-sdk/codec"
@ -42,48 +43,7 @@ import (
ethdebug "github.com/tharsis/ethermint/ethereum/rpc/namespaces/debug"
"github.com/tharsis/ethermint/server/config"
)
// 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"
srvflags "github.com/tharsis/ethermint/server/flags"
)
// 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
}
withTM, _ := cmd.Flags().GetBool(flagWithTendermint)
withTM, _ := cmd.Flags().GetBool(srvflags.WithTendermint)
if !withTM {
serverCtx.Logger.Info("starting ABCI without Tendermint")
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().Bool(flagWithTendermint, true, "Run abci app embedded in-process with tendermint")
cmd.Flags().String(flagAddress, "tcp://0.0.0.0:26658", "Listen address")
cmd.Flags().String(flagTransport, "socket", "Transport protocol: socket, grpc")
cmd.Flags().String(flagTraceStore, "", "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().IntSlice(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(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().String(flagCPUProfile, "", "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().String(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(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().Uint(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().Bool(srvflags.WithTendermint, true, "Run abci app embedded in-process with tendermint")
cmd.Flags().String(srvflags.Address, "tcp://0.0.0.0:26658", "Listen address")
cmd.Flags().String(srvflags.Transport, "socket", "Transport protocol: socket, grpc")
cmd.Flags().String(srvflags.TraceStore, "", "Enable KVStore tracing to an output file")
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(server.FlagUnsafeSkipUpgrades, []int{}, "Skip a set of upgrade heights to continue the old binary")
cmd.Flags().Uint64(server.FlagHaltHeight, 0, "Block height 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(server.FlagInterBlockCache, true, "Enable inter-block caching")
cmd.Flags().String(srvflags.CPUProfile, "", "Enable CPU profiling and write to the provided file")
cmd.Flags().Bool(server.FlagTrace, false, "Provide full stack traces for errors in ABCI Log")
cmd.Flags().String(server.FlagPruning, storetypes.PruningOptionDefault, "Pruning strategy (default|nothing|everything|custom)")
cmd.Flags().Uint64(server.FlagPruningKeepRecent, 0, "Number of recent heights to keep on disk (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(server.FlagPruningInterval, 0, "Height interval at which pruned heights are removed from disk (ignored if pruning is not 'custom')")
cmd.Flags().Uint(server.FlagInvCheckPeriod, 0, "Assert registered invariants every N 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().String(flagGRPCAddress, serverconfig.DefaultGRPCAddress, "the gRPC server address to listen on")
cmd.Flags().Bool(srvflags.GRPCEnable, true, "Define if the gRPC server should be enabled")
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().String(flagGRPCWebAddress, serverconfig.DefaultGRPCWebAddress, "The gRPC-Web server address to listen on")
cmd.Flags().Bool(srvflags.JSONRPCEnable, true, "Define if the gRPC server should be enabled")
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().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().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().Uint64(FlagStateSyncSnapshotInterval, 0, "State sync snapshot interval")
cmd.Flags().Uint32(FlagStateSyncSnapshotKeepRecent, 2, "State sync snapshot to keep")
cmd.Flags().Uint64(server.FlagStateSyncSnapshotInterval, 0, "State sync snapshot interval")
cmd.Flags().Uint32(server.FlagStateSyncSnapshotKeepRecent, 2, "State sync snapshot to keep")
// add support for all Tendermint-specific command line options
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 {
addr := ctx.Viper.GetString(flagAddress)
transport := ctx.Viper.GetString(flagTransport)
addr := ctx.Viper.GetString(srvflags.Address)
transport := ctx.Viper.GetString(srvflags.Transport)
home := ctx.Viper.GetString(flags.FlagHome)
db, err := openDB(home)
@ -203,7 +164,7 @@ func startStandAlone(ctx *server.Context, appCreator types.AppCreator) error {
return err
}
traceWriterFile := ctx.Viper.GetString(flagTraceStore)
traceWriterFile := ctx.Viper.GetString(srvflags.TraceStore)
traceWriter, err := openTraceWriter(traceWriterFile)
if err != nil {
return err
@ -240,7 +201,7 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, appCreator ty
logger := ctx.Logger
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))
if err != nil {
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)
if err != nil {
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)
if err := config.ValidateBasic(); err != nil {
ctx.Logger.Error("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.")
if strings.Contains(err.Error(), "set min gas price in app.toml or flag or env variable") {
ctx.Logger.Error(
"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)
@ -405,7 +373,7 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, appCreator ty
httpSrv *http.Server
httpSrvDone chan struct{}
)
if config.EVMRPC.Enable {
if config.JSONRPC.Enable {
genDoc, err := genDocProvider()
if err != nil {
return err
@ -415,7 +383,7 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, appCreator ty
tmEndpoint := "/websocket"
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 {
return err
}

View File

@ -43,4 +43,4 @@ ethermintd collect-gentxs
ethermintd validate-genesis
# 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 := ""
tmCfg.RPC.ListenAddress = ""
appCfg.GRPC.Enable = false
appCfg.EVMRPC.Enable = false
appCfg.JSONRPC.Enable = false
if i == 0 {
apiListenAddr, _, err := server.FreeTCPAddr()
@ -244,8 +244,8 @@ func New(t *testing.T, cfg Config) *Network {
jsonRPCListenAddr, _, err := server.FreeTCPAddr()
require.NoError(t, err)
t.Log(jsonRPCListenAddr)
appCfg.EVMRPC.RPCAddress = jsonRPCListenAddr
appCfg.EVMRPC.Enable = true
appCfg.JSONRPC.Address = jsonRPCListenAddr
appCfg.JSONRPC.Enable = true
jsonRPCAPIURL, err := url.Parse(jsonRPCListenAddr)
require.NoError(t, err)

View File

@ -114,14 +114,14 @@ func startInProcess(cfg Config, val *Validator) error {
val.grpc = grpcSrv
}
if val.AppConfig.EVMRPC.Enable {
if val.AppConfig.JSONRPC.Enable {
tmEndpoint := "/websocket"
tmRPCAddr := val.Ctx.Config.RPC.ListenAddress
tmWsClient := ethsrv.ConnectTmWS(tmRPCAddr, tmEndpoint)
val.jsonRPC = jsonrpc.NewServer()
rpcAPIArr := val.AppConfig.EVMRPC.API
rpcAPIArr := val.AppConfig.JSONRPC.API
apis := rpc.GetRPCAPIs(val.Ctx, val.ClientCtx, tmWsClient, rpcAPIArr)
for _, api := range apis {
@ -153,8 +153,8 @@ func startInProcess(cfg Config, val *Validator) error {
})
httpSrv := &http.Server{
Addr: strings.TrimPrefix(val.AppConfig.EVMRPC.RPCAddress, "tcp://"), // FIXME: timeouts
// Addr: val.AppConfig.EVMRPC.RPCAddress, // FIXME: address has too many colons
Addr: strings.TrimPrefix(val.AppConfig.JSONRPC.Address, "tcp://"), // FIXME: timeouts
// Addr: val.AppConfig.JSONRPC.RPCAddress, // FIXME: address has too many colons
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())
}
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.
res, err := k.ApplyMessage(evm, msg, ethCfg, true)
k.ctxStack.RevertAll()
@ -448,7 +449,7 @@ func (k Keeper) EstimateGas(c context.Context, req *types.EthCallRequest) (*type
k.WithContext(ctx)
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.
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
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.
// For more info check https://geth.ethereum.org/docs/dapp/tracing
debug bool
@ -58,7 +61,7 @@ func NewKeeper(
cdc codec.BinaryCodec,
storeKey, transientKey sdk.StoreKey, paramSpace paramtypes.Subspace,
ak types.AccountKeeper, bankKeeper types.BankKeeper, sk types.StakingKeeper,
debug bool,
tracer string, debug bool,
) *Keeper {
// ensure evm module account is set
@ -80,6 +83,7 @@ func NewKeeper(
stakingKeeper: sk,
storeKey: storeKey,
transientKey: transientKey,
tracer: tracer,
debug: debug,
}
}

View File

@ -2,7 +2,6 @@ package keeper
import (
"math/big"
"os"
"time"
"github.com/palantir/stacktrace"
@ -28,7 +27,13 @@ import (
// (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
// 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{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
@ -41,18 +46,20 @@ func (k *Keeper) NewEVM(msg core.Message, config *params.ChainConfig, params typ
}
txCtx := core.NewEVMTxContext(msg)
vmConfig := k.VMConfig(params)
vmConfig := k.VMConfig(msg, params, tracer)
return vm.NewEVM(blockCtx, txCtx, k, config, vmConfig)
}
// 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.
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{
Debug: k.debug,
Tracer: vm.NewJSONLogger(&vm.LogConfig{Debug: k.debug}, os.Stderr), // TODO: consider using the Struct Logger too
NoRecursion: false, // TODO: consider disabling recursion though params
Tracer: types.NewTracer(tracer, msg, cfg, k.Ctx().BlockHeight(), k.debug),
NoRecursion: false, // TODO: consider disabling recursion though params
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
evm := k.NewEVM(msg, ethCfg, params, coinbase)
evm := k.NewEVM(msg, ethCfg, params, coinbase, k.tracer)
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
}
}