diff --git a/evmrpc/historical_debug_trace_test.go b/evmrpc/historical_debug_trace_test.go index f5691517bf..894b4e4660 100644 --- a/evmrpc/historical_debug_trace_test.go +++ b/evmrpc/historical_debug_trace_test.go @@ -4,6 +4,7 @@ import ( "context" "testing" + "github.com/ethereum/go-ethereum/common" sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" "github.com/stretchr/testify/require" ) @@ -91,3 +92,25 @@ func TestGuardHistoricalDebugTraceHeight(t *testing.T) { require.Error(t, err) require.Contains(t, err.Error(), "block number 9 is beyond max lookback of 0") } + +func TestGuardHistoricalDebugTraceByHashUsesTendermintHeight(t *testing.T) { + latestHeight := int64(10) + latestCtx := sdk.Context{}.WithBlockHeight(latestHeight) + tmClient := newHeightTestClient(8, 1, latestHeight) + api := &DebugAPI{ + tmClient: tmClient, + ctxProvider: func(int64) sdk.Context { return latestCtx }, + connectionType: ConnectionTypeHTTP, + maxBlockLookback: 1, + backend: &Backend{ + watermarks: NewWatermarkManager(tmClient, func(int64) sdk.Context { return latestCtx }, nil, nil), + }, + } + + err := api.guardHistoricalDebugTraceByHash(context.Background(), "debug_traceBlockByHash", common.HexToHash(highBlockHashHex)) + require.Error(t, err) + require.Contains(t, err.Error(), "block number 8 is beyond max lookback of 1") + + err = api.guardHistoricalDebugTraceByHash(context.Background(), "debug_traceCall", common.HexToHash("0x1")) + require.NoError(t, err) +} diff --git a/evmrpc/tracers.go b/evmrpc/tracers.go index c3760bddda..2926c96451 100644 --- a/evmrpc/tracers.go +++ b/evmrpc/tracers.go @@ -112,14 +112,14 @@ func (api *DebugAPI) guardHistoricalDebugTraceByNumber(ctx context.Context, endp } func (api *DebugAPI) guardHistoricalDebugTraceByHash(ctx context.Context, endpoint string, hash common.Hash) error { - if api.backend == nil { + if api.backend == nil || api.tmClient == nil { return nil } - block, _, err := api.backend.BlockByHash(ctx, hash) - if err != nil || block == nil { + block, err := blockByHashRespectingWatermarks(ctx, api.tmClient, api.backend.watermarks, hash.Bytes(), 1) + if err != nil || block == nil || block.Block == nil { return nil } - return api.guardHistoricalDebugTraceHeight(ctx, endpoint, int64(block.NumberU64())) //nolint:gosec + return api.guardHistoricalDebugTraceHeight(ctx, endpoint, block.Block.Height) } func (api *DebugAPI) guardHistoricalDebugTraceByNumberOrHash(ctx context.Context, endpoint string, blockNrOrHash rpc.BlockNumberOrHash) error { @@ -646,16 +646,16 @@ func (api *DebugAPI) TraceBlockByHash(ctx context.Context, hash common.Hash, con recordMetricsWithError(ctx, "debug_traceBlockByHash", api.connectionType, startTime, returnErr, recover()) }() - if returnErr = api.guardHistoricalDebugTraceByHash(ctx, "debug_traceBlockByHash", hash); returnErr != nil { - return nil, returnErr - } - ctx, done, err := api.prepareTraceContext(ctx) if err != nil { return nil, err } defer done() + if returnErr = api.guardHistoricalDebugTraceByHash(ctx, "debug_traceBlockByHash", hash); returnErr != nil { + return nil, returnErr + } + if cached, ok := api.tryBlockTraceCacheByHash(ctx, hash, config); ok { return cached, nil } @@ -674,16 +674,16 @@ func (api *DebugAPI) TraceCall(ctx context.Context, args export.TransactionArgs, recordMetricsWithError(ctx, "debug_traceCall", api.connectionType, startTime, returnErr, recover()) }() - if returnErr = api.guardHistoricalDebugTraceByNumberOrHash(ctx, "debug_traceCall", blockNrOrHash); returnErr != nil { - return nil, returnErr - } - ctx, done, err := api.prepareTraceContext(ctx) if err != nil { return nil, err } defer done() + if returnErr = api.guardHistoricalDebugTraceByNumberOrHash(ctx, "debug_traceCall", blockNrOrHash); returnErr != nil { + return nil, returnErr + } + result, returnErr = api.tracersAPI.TraceCall(ctx, args, blockNrOrHash, config) return } diff --git a/evmrpc/tracers_semaphore_test.go b/evmrpc/tracers_semaphore_test.go index ff81f1c5f7..37bf079cd3 100644 --- a/evmrpc/tracers_semaphore_test.go +++ b/evmrpc/tracers_semaphore_test.go @@ -5,6 +5,12 @@ import ( "testing" "time" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/export" + "github.com/ethereum/go-ethereum/rpc" + sdk "github.com/sei-protocol/sei-chain/sei-cosmos/types" + tmbytes "github.com/sei-protocol/sei-chain/sei-tendermint/libs/bytes" + "github.com/sei-protocol/sei-chain/sei-tendermint/rpc/coretypes" "github.com/stretchr/testify/require" ) @@ -82,3 +88,42 @@ func TestAcquireTraceSemaphoreCanceledContextDoesNotConsumeSlot(t *testing.T) { } } } + +type panicHashLookupClient struct { + *heightTestClient +} + +func (c *panicHashLookupClient) BlockByHash(context.Context, tmbytes.HexBytes) (*coretypes.ResultBlock, error) { + panic("hash lookup should not happen before trace context setup") +} + +func TestHashBasedTraceEndpointsAcquireSemaphoreBeforeHashLookup(t *testing.T) { + latestHeight := int64(10) + latestCtx := sdk.Context{}.WithBlockHeight(latestHeight) + tmClient := &panicHashLookupClient{ + heightTestClient: newHeightTestClient(8, 1, latestHeight), + } + watermarks := NewWatermarkManager(tmClient, func(int64) sdk.Context { return latestCtx }, nil, nil) + api := &DebugAPI{ + tmClient: tmClient, + ctxProvider: func(int64) sdk.Context { return latestCtx }, + connectionType: ConnectionTypeHTTP, + traceCallSemaphore: make(chan struct{}, 1), + traceTimeout: time.Second, + backend: &Backend{ + tmClient: tmClient, + watermarks: watermarks, + }, + } + + api.traceCallSemaphore <- struct{}{} + defer func() { <-api.traceCallSemaphore }() + + hash := common.HexToHash(highBlockHashHex) + _, err := api.TraceBlockByHash(context.Background(), hash, nil) + require.ErrorIs(t, err, errTraceConcurrencyLimit) + + blockNrOrHash := rpc.BlockNumberOrHashWithHash(hash, false) + _, err = api.TraceCall(context.Background(), export.TransactionArgs{}, blockNrOrHash, nil) + require.ErrorIs(t, err, errTraceConcurrencyLimit) +}