diff --git a/cmd/rollappd/cmd/root.go b/cmd/rollappd/cmd/root.go index 1e487bbe..5f93f5a7 100644 --- a/cmd/rollappd/cmd/root.go +++ b/cmd/rollappd/cmd/root.go @@ -14,6 +14,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/evmos/evmos/v12/crypto/hd" + berpcconfig "github.com/bcdevtools/block-explorer-rpc-cosmos/be_rpc/config" "github.com/cosmos/cosmos-sdk/client/rpc" "github.com/cosmos/cosmos-sdk/server" servertypes "github.com/cosmos/cosmos-sdk/server/types" @@ -105,6 +106,9 @@ func NewRootCmd() (*cobra.Command, params.EncodingConfig) { chainID := client.GetClientContextFromCmd(cmd).ChainID dymintconf.EnsureRoot(home, dymintconf.DefaultConfig(home, chainID)) + //create Block Explorer Json-RPC toml config file + berpcconfig.EnsureRoot(home, berpcconfig.DefaultBeJsonRpcConfig()) + return nil }, } diff --git a/cmd/rollappd/cmd/start.go b/cmd/rollappd/cmd/start.go index 7e7f5aa8..6053247b 100644 --- a/cmd/rollappd/cmd/start.go +++ b/cmd/rollappd/cmd/start.go @@ -3,6 +3,16 @@ package cmd import ( "context" "fmt" + berpc "github.com/bcdevtools/block-explorer-rpc-cosmos/be_rpc" + berpcbackend "github.com/bcdevtools/block-explorer-rpc-cosmos/be_rpc/backend" + berpccfg "github.com/bcdevtools/block-explorer-rpc-cosmos/be_rpc/config" + berpctypes "github.com/bcdevtools/block-explorer-rpc-cosmos/be_rpc/types" + "github.com/bcdevtools/evm-block-explorer-rpc-cosmos/integrate_be_rpc" + evmberpcbackend "github.com/bcdevtools/evm-block-explorer-rpc-cosmos/integrate_be_rpc/backend/evm" + raeberpcbackend "github.com/dymensionxyz/rollapp-evm/ra_evm_be_rpc/backend" + raebeapi "github.com/dymensionxyz/rollapp-evm/ra_evm_be_rpc/namespaces/rae" + "github.com/ethereum/go-ethereum/rpc" + rpcclient "github.com/tendermint/tendermint/rpc/jsonrpc/client" "net" "net/http" "os" @@ -154,9 +164,15 @@ which accepts a path for the resulting pprof file. return err } + beJsonRpcConfig := berpccfg.DefaultBeJsonRpcConfig() + err = beJsonRpcConfig.GetViperConfig(cmd, serverCtx.Viper.GetString(flags.FlagHome)) + if err != nil { + return err + } + // amino is needed here for backwards compatibility of REST routes err = wrapCPUProfile(serverCtx, func() error { - return startInProcess(serverCtx, clientCtx, dymconfig, appCreator) + return startInProcess(serverCtx, clientCtx, dymconfig, beJsonRpcConfig, appCreator) }) errCode, ok := err.(server.ErrorCode) if !ok { @@ -225,12 +241,14 @@ which accepts a path for the resulting pprof file. 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)") //nolint:lll cmd.Flags().Uint64(srvflags.EVMMaxTxGasWanted, config.DefaultMaxTxGasWanted, "the gas wanted for each eth tx returned in ante handler in check tx mode") //nolint:lll + berpccfg.AddBeJsonRpcFlags(cmd) + dymintconf.AddNodeFlags(cmd) rdklogger.AddLogFlags(cmd) return cmd } -func startInProcess(ctx *server.Context, clientCtx client.Context, nodeConfig *dymintconf.NodeConfig, appCreator types.AppCreator) error { +func startInProcess(ctx *server.Context, clientCtx client.Context, nodeConfig *dymintconf.NodeConfig, beRpcCfg *berpccfg.BeJsonRpcConfig, appCreator types.AppCreator) error { cfg := ctx.Config home := cfg.RootDir @@ -255,6 +273,10 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, nodeConfig *d return err } + if err := beRpcCfg.Validate(); err != nil { + return err + } + app := appCreator(ctx.Logger, db, traceWriter, ctx.Viper) nodeKey, err := p2p.LoadOrGenNodeKey(cfg.NodeKeyFile()) @@ -316,7 +338,7 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, nodeConfig *d // Add the tx service to the gRPC router. We only need to register this // service if API or gRPC is enabled, and avoid doing so in the general // case, because it spawns a new local tendermint RPC client. - if (config.API.Enable || config.GRPC.Enable || config.JSONRPC.Enable) && tmNode != nil { + if (config.API.Enable || config.GRPC.Enable || config.JSONRPC.Enable || beRpcCfg.Enable) && tmNode != nil { clientCtx = clientCtx.WithClient(dymserver.Client()) app.RegisterTxService(clientCtx) @@ -358,7 +380,7 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, nodeConfig *d } } - if config.API.Enable || config.JSONRPC.Enable { + if config.API.Enable || config.JSONRPC.Enable || beRpcCfg.Enable { genDoc, err := genDocProvider() if err != nil { return err @@ -487,6 +509,57 @@ func startInProcess(ctx *server.Context, clientCtx client.Context, nodeConfig *d }() } + if beRpcCfg.Enable { + genDoc, err := genDocProvider() + if err != nil { + return err + } + + raeBeRpcBackend := raeberpcbackend.NewRollAppEvmBackend(ctx, ctx.Logger, clientCtx) + + serverCloseDeferFunc, err := integrate_be_rpc.StartEvmBeJsonRPC( + ctx, + clientCtx, + genDoc.ChainID, + *beRpcCfg, + idxer, + nil, // external services modifier + func(evmberpcbackend.EvmBackendI) { + berpc.RegisterAPINamespace(raebeapi.DymRollAppEvmBlockExplorerNamespace, func(ctx *server.Context, + _ client.Context, + _ *rpcclient.WSClient, + _ map[string]berpctypes.MessageParser, + _ map[string]berpctypes.MessageInvolversExtractor, + _ func(berpcbackend.BackendI) berpcbackend.RequestInterceptor, + _ berpctypes.ExternalServices, + ) []rpc.API { + return []rpc.API{ + { + Namespace: raebeapi.DymRollAppEvmBlockExplorerNamespace, + Version: raebeapi.ApiVersion, + Service: raebeapi.NewRollAppEvmApi(ctx, raeBeRpcBackend), + Public: true, + }, + } + }, false) + }, + func(backend berpcbackend.BackendI, evmBeRpcBackend evmberpcbackend.EvmBackendI) berpcbackend.RequestInterceptor { + return raeberpcbackend.NewRollAppEvmRequestInterceptor( + backend, + raeBeRpcBackend, + evmberpcbackend.NewDefaultRequestInterceptor(backend, evmBeRpcBackend), + ) + }, + cfg.RPC.ListenAddress, + "/websocket", + ) + if err != nil { + return err + } + + defer serverCloseDeferFunc() + } + var rosettaSrv crgserver.Server if config.Rosetta.Enable { offlineMode := config.Rosetta.Offline diff --git a/go.mod b/go.mod index baf317bd..e02ec8f2 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,8 @@ go 1.22.1 require ( cosmossdk.io/errors v1.0.1 + github.com/bcdevtools/block-explorer-rpc-cosmos v1.0.1 + github.com/bcdevtools/evm-block-explorer-rpc-cosmos v1.0.1 github.com/cosmos/cosmos-sdk v0.46.15 github.com/cosmos/ibc-go/v6 v6.2.1 github.com/dymensionxyz/dymension-rdk v1.2.0-beta @@ -11,6 +13,7 @@ require ( github.com/ethereum/go-ethereum v1.12.0 github.com/evmos/evmos/v12 v12.1.6 github.com/gorilla/mux v1.8.1 + github.com/pkg/errors v0.9.1 github.com/rakyll/statik v0.1.7 github.com/spf13/cast v1.5.1 github.com/spf13/cobra v1.8.0 @@ -228,7 +231,6 @@ require ( github.com/pelletier/go-toml/v2 v2.0.7 // indirect github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect github.com/pierrec/xxHash v0.1.5 // indirect - github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/polydawn/refmt v0.89.0 // indirect github.com/prometheus/client_golang v1.18.0 // indirect diff --git a/go.sum b/go.sum index ec6eb3e9..5fb767bd 100644 --- a/go.sum +++ b/go.sum @@ -293,6 +293,10 @@ github.com/aws/aws-sdk-go-v2/service/route53 v1.1.1/go.mod h1:rLiOUrPLW/Er5kRcQ7 github.com/aws/aws-sdk-go-v2/service/sso v1.1.1/go.mod h1:SuZJxklHxLAXgLTc1iFXbEWkXs7QRTQpCLGaKIprQW0= github.com/aws/aws-sdk-go-v2/service/sts v1.1.1/go.mod h1:Wi0EBZwiz/K44YliU0EKxqTCJGUfYTWXrrBwkq736bM= github.com/aws/smithy-go v1.1.0/go.mod h1:EzMw8dbp/YJL4A5/sbhGddag+NPT7q084agLbB9LgIw= +github.com/bcdevtools/block-explorer-rpc-cosmos v1.0.1 h1:/mkYfjhJSQr71mJUFbOoyRvykSUj6TgwB9m9sKtqtOI= +github.com/bcdevtools/block-explorer-rpc-cosmos v1.0.1/go.mod h1:AWXHI5ICXK4wB+A59dNddzq5Xdc1wtQDRiIXfMw8cwc= +github.com/bcdevtools/evm-block-explorer-rpc-cosmos v1.0.1 h1:O0sWD/Hky5wUWsWMhmkifDUj2Qhjyu3pZH1/C7n0Kqg= +github.com/bcdevtools/evm-block-explorer-rpc-cosmos v1.0.1/go.mod h1:IN6s8jCudUJh8E8Mm3iNqITL05Wcj4+PJvfRUUQnipI= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= diff --git a/ra_evm_be_rpc/backend/backend.go b/ra_evm_be_rpc/backend/backend.go new file mode 100644 index 00000000..67c69343 --- /dev/null +++ b/ra_evm_be_rpc/backend/backend.go @@ -0,0 +1,50 @@ +package backend + +import ( + "context" + "github.com/bcdevtools/block-explorer-rpc-cosmos/be_rpc/config" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/server" + hubgentypes "github.com/dymensionxyz/dymension-rdk/x/hub-genesis/types" + sequencerstypes "github.com/dymensionxyz/dymension-rdk/x/sequencers/types" + raeberpctypes "github.com/dymensionxyz/rollapp-evm/ra_evm_be_rpc/types" + "github.com/tendermint/tendermint/libs/log" +) + +type RollAppEvmBackendI interface { + // Misc + + GetSequencersModuleParams() (*sequencerstypes.Params, error) + GetHubGenesisModuleParams() (*hubgentypes.Params, error) +} + +var _ RollAppEvmBackendI = (*RollAppEvmBackend)(nil) + +// RollAppEvmBackend implements the RollAppEvmBackendI interface +type RollAppEvmBackend struct { + ctx context.Context + clientCtx client.Context + queryClient *raeberpctypes.QueryClient // gRPC query client + logger log.Logger + cfg config.BeJsonRpcConfig +} + +// NewRollAppEvmBackend creates a new RollAppEvmBackend instance for RollApp EVM Block Explorer +func NewRollAppEvmBackend( + ctx *server.Context, + logger log.Logger, + clientCtx client.Context, +) *RollAppEvmBackend { + appConf, err := config.GetConfig(ctx.Viper) + if err != nil { + panic(err) + } + + return &RollAppEvmBackend{ + ctx: context.Background(), + clientCtx: clientCtx, + queryClient: raeberpctypes.NewQueryClient(clientCtx), + logger: logger.With("module", "rae_be_rpc"), + cfg: appConf, + } +} diff --git a/ra_evm_be_rpc/backend/hub_genesis.go b/ra_evm_be_rpc/backend/hub_genesis.go new file mode 100644 index 00000000..8b68aad6 --- /dev/null +++ b/ra_evm_be_rpc/backend/hub_genesis.go @@ -0,0 +1,13 @@ +package backend + +import ( + hubgentypes "github.com/dymensionxyz/dymension-rdk/x/hub-genesis/types" +) + +func (m *RollAppEvmBackend) GetHubGenesisModuleParams() (*hubgentypes.Params, error) { + res, err := m.queryClient.HubGenesisQueryClient.Params(m.ctx, &hubgentypes.QueryParamsRequest{}) + if err != nil { + return nil, err + } + return &res.Params, nil +} diff --git a/ra_evm_be_rpc/backend/rollapp_evm_interceptor.go b/ra_evm_be_rpc/backend/rollapp_evm_interceptor.go new file mode 100644 index 00000000..a1224461 --- /dev/null +++ b/ra_evm_be_rpc/backend/rollapp_evm_interceptor.go @@ -0,0 +1,82 @@ +package backend + +import ( + berpcbackend "github.com/bcdevtools/block-explorer-rpc-cosmos/be_rpc/backend" + berpctypes "github.com/bcdevtools/block-explorer-rpc-cosmos/be_rpc/types" + "github.com/pkg/errors" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +var _ berpcbackend.RequestInterceptor = (*RollAppEvmRequestInterceptor)(nil) + +type RollAppEvmRequestInterceptor struct { + beRpcBackend berpcbackend.BackendI + backend RollAppEvmBackendI + defaultInterceptor berpcbackend.RequestInterceptor +} + +func NewRollAppEvmRequestInterceptor( + beRpcBackend berpcbackend.BackendI, + backend RollAppEvmBackendI, + defaultInterceptor berpcbackend.RequestInterceptor, +) *RollAppEvmRequestInterceptor { + return &RollAppEvmRequestInterceptor{ + beRpcBackend: beRpcBackend, + backend: backend, + defaultInterceptor: defaultInterceptor, + } +} + +func (m *RollAppEvmRequestInterceptor) GetTransactionByHash(hashStr string) (intercepted bool, response berpctypes.GenericBackendResponse, err error) { + // handled completely by the default interceptor + return m.defaultInterceptor.GetTransactionByHash(hashStr) +} + +func (m *RollAppEvmRequestInterceptor) GetDenomsInformation() (intercepted, append bool, denoms map[string]string, err error) { + // handled completely by the default interceptor + return m.defaultInterceptor.GetDenomsInformation() +} + +func (m *RollAppEvmRequestInterceptor) GetModuleParams(moduleName string) (intercepted bool, res berpctypes.GenericBackendResponse, err error) { + var params any + + switch moduleName { + case "sequencers": + sequencersParams, errFetch := m.backend.GetSequencersModuleParams() + if errFetch != nil { + err = errors.Wrap(errFetch, "failed to get sequencers params") + } else { + params = *sequencersParams + } + break + case "hub-genesis": + hubGenesisParams, errFetch := m.backend.GetHubGenesisModuleParams() + if errFetch != nil { + err = errors.Wrap(errFetch, "failed to get hub genesis params") + } else { + params = *hubGenesisParams + } + break + default: + return m.defaultInterceptor.GetModuleParams(moduleName) + } + + if err != nil { + return + } + + res, err = berpctypes.NewGenericBackendResponseFrom(params) + if err != nil { + err = status.Error(codes.Internal, errors.Wrap(err, "module params").Error()) + return + } + + intercepted = true + return +} + +func (m *RollAppEvmRequestInterceptor) GetAccount(accountAddressStr string) (intercepted, append bool, response berpctypes.GenericBackendResponse, err error) { + // handled completely by the default interceptor + return m.defaultInterceptor.GetAccount(accountAddressStr) +} diff --git a/ra_evm_be_rpc/backend/sequencers.go b/ra_evm_be_rpc/backend/sequencers.go new file mode 100644 index 00000000..6ee34883 --- /dev/null +++ b/ra_evm_be_rpc/backend/sequencers.go @@ -0,0 +1,13 @@ +package backend + +import ( + sequencerstypes "github.com/dymensionxyz/dymension-rdk/x/sequencers/types" +) + +func (m *RollAppEvmBackend) GetSequencersModuleParams() (*sequencerstypes.Params, error) { + res, err := m.queryClient.SequencersQueryClient.Params(m.ctx, &sequencerstypes.QueryParamsRequest{}) + if err != nil { + return nil, err + } + return &res.Params, nil +} diff --git a/ra_evm_be_rpc/namespaces/rae/api.go b/ra_evm_be_rpc/namespaces/rae/api.go new file mode 100644 index 00000000..ce319517 --- /dev/null +++ b/ra_evm_be_rpc/namespaces/rae/api.go @@ -0,0 +1,40 @@ +package rae + +import ( + "fmt" + "github.com/cosmos/cosmos-sdk/server" + raeberpcbackend "github.com/dymensionxyz/rollapp-evm/ra_evm_be_rpc/backend" + "github.com/tendermint/tendermint/libs/log" +) + +// RPC namespaces and API version +const ( + DymRollAppEvmBlockExplorerNamespace = "rae" + + ApiVersion = "1.0" +) + +// API is the RollApp EVM Block Explorer JSON-RPC. +// Developers can create custom API for the chain. +type API struct { + ctx *server.Context + logger log.Logger + backend raeberpcbackend.RollAppEvmBackendI +} + +// NewRollAppEvmApi creates an instance of the RollApp EVM Block Explorer API. +func NewRollAppEvmApi( + ctx *server.Context, + backend raeberpcbackend.RollAppEvmBackendI, +) *API { + return &API{ + ctx: ctx, + logger: ctx.Logger.With("api", "rae"), + backend: backend, + } +} + +func (api *API) Echo(text string) string { + api.logger.Debug("rae_echo") + return fmt.Sprintf("hello \"%s\" from RollApp EVM Block Explorer API", text) +} diff --git a/ra_evm_be_rpc/types/query_client.go b/ra_evm_be_rpc/types/query_client.go new file mode 100644 index 00000000..cc4eee58 --- /dev/null +++ b/ra_evm_be_rpc/types/query_client.go @@ -0,0 +1,36 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/types/tx" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + epochstypes "github.com/dymensionxyz/dymension-rdk/x/epochs/types" + hubgentypes "github.com/dymensionxyz/dymension-rdk/x/hub-genesis/types" + sequencerstypes "github.com/dymensionxyz/dymension-rdk/x/sequencers/types" + evmtypes "github.com/evmos/evmos/v12/x/evm/types" + + "github.com/cosmos/cosmos-sdk/client" +) + +// QueryClient defines a gRPC Client used for: +// - Transaction simulation +type QueryClient struct { + tx.ServiceClient + + BankQueryClient banktypes.QueryClient + EvmQueryClient evmtypes.QueryClient + SequencersQueryClient sequencerstypes.QueryClient + EpochQueryClient epochstypes.QueryClient + HubGenesisQueryClient hubgentypes.QueryClient +} + +// NewQueryClient creates a new gRPC query client +func NewQueryClient(clientCtx client.Context) *QueryClient { + return &QueryClient{ + ServiceClient: tx.NewServiceClient(clientCtx), + BankQueryClient: banktypes.NewQueryClient(clientCtx), + EvmQueryClient: evmtypes.NewQueryClient(clientCtx), + SequencersQueryClient: sequencerstypes.NewQueryClient(clientCtx), + EpochQueryClient: epochstypes.NewQueryClient(clientCtx), + HubGenesisQueryClient: hubgentypes.NewQueryClient(clientCtx), + } +}