diff --git a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs index 18c04dc057f..e1bb762bceb 100644 --- a/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs +++ b/src/Nethermind/Nethermind.Evm/CodeAnalysis/CodeInfo.cs @@ -29,6 +29,7 @@ public CodeInfo(ReadOnlyMemory code) } public bool IsPrecompile => Precompile is not null; + public bool IsEmpty => ReferenceEquals(_analyzer, _emptyAnalyzer) && !IsPrecompile; public CodeInfo(IPrecompile precompile) { diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index 1fcd07bdc3b..d638965d2cf 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -141,6 +141,7 @@ internal readonly ref struct CallResult public static CallResult StackUnderflowException => new(EvmExceptionType.StackUnderflow); // TODO: use these to avoid CALL POP attacks public static CallResult InvalidCodeException => new(EvmExceptionType.InvalidCode); public static CallResult Empty => new(default, null); + public static object BoxedEmpty { get; } = new object(); public CallResult(EvmState stateToExecute) { @@ -1869,6 +1870,11 @@ private CallResult ExecuteCode( if (typeof(TLogger) == typeof(IsTracing)) { - _logger.Trace($"caller {caller}"); - _logger.Trace($"code source {codeSource}"); - _logger.Trace($"target {target}"); - _logger.Trace($"value {callValue}"); - _logger.Trace($"transfer value {transferValue}"); + TraceCallDetails(codeSource, ref callValue, ref transferValue, caller, target); } long gasExtra = 0L; @@ -2256,11 +2258,19 @@ private EvmExceptionType InstructionCall( return EvmExceptionType.None; } - ReadOnlyMemory callData = vmState.Memory.Load(in dataOffset, dataLength); - Snapshot snapshot = _worldState.TakeSnapshot(); _state.SubtractFromBalance(caller, transferValue, spec); + if (codeInfo.IsEmpty && typeof(TTracingInstructions) != typeof(IsTracing) && !_txTracer.IsTracingActions) + { + // Non contract call, no need to construct call frame can just credit balance and return gas + _returnDataBuffer = default; + stack.PushBytes(StatusCode.SuccessBytes.Span); + UpdateGasUp(gasLimitUl, ref gasAvailable); + return FastCall(spec, out returnData, in transferValue, target); + } + + ReadOnlyMemory callData = vmState.Memory.Load(in dataOffset, dataLength); ExecutionEnvironment callEnv = new ( txExecutionContext: in env.TxExecutionContext, @@ -2296,6 +2306,32 @@ private EvmExceptionType InstructionCall( isCreateOnPreExistingAccount: false); return EvmExceptionType.None; + + EvmExceptionType FastCall(IReleaseSpec spec, out object returnData, in UInt256 transferValue, Address target) + { + if (!_state.AccountExists(target)) + { + _state.CreateAccount(target, transferValue); + } + else + { + _state.AddToBalance(target, transferValue, spec); + } + Metrics.EmptyCalls++; + + returnData = CallResult.BoxedEmpty; + return EvmExceptionType.None; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + void TraceCallDetails(Address codeSource, ref UInt256 callValue, ref UInt256 transferValue, Address caller, Address target) + { + _logger.Trace($"caller {caller}"); + _logger.Trace($"code source {codeSource}"); + _logger.Trace($"target {target}"); + _logger.Trace($"value {callValue}"); + _logger.Trace($"transfer value {transferValue}"); + } } [SkipLocalsInit]