diff --git a/evmrpc/README.md b/evmrpc/README.md new file mode 100644 index 000000000..06cfd436b --- /dev/null +++ b/evmrpc/README.md @@ -0,0 +1,24 @@ +# Sei's EVM RPC + +Sei supports the standard [Ethereum JSON-RPC API](https://ethereum.org/en/developers/docs/apis/json-rpc/) endpoints. On top of that, Sei supports some additional custom endpoints. + +## Sei_ endpoints + +### Endpoints for Synthetic txs +The motivation for these endpoints is to expose CW20 and CW721 events on the EVM side through synthetic receipts and logs. This is useful for indexing pointer contracts. + - `sei_getFilterLogs` + - same as `eth_getFilterLogs` but includes synthetic logs + - `sei_getLogs` + - same as `eth_getLogs` but includes synthetic logs + - `sei_getBlockByNumber` and `sei_getBlockByHash` + - same as `eth_getBlockByNumber` and `eth_getBlockByHash` but includes synthetic txs + - NOTE: for synthetic txs, `eth_getTransactionReceipt` can be used to get the receipt data for a synthetic tx hash. + +### Endpoints for excluding tracing failures +The motivation for these endpoints is to exclude tracing failures from the EVM side. Due to how our mempool works and our lack of tx simulation, we cannot rely on txs to pass all pre-state checks. Therefore, in the eth_ endpoints, we may see txs that fail tracing with errors like "nonce too low", "nonce too high", "insufficient funds", or other types of panic failures. These transactions are not executed, yet are still included in the block. These endpoints are useful for filtering out these txs. +- `sei_traceBlockByNumberExcludeTraceFail` + - same as `debug_traceBlockByNumber` but excludes panic txs +- `sei_getTransactionReceiptExcludeTraceFail` + - same as `eth_getTransactionReceipt` but excludes panic txs +- `sei_getBlockByNumberExcludeTraceFail` and `sei_getBlockByHashExcludeTraceFail` + - same as `eth_getBlockByNumber` and `eth_getBlockByHash` but excludes panic txs diff --git a/evmrpc/block.go b/evmrpc/block.go index 5af6cfe82..431ca80cf 100644 --- a/evmrpc/block.go +++ b/evmrpc/block.go @@ -80,7 +80,7 @@ func NewSeiBlockAPI( } } -func (a *SeiBlockAPI) GetBlockByNumberExcludePanicTx(ctx context.Context, number rpc.BlockNumber, fullTx bool) (result map[string]interface{}, returnErr error) { +func (a *SeiBlockAPI) GetBlockByNumberExcludeTraceFail(ctx context.Context, number rpc.BlockNumber, fullTx bool) (result map[string]interface{}, returnErr error) { return a.getBlockByNumber(ctx, number, fullTx, a.isPanicTx) } @@ -109,18 +109,18 @@ func (a *BlockAPI) GetBlockTransactionCountByHash(ctx context.Context, blockHash } func (a *BlockAPI) GetBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool) (result map[string]interface{}, returnErr error) { - return a.getBlockByHash(ctx, blockHash, fullTx, true, nil) + return a.getBlockByHash(ctx, blockHash, fullTx, nil) } func (a *SeiBlockAPI) GetBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool) (result map[string]interface{}, returnErr error) { - return a.getBlockByHash(ctx, blockHash, fullTx, true, nil) + return a.getBlockByHash(ctx, blockHash, fullTx, nil) } -func (a *SeiBlockAPI) GetBlockByHashExcludePanicTx(ctx context.Context, blockHash common.Hash, fullTx bool) (result map[string]interface{}, returnErr error) { - return a.getBlockByHash(ctx, blockHash, fullTx, false, a.isPanicTx) +func (a *SeiBlockAPI) GetBlockByHashExcludeTraceFail(ctx context.Context, blockHash common.Hash, fullTx bool) (result map[string]interface{}, returnErr error) { + return a.getBlockByHash(ctx, blockHash, fullTx, a.isPanicTx) } -func (a *BlockAPI) getBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool, includePanicTx bool, isPanicTx func(ctx context.Context, hash common.Hash) (bool, error)) (result map[string]interface{}, returnErr error) { +func (a *BlockAPI) getBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool, isPanicTx func(ctx context.Context, hash common.Hash) (bool, error)) (result map[string]interface{}, returnErr error) { startTime := time.Now() defer recordMetrics(fmt.Sprintf("%s_getBlockByHash", a.namespace), a.connectionType, startTime, returnErr == nil) block, err := blockByHashWithRetry(ctx, a.tmClient, blockHash[:], 1) diff --git a/evmrpc/block_test.go b/evmrpc/block_test.go index f92bf7c11..21dfae774 100644 --- a/evmrpc/block_test.go +++ b/evmrpc/block_test.go @@ -34,8 +34,8 @@ func TestGetSeiBlockByHash(t *testing.T) { verifyBlockResult(t, resObj) } -func TestGetSeiBlockByNumberExcludePanicTx(t *testing.T) { - resObj := sendSeiRequestGood(t, "getBlockByNumberExcludePanicTx", "0x67", true) +func TestGetSeiBlockByNumberExcludeTraceFail(t *testing.T) { + resObj := sendSeiRequestGood(t, "getBlockByNumberExcludeTraceFail", "0x67", true) // first tx is not a panic tx, second tx is a panic tx expectedNumTxs := 1 require.Equal(t, expectedNumTxs, len(resObj["result"].(map[string]interface{})["transactions"].([]interface{}))) diff --git a/evmrpc/tracers.go b/evmrpc/tracers.go index 46bd49ac7..cb4ea0a89 100644 --- a/evmrpc/tracers.go +++ b/evmrpc/tracers.go @@ -73,15 +73,14 @@ func (api *DebugAPI) TraceTransaction(ctx context.Context, hash common.Hash, con return } -func (api *SeiDebugAPI) TraceBlockByNumber(ctx context.Context, number rpc.BlockNumber, config *tracers.TraceConfig) (result interface{}, returnErr error) { +func (api *SeiDebugAPI) TraceBlockByNumberExcludeTraceFail(ctx context.Context, number rpc.BlockNumber, config *tracers.TraceConfig) (result interface{}, returnErr error) { startTime := time.Now() - defer recordMetrics("debug_traceBlockByNumber", api.connectionType, startTime, returnErr == nil) + defer recordMetrics("sei_traceBlockByNumberExcludeTraceFail", api.connectionType, startTime, returnErr == nil) result, returnErr = api.tracersAPI.TraceBlockByNumber(ctx, number, config) traces, ok := result.([]*tracers.TxTraceResult) if !ok { return nil, fmt.Errorf("unexpected type: %T", result) } - // iterate through and look for error "tracing failed" finalTraces := make([]*tracers.TxTraceResult, 0) for _, trace := range traces { if len(trace.Error) > 0 { diff --git a/evmrpc/tracers_test.go b/evmrpc/tracers_test.go index 1b8fcbb7e..ff86fb914 100644 --- a/evmrpc/tracers_test.go +++ b/evmrpc/tracers_test.go @@ -87,10 +87,10 @@ func TestTraceCall(t *testing.T) { require.Equal(t, false, result["failed"]) } -func TestTraceTransactionPanic(t *testing.T) { +func TestTraceBlockByNumberExcludeTraceFail(t *testing.T) { args := map[string]interface{}{} args["tracer"] = "callTracer" - seiResObj := sendRequestGoodWithNamespace(t, "sei", "traceBlockByNumber", "0x67", args) + seiResObj := sendRequestGoodWithNamespace(t, "sei", "traceBlockByNumberExcludeTraceFail", "0x67", args) result := seiResObj["result"].([]interface{}) // sei_traceBlockByNumber returns 1 trace, and removes the panic tx require.Equal(t, 1, len(result)) diff --git a/evmrpc/tx.go b/evmrpc/tx.go index fa4fb6f4c..30fee1afe 100644 --- a/evmrpc/tx.go +++ b/evmrpc/tx.go @@ -59,7 +59,7 @@ func NewSeiTransactionAPI( return &SeiTransactionAPI{TransactionAPI: NewTransactionAPI(tmClient, k, ctxProvider, txConfig, homeDir, connectionType), isPanicTx: isPanicTx} } -func (t *SeiTransactionAPI) GetTransactionReceipt(ctx context.Context, hash common.Hash) (result map[string]interface{}, returnErr error) { +func (t *SeiTransactionAPI) GetTransactionReceiptExcludeTraceFail(ctx context.Context, hash common.Hash) (result map[string]interface{}, returnErr error) { return getTransactionReceipt(ctx, t.TransactionAPI, hash, true, t.isPanicTx) } diff --git a/evmrpc/tx_test.go b/evmrpc/tx_test.go index 300b501f7..d30d60be3 100644 --- a/evmrpc/tx_test.go +++ b/evmrpc/tx_test.go @@ -290,8 +290,8 @@ func TestGetTransactionReceiptFailedTx(t *testing.T) { require.Nil(t, resObj["contractAddress"]) } -func TestGetTransactionReceiptExcludePanicTx(t *testing.T) { - body := fmt.Sprintf("{\"jsonrpc\": \"2.0\",\"method\": \"%s_getTransactionReceipt\",\"params\":[\"%s\"],\"id\":\"test\"}", "sei", TestPanicTxHash) +func TestGetTransactionReceiptExcludeTraceFail(t *testing.T) { + body := fmt.Sprintf("{\"jsonrpc\": \"2.0\",\"method\": \"%s_getTransactionReceiptExcludeTraceFail\",\"params\":[\"%s\"],\"id\":\"test\"}", "sei", TestPanicTxHash) req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://%s:%d", TestAddr, TestPort), strings.NewReader(body)) require.Nil(t, err) req.Header.Set("Content-Type", "application/json")