Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
760b319
Incorrect Gas Estimation
svlachakis Jul 31, 2025
07d3983
Incorrect Gas Estimation
svlachakis Jul 31, 2025
bffa6e9
Incorrect Gas Estimation
svlachakis Jul 31, 2025
4ef3b96
Merge branch 'master' into 9061-incorrect-gas-estimation
svlachakis Jul 31, 2025
76746d8
Merge branch 'master' into 9061-incorrect-gas-estimation
svlachakis Aug 1, 2025
74a2409
Changes
svlachakis Aug 3, 2025
313cc67
Merge branch 'master' into 9061-incorrect-gas-estimation
svlachakis Aug 3, 2025
a5b6699
Format
svlachakis Aug 3, 2025
86d1a5f
Tracer Unit Test
svlachakis Aug 3, 2025
6f1b0b3
Improvements
svlachakis Aug 5, 2025
57cdb30
Unit Tests
svlachakis Aug 5, 2025
63035a2
Merge branch 'master' into 9061-incorrect-gas-estimation
svlachakis Aug 5, 2025
8d69dc2
Unit Tests
svlachakis Aug 5, 2025
aa8722e
Unit Tests
svlachakis Aug 5, 2025
4a54305
Merge branch 'master' into 9061-incorrect-gas-estimation
svlachakis Aug 5, 2025
8508806
Improvements
svlachakis Aug 5, 2025
19b4937
Merge branch '9061-incorrect-gas-estimation' of https://github.com/Ne…
svlachakis Aug 5, 2025
c943f30
Merge branch 'master' into 9061-incorrect-gas-estimation
svlachakis Aug 5, 2025
1d5038a
Gas Estimation Refactor
svlachakis Aug 7, 2025
072d8bd
Merge branch '9061-incorrect-gas-estimation' of https://github.com/Ne…
svlachakis Aug 7, 2025
38538ff
Small fixes
svlachakis Aug 7, 2025
1484251
Merge branch 'master' into 9061-incorrect-gas-estimation
svlachakis Aug 7, 2025
0ebfc93
Refactoring
svlachakis Aug 7, 2025
51bfaf1
Revert Changes
svlachakis Aug 7, 2025
5f6a019
Revert Error handling
svlachakis Aug 7, 2025
05fa40a
Revert Error handling
svlachakis Aug 7, 2025
39fbb16
Tests
svlachakis Aug 7, 2025
1a960be
Tests
svlachakis Aug 7, 2025
35b2ffe
Merge branch 'master' into 9061-incorrect-gas-estimation
svlachakis Aug 8, 2025
a93e79b
Error Handling Fixes & Refactoring
svlachakis Aug 8, 2025
6db1aef
Merge branch '9061-incorrect-gas-estimation' of https://github.com/Ne…
svlachakis Aug 8, 2025
c2142e4
GasEstimator Refactoring
svlachakis Aug 8, 2025
39f904a
Small fix
svlachakis Aug 8, 2025
d582293
Small fix
svlachakis Aug 8, 2025
60136ed
Merge branch 'master' into 9061-incorrect-gas-estimation
svlachakis Aug 13, 2025
466e337
Merge remote-tracking branch 'origin/master' into 9061-incorrect-gas-…
svlachakis Aug 14, 2025
cc8d23d
Merge remote-tracking branch 'origin/master' into 9061-incorrect-gas-…
svlachakis Aug 26, 2025
53c3b1e
Refactor revert
svlachakis Aug 26, 2025
9e64227
Revert refactoring
svlachakis Aug 26, 2025
32d3e1e
Refactor Rollback
svlachakis Aug 26, 2025
ad2a6a0
Refactor rollback
svlachakis Aug 26, 2025
2a3d26f
Refactor rollback
svlachakis Aug 26, 2025
43e21fd
Unused Import
svlachakis Aug 26, 2025
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 @@ -331,7 +331,16 @@ public long Should_not_estimate_tx_with_high_value(UInt256 txValue)
GasEstimator estimator = new(_transactionProcessor, _stateProvider, _specProvider, blocksConfig);

long estimate = estimator.Estimate(tx, block.Header, tracer, out string? err, 0);
Assert.That(err, Is.Null);

if (txValue > AccountBalance)
{
Assert.That(err, Is.Not.Null); // Should have error
Assert.That(err, Is.EqualTo("Transaction execution fails"));
}
else
{
Assert.That(err, Is.Null); // Should succeed
}

return estimate;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public EstimateGasTracer()

public byte StatusCode { get; set; }

public bool OutOfGas { get; private set; }

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

