diff --git a/core/state_processor.go b/core/state_processor.go index 6706ce1f6e0..e30a554f39f 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -126,3 +126,22 @@ func ApplyTransaction(config *chain.Config, blockHashFunc func(n uint64) (common return applyTransaction(config, engine, gp, ibs, stateWriter, header, txn, gasUsed, usedBlobGas, vmenv, cfg) } + +func CreateEVM(config *chain.Config, blockHashFunc func(n uint64) (common.Hash, error), engine consensus.EngineReader, author *common.Address, ibs *state.IntraBlockState, header *types.Header, cfg vm.Config) *vm.EVM { + // Create a new context to be used in the EVM environment + + // Add addresses to access list if applicable + // about the transaction and calling mechanisms. + cfg.SkipAnalysis = SkipAnalysis(config, header.Number.Uint64()) + + blockContext := NewEVMBlockContext(header, blockHashFunc, engine, author, config) + return vm.NewEVM(blockContext, evmtypes.TxContext{}, ibs, config, cfg) +} + +func ApplyTransactionWithEVM(config *chain.Config, engine consensus.EngineReader, gp *GasPool, + ibs *state.IntraBlockState, + stateWriter state.StateWriter, header *types.Header, txn types.Transaction, usedGas, usedBlobGas *uint64, + cfg vm.Config, vmenv *vm.EVM, +) (*types.Receipt, []byte, error) { + return applyTransaction(config, engine, gp, ibs, stateWriter, header, txn, usedGas, usedBlobGas, vmenv, cfg) +} diff --git a/core/vm/evm.go b/core/vm/evm.go index 57ad0fe34f1..dad08956cd6 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -179,6 +179,10 @@ func (evm *EVM) Interpreter() Interpreter { } func (evm *EVM) call(typ OpCode, caller ContractRef, addr common.Address, input []byte, gas uint64, value *uint256.Int, bailout bool) (ret []byte, leftOverGas uint64, err error) { + if evm.abort.Load() { + return ret, leftOverGas, nil + } + depth := evm.interpreter.Depth() p, isPrecompile := evm.precompile(addr) diff --git a/execution/stages/mock/mock_sentry.go b/execution/stages/mock/mock_sentry.go index 451050a22fc..8f7d8fdbf96 100644 --- a/execution/stages/mock/mock_sentry.go +++ b/execution/stages/mock/mock_sentry.go @@ -314,7 +314,7 @@ func MockWithEverything(tb testing.TB, gspec *types.Genesis, key *ecdsa.PrivateK PeerId: gointerfaces.ConvertHashToH512([64]byte{0x12, 0x34, 0x50}), // "12345" BlockSnapshots: allSnapshots, BlockReader: br, - ReceiptsReader: receipts.NewGenerator(br, engine), + ReceiptsReader: receipts.NewGenerator(br, engine, 5*time.Second), HistoryV3: true, cfg: cfg, } diff --git a/p2p/sentry/sentry_multi_client/sentry_multi_client.go b/p2p/sentry/sentry_multi_client/sentry_multi_client.go index 74429fb7520..12b4a7c4a36 100644 --- a/p2p/sentry/sentry_multi_client/sentry_multi_client.go +++ b/p2p/sentry/sentry_multi_client/sentry_multi_client.go @@ -224,7 +224,7 @@ func NewMultiClient( disableBlockDownload: disableBlockDownload, logger: logger, getReceiptsActiveGoroutineNumber: semaphore.NewWeighted(1), - ethApiWrapper: receipts.NewGenerator(blockReader, engine), + ethApiWrapper: receipts.NewGenerator(blockReader, engine, 5*time.Minute), } return cs, nil diff --git a/rpc/jsonrpc/eth_api.go b/rpc/jsonrpc/eth_api.go index 7fce8a9a422..a57a1f85ff8 100644 --- a/rpc/jsonrpc/eth_api.go +++ b/rpc/jsonrpc/eth_api.go @@ -170,7 +170,7 @@ func NewBaseApi(f *rpchelper.Filters, stateCache kvcache.Cache, blockReader serv _txNumReader: blockReader.TxnumReader(context.Background()), evmCallTimeout: evmCallTimeout, _engine: engine, - receiptsGenerator: receipts.NewGenerator(blockReader, engine), + receiptsGenerator: receipts.NewGenerator(blockReader, engine, evmCallTimeout), borReceiptGenerator: receipts.NewBorGenerator(blockReader, engine), dirs: dirs, useBridgeReader: bridgeReader != nil && !reflect.ValueOf(bridgeReader).IsNil(), // needed for interface nil caveat diff --git a/rpc/jsonrpc/receipts/handler_test.go b/rpc/jsonrpc/receipts/handler_test.go index 03799795e15..ded9875c76b 100644 --- a/rpc/jsonrpc/receipts/handler_test.go +++ b/rpc/jsonrpc/receipts/handler_test.go @@ -23,6 +23,7 @@ import ( "math" "math/big" "testing" + "time" "github.com/holiman/uint256" "github.com/stretchr/testify/require" @@ -297,7 +298,7 @@ func TestGetBlockReceipts(t *testing.T) { } // Assemble the test environment m := mockWithGenerator(t, 4, generator) - receiptsGetter := receipts.NewGenerator(m.BlockReader, m.Engine) + receiptsGetter := receipts.NewGenerator(m.BlockReader, m.Engine, time.Minute) // Collect the hashes to request, and the response to expect var ( hashes []common.Hash diff --git a/rpc/jsonrpc/receipts/receipts_generator.go b/rpc/jsonrpc/receipts/receipts_generator.go index 2c54b5ccb8a..3b506024fbd 100644 --- a/rpc/jsonrpc/receipts/receipts_generator.go +++ b/rpc/jsonrpc/receipts/receipts_generator.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "sync" + "time" "github.com/erigontech/erigon-db/rawdb" "github.com/erigontech/erigon-db/rawdb/rawtemporaldb" @@ -39,6 +40,7 @@ type Generator struct { receiptsCacheTrace bool receiptCacheTrace bool + evmTimeout time.Duration blockReader services.FullBlockReader txNumReader rawdbv3.TxNumsReader @@ -60,7 +62,7 @@ var ( receiptsCacheTrace = dbg.EnvBool("R_LRU_TRACE", false) ) -func NewGenerator(blockReader services.FullBlockReader, engine consensus.EngineReader) *Generator { +func NewGenerator(blockReader services.FullBlockReader, engine consensus.EngineReader, evmTimeout time.Duration) *Generator { receiptsCache, err := lru.New[common.Hash, types.Receipts](receiptsCacheLimit) //TODO: is handling both of them a good idea though...? if err != nil { panic(err) @@ -81,6 +83,7 @@ func NewGenerator(blockReader services.FullBlockReader, engine consensus.EngineR receiptsCacheTrace: receiptsCacheTrace, receiptCacheTrace: receiptsCacheTrace, receiptCache: receiptCache, + evmTimeout: evmTimeout, blockExecMutex: &loaderMutex[common.Hash]{}, txnExecMutex: &loaderMutex[common.Hash]{}, @@ -216,6 +219,13 @@ func (g *Generator) GetReceipt(ctx context.Context, cfg *chain.Config, tx kv.Tem return nil, err } + ctx, cancel := context.WithTimeout(ctx, g.evmTimeout) + defer cancel() + go func() { + <-ctx.Done() + evm.Cancel() + }() + status, gasUsed, err := aa.ExecuteAATransaction(aaTxn, paymasterContext, validationGasUsed, genEnv.gp, evm, header, genEnv.ibs) if err != nil { return nil, err @@ -224,7 +234,15 @@ func (g *Generator) GetReceipt(ctx context.Context, cfg *chain.Config, tx kv.Tem logs := genEnv.ibs.GetLogs(genEnv.ibs.TxnIndex(), txn.Hash(), header.Number.Uint64(), header.Hash()) receipt = aa.CreateAAReceipt(txn.Hash(), status, gasUsed, header.GasUsed, header.Number.Uint64(), uint64(genEnv.ibs.TxnIndex()), logs) } else { - receipt, _, err = core.ApplyTransaction(cfg, core.GetHashFn(genEnv.header, genEnv.getHeader), g.engine, nil, genEnv.gp, genEnv.ibs, genEnv.noopWriter, genEnv.header, txn, genEnv.gasUsed, genEnv.usedBlobGas, vm.Config{}) + evm := core.CreateEVM(cfg, core.GetHashFn(genEnv.header, genEnv.getHeader), g.engine, nil, genEnv.ibs, genEnv.header, vm.Config{}) + ctx, cancel := context.WithTimeout(ctx, g.evmTimeout) + defer cancel() + go func() { + <-ctx.Done() + evm.Cancel() + }() + + receipt, _, err = core.ApplyTransactionWithEVM(cfg, g.engine, genEnv.gp, genEnv.ibs, genEnv.noopWriter, genEnv.header, txn, genEnv.gasUsed, genEnv.usedBlobGas, vm.Config{}, evm) if err != nil { return nil, fmt.Errorf("ReceiptGen.GetReceipt: bn=%d, txnIdx=%d, %w", blockNum, index, err) } @@ -288,9 +306,28 @@ func (g *Generator) GetReceipts(ctx context.Context, cfg *chain.Config, tx kv.Te //genEnv.ibs.SetTrace(true) blockNum := block.NumberU64() + vmCfg := vm.Config{ + JumpDestCache: vm.NewJumpDestCache(16), + } + + evm := core.CreateEVM(cfg, core.GetHashFn(genEnv.header, genEnv.getHeader), g.engine, nil, genEnv.ibs, genEnv.header, vm.Config{}) + ctx, cancel := context.WithTimeout(ctx, g.evmTimeout) + defer cancel() + go func() { + <-ctx.Done() + evm.Cancel() + }() + for i, txn := range block.Transactions() { + select { + case <-ctx.Done(): + return nil, ctx.Err() + default: + } + + evm = core.CreateEVM(cfg, core.GetHashFn(genEnv.header, genEnv.getHeader), g.engine, nil, genEnv.ibs, genEnv.header, vmCfg) genEnv.ibs.SetTxContext(blockNum, i) - receipt, _, err := core.ApplyTransaction(cfg, core.GetHashFn(genEnv.header, genEnv.getHeader), g.engine, nil, genEnv.gp, genEnv.ibs, genEnv.noopWriter, genEnv.header, txn, genEnv.gasUsed, genEnv.usedBlobGas, vm.Config{}) + receipt, _, err := core.ApplyTransactionWithEVM(cfg, g.engine, genEnv.gp, genEnv.ibs, genEnv.noopWriter, genEnv.header, txn, genEnv.gasUsed, genEnv.usedBlobGas, vmCfg, evm) if err != nil { return nil, fmt.Errorf("ReceiptGen.GetReceipts: bn=%d, txnIdx=%d, %w", block.NumberU64(), i, err) } diff --git a/turbo/transactions/tracing.go b/turbo/transactions/tracing.go index c58b0496649..929ce52175d 100644 --- a/turbo/transactions/tracing.go +++ b/turbo/transactions/tracing.go @@ -177,9 +177,11 @@ func AssembleTracer( return tracer, false, cancel, nil case config == nil: - return logger.NewJsonStreamLogger(nil, ctx, stream).Tracer(), true, func() {}, nil + ctx, cancel := context.WithTimeout(ctx, callTimeout) + return logger.NewJsonStreamLogger(nil, ctx, stream).Tracer(), true, cancel, nil default: - return logger.NewJsonStreamLogger(config.LogConfig, ctx, stream).Tracer(), true, func() {}, nil + ctx, cancel := context.WithTimeout(ctx, callTimeout) + return logger.NewJsonStreamLogger(config.LogConfig, ctx, stream).Tracer(), true, cancel, nil } } diff --git a/txnprovider/shutter/block_building_integration_test.go b/txnprovider/shutter/block_building_integration_test.go index 871b24635d4..40529d56ab3 100644 --- a/txnprovider/shutter/block_building_integration_test.go +++ b/txnprovider/shutter/block_building_integration_test.go @@ -21,6 +21,7 @@ import ( "crypto/ecdsa" "errors" "fmt" + "github.com/erigontech/erigon/rpc/rpccfg" "math/big" "path" "runtime" @@ -261,6 +262,7 @@ func initBlockBuildingUniverse(ctx context.Context, t *testing.T) blockBuildingU AuthRpcPort: engineApiPort, JWTSecretPath: path.Join(dataDir, "jwt.hex"), ReturnDataLimit: 100_000, + EvmCallTimeout: rpccfg.DefaultEvmCallTimeout, } nodeKeyConfig := p2p.NodeKeyConfig{}