Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ public EstimateGasTracer()

public bool OutOfGas { get; private set; }

public bool TopLevelRevert { get; private set; }

public override void MarkAsSuccess(Address recipient, GasConsumed gasSpent, byte[] output, LogEntry[] logs,
Hash256? stateRoot = null)
{
Expand Down Expand Up @@ -108,6 +110,7 @@ public override void ReportAction(long gas, UInt256 value, Address from, Address
if (_currentNestingLevel == -1)
{
OutOfGas = false;
TopLevelRevert = false;
IntrinsicGasAt = gas;
}

Expand Down Expand Up @@ -146,7 +149,12 @@ public void ReportActionError(EvmExceptionType exceptionType, long gasLeft)

public override void ReportOperationError(EvmExceptionType error)
{
OutOfGas |= error == EvmExceptionType.OutOfGas || error == EvmExceptionType.Revert;
OutOfGas |= error == EvmExceptionType.OutOfGas;

if (error == EvmExceptionType.Revert && _currentNestingLevel == 0)
{
TopLevelRevert = true;
}
}

private void UpdateAdditionalGas(long? gasLeft = null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Threading;
using Nethermind.Config;
using Nethermind.Core;
using Nethermind.Core.Extensions;
using Nethermind.Core.Specs;
using Nethermind.Evm;
using Nethermind.Evm.TransactionProcessing;
Expand Down Expand Up @@ -134,6 +135,10 @@ private long BinarySearchEstimate(
private static string GetError(EstimateGasTracer gasTracer, string defaultError = "Transaction execution fails") =>
gasTracer switch
{
{ TopLevelRevert: true } => gasTracer.Error ??
(gasTracer.ReturnValue?.Length > 0 ?
$"execution reverted: {gasTracer.ReturnValue.ToHexString(true)}"
: "execution reverted"),
{ OutOfGas: true } => "Gas estimation failed due to out of gas",
{ StatusCode: StatusCode.Failure } => gasTracer.Error ?? "Transaction execution fails",
_ => defaultError
Expand All @@ -149,6 +154,8 @@ private bool TryExecutableTransaction(Transaction transaction, BlockHeader block
transactionProcessor.SetBlockExecutionContext(new BlockExecutionContext(block, specProvider.GetSpec(block)));
TransactionResult result = transactionProcessor.CallAndRestore(txClone, gasTracer.WithCancellation(token));

return result.TransactionExecuted && gasTracer.StatusCode == StatusCode.Success && !gasTracer.OutOfGas;
// Transaction succeeds if it executed, has success status, no OutOfGas, and no top-level revert
return result.TransactionExecuted && gasTracer.StatusCode == StatusCode.Success &&
!gasTracer.OutOfGas && !gasTracer.TopLevelRevert;
}
}
58 changes: 57 additions & 1 deletion src/Nethermind/Nethermind.Evm.Test/Tracing/GasEstimationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -604,7 +604,8 @@ public void Should_estimate_gas_for_explicit_gas_check_and_revert(long gasLimit,

if (shouldSucceed)
{
result.Should().BeGreaterThan(1_000_000, "Gas estimation should account for the gas threshold in the contract");
result.Should().BeGreaterThan(1_000_000,
"Gas estimation should account for the gas threshold in the contract");
err.Should().BeNull();
}
else
Expand All @@ -613,6 +614,61 @@ public void Should_estimate_gas_for_explicit_gas_check_and_revert(long gasLimit,
}
}

[Test]
public void Should_succeed_with_internal_revert()
{
using TestEnvironment testEnvironment = new();
long gasLimit = 100_000;
Transaction tx = Build.A.Transaction.WithGasLimit(gasLimit).TestObject;
Block block = Build.A.Block.WithNumber(1).WithTransactions(tx).WithGasLimit(gasLimit).TestObject;

long gasLeft = gasLimit - 22000;
testEnvironment.tracer.ReportAction(gasLeft, 0, Address.Zero, Address.Zero, Array.Empty<byte>(),
ExecutionType.TRANSACTION, false);

gasLeft = 63 * gasLeft / 64;
testEnvironment.tracer.ReportAction(gasLeft, 0, Address.Zero, Address.Zero, Array.Empty<byte>(),
ExecutionType.CALL, false);

gasLeft = 63 * gasLeft / 64;
testEnvironment.tracer.ReportAction(gasLeft, 0, Address.Zero, Address.Zero, Array.Empty<byte>(),
ExecutionType.CALL, false);

testEnvironment.tracer.ReportActionRevert(gasLeft - 1000, Array.Empty<byte>());
testEnvironment.tracer.ReportActionEnd(gasLeft - 500, Array.Empty<byte>());
testEnvironment.tracer.ReportActionEnd(gasLeft, Array.Empty<byte>());
testEnvironment.tracer.MarkAsSuccess(Address.Zero, 25000, Array.Empty<byte>(), Array.Empty<LogEntry>());

long result = testEnvironment.estimator.Estimate(tx, block.Header, testEnvironment.tracer, out string? err);

result.Should().BeGreaterThan(0);
err.Should().BeNull();
testEnvironment.tracer.TopLevelRevert.Should().BeFalse();
testEnvironment.tracer.OutOfGas.Should().BeFalse();
}

[Test]
public void Should_fail_with_top_level_revert()
{
using TestEnvironment testEnvironment = new();
long gasLimit = 100_000;
Transaction tx = Build.A.Transaction.WithGasLimit(gasLimit).TestObject;
Block block = Build.A.Block.WithNumber(1).WithTransactions(tx).WithGasLimit(gasLimit).TestObject;

long gasLeft = gasLimit - 22000;
testEnvironment.tracer.ReportAction(gasLeft, 0, Address.Zero, Address.Zero, Array.Empty<byte>(),
ExecutionType.TRANSACTION, false);

testEnvironment.tracer.ReportActionRevert(gasLeft - 1000, Array.Empty<byte>());
testEnvironment.tracer.MarkAsFailed(Address.Zero, 25000, Array.Empty<byte>(), "execution reverted");

long result = testEnvironment.estimator.Estimate(tx, block.Header, testEnvironment.tracer, out string? err);

result.Should().Be(0);
err.Should().Be("execution reverted");
testEnvironment.tracer.TopLevelRevert.Should().BeTrue();
}

private class TestEnvironment : IDisposable
{
public ISpecProvider _specProvider;
Expand Down