Expand All @@ -131,14 +134,21 @@ public override void ReportActionEnd(long gas, Address deploymentAddress, ReadOn

public override void ReportActionError(EvmExceptionType exceptionType)
{
ReportOperationError(exceptionType);
UpdateAdditionalGas();
}

public void ReportActionError(EvmExceptionType exceptionType, long gasLeft)
{
ReportOperationError(exceptionType);
UpdateAdditionalGas(gasLeft);
}

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

private void UpdateAdditionalGas(long? gasLeft = null)
{
if (_isInPrecompile)
Expand Down
86 changes: 41 additions & 45 deletions src/Nethermind/Nethermind.Blockchain/Tracing/GasEstimator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.Threading;
using Nethermind.Config;
using Nethermind.Core;
using Nethermind.Core.Crypto;
using Nethermind.Core.Specs;
using Nethermind.Evm;
using Nethermind.Evm.TransactionProcessing;
Expand Down Expand Up @@ -62,7 +61,24 @@ public long Estimate(Transaction tx, BlockHeader header, EstimateGasTracer gasTr
UInt256 senderBalance = _stateProvider.GetBalance(tx.SenderAddress);
if (tx.ValueRef != UInt256.Zero && tx.ValueRef > senderBalance && !tx.IsSystem())
{
return gasTracer.CalculateAdditionalGasRequired(tx, releaseSpec);
long additionalGas = gasTracer.CalculateAdditionalGasRequired(tx, releaseSpec);
if (additionalGas == 0)
{
// If no additional gas can help, it's an insufficient balance error
if (gasTracer.OutOfGas)
{
err = "Gas estimation failed due to out of gas";
}
else if (gasTracer.StatusCode == StatusCode.Failure)
{
err = gasTracer.Error ?? "Transaction execution fails";
}
else
{
err = "insufficient balance";
}
}
return additionalGas;
}

var lowerBound = IntrinsicGasCalculator.Calculate(tx, releaseSpec).MinimalGas;
Expand All @@ -82,19 +98,21 @@ public long Estimate(Transaction tx, BlockHeader header, EstimateGasTracer gasTr
}

// Execute binary search to find the optimal gas estimation.
return BinarySearchEstimate(leftBound, rightBound, tx, header, gasTracer, errorMargin, token);
return BinarySearchEstimate(leftBound, rightBound, tx, header, gasTracer, errorMargin, token, out err);
}

private long BinarySearchEstimate(long leftBound, long rightBound, Transaction tx, BlockHeader header,
EstimateGasTracer gasTracer, int errorMargin, CancellationToken token)
EstimateGasTracer gasTracer, int errorMargin, CancellationToken token, out string? err)
{
err = null;
double marginWithDecimals = errorMargin == 0 ? 1 : errorMargin / 10000d + 1;

//This approach is similar to Geth, by starting from an optimistic guess the number of iterations is greatly reduced in most cases
long optimisticGasEstimate =
(long)((gasTracer.GasSpent + gasTracer.TotalRefund + GasCostOf.CallStipend) * marginWithDecimals);
if (optimisticGasEstimate > leftBound && optimisticGasEstimate < rightBound)
{
if (TryExecutableTransaction(tx, header, optimisticGasEstimate, token))
if (TryExecutableTransaction(tx, header, optimisticGasEstimate, token, gasTracer))
rightBound = optimisticGasEstimate;
else
leftBound = optimisticGasEstimate;
Expand All @@ -106,7 +124,7 @@ private long BinarySearchEstimate(long leftBound, long rightBound, Transaction t
&& leftBound + 1 < rightBound)
{
long mid = (leftBound + rightBound) / 2;
if (!TryExecutableTransaction(tx, header, mid, token))
if (!TryExecutableTransaction(tx, header, mid, token, gasTracer))
{
leftBound = mid;
}
Expand All @@ -116,59 +134,37 @@ private long BinarySearchEstimate(long leftBound, long rightBound, Transaction t
}
}

if (rightBound == cap && !TryExecutableTransaction(tx, header, rightBound, token))
if (rightBound == cap && !TryExecutableTransaction(tx, header, rightBound, token, gasTracer))
{
// Set error based on failure reason
if (gasTracer.OutOfGas)
{
err = "Gas estimation failed due to out of gas";
}
else if (gasTracer.StatusCode == StatusCode.Failure)
{
err = gasTracer.Error ?? "Transaction execution fails";
}
else
{
err = "Transaction execution fails";
}
return 0;
}

return rightBound;
}

private bool TryExecutableTransaction(Transaction transaction, BlockHeader block, long gasLimit,
CancellationToken token)
CancellationToken token, EstimateGasTracer gasTracer)
{
OutOfGasTracer tracer = new();

Transaction txClone = new Transaction();
transaction.CopyTo(txClone);
txClone.GasLimit = gasLimit;

_transactionProcessor.SetBlockExecutionContext(new(block, _specProvider.GetSpec(block)));
_transactionProcessor.CallAndRestore(txClone, tracer.WithCancellation(token));

return !tracer.OutOfGas;
}

private class OutOfGasTracer : TxTracer
{
public OutOfGasTracer()
{
OutOfGas = false;
}

public override bool IsTracingReceipt => true;
public override bool IsTracingInstructions => true;
public override bool IsTracingActions => true;
public bool OutOfGas { get; private set; }
TransactionResult result = _transactionProcessor.CallAndRestore(txClone, gasTracer.WithCancellation(token));

public override void MarkAsSuccess(Address recipient, GasConsumed gasSpent, byte[] output, LogEntry[] logs,
Hash256? stateRoot = null)
{
}

public override void MarkAsFailed(Address recipient, GasConsumed gasSpent, byte[] output, string? error,
Hash256? stateRoot = null)
{
}

public override void ReportActionError(EvmExceptionType error)
{
OutOfGas |= error == EvmExceptionType.OutOfGas;
}

public override void ReportOperationError(EvmExceptionType error)
{
OutOfGas |= error == EvmExceptionType.OutOfGas;
}
return result.TransactionExecuted && gasTracer.StatusCode == StatusCode.Success && !gasTracer.OutOfGas;
}
}
Loading