Skip to content
1 change: 1 addition & 0 deletions src/Nethermind/Nethermind.Evm.Test/Eip3198BaseFeeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public void Base_fee_opcode_should_return_expected_results(bool eip3198Enabled,
if (send1559Tx)
{
transaction.DecodedMaxFeePerGas = (UInt256)baseFee;
transaction.GasPrice = (UInt256)baseFee;
transaction.Type = TxType.EIP1559;
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public ProofTxTracer(bool treatSystemAccountDifferently)
public bool IsTracingStack => false;
public bool IsTracingState => true;
public bool IsTracingStorage => true;

public void ReportActionEnd(long gas, Address deploymentAddress, ReadOnlyMemory<byte> deployedCode)
{
throw new NotSupportedException();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,10 @@ public interface ITransactionProcessor
/// Call transaction, no validations, commit state
/// </summary>
void Trace(Transaction transaction, BlockHeader block, ITxTracer txTracer);

/// <summary>
/// Call transaction, rollback state, do not check base fee when gas pricing is not passed
/// </summary>
void CallWithNoBaseFee(Transaction transaction, BlockHeader block, ITxTracer txTracer);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ public void BuildUp(Transaction transaction, BlockHeader block, ITxTracer txTrac
public void Trace(Transaction transaction, BlockHeader block, ITxTracer txTracer) =>
_transactionProcessor.Trace(transaction, block, txTracer);

public void CallWithNoBaseFee(Transaction transaction, BlockHeader block, ITxTracer txTracer)
{
_transactionProcessor.CallWithNoBaseFee(transaction, block, txTracer);
}


public bool IsContractDeployed(Address address) => _stateProvider.IsContract(address);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,12 @@ private enum ExecutionOptions
/// <summary>
/// Commit and later restore state also skip validation, use for CallAndRestore
/// </summary>
CommitAndRestore = Commit | Restore | NoValidation
CommitAndRestore = Commit | Restore | NoValidation,

/// <summary>
/// Commit and later restore state also skip base fee validation if gas pricing is not passed
/// </summary>
NoBaseFee = CommitAndRestore | 8
}

public TransactionProcessor(
Expand Down Expand Up @@ -116,6 +121,11 @@ public void Trace(Transaction transaction, BlockHeader block, ITxTracer txTracer
Execute(transaction, block, txTracer, ExecutionOptions.NoValidation);
}

public void CallWithNoBaseFee(Transaction transaction, BlockHeader block, ITxTracer txTracer)
{
Execute(transaction, block, txTracer, ExecutionOptions.NoBaseFee);
}

private void QuickFail(Transaction tx, BlockHeader block, ITxTracer txTracer, bool eip658NotEnabled,
string? reason)
{
Expand All @@ -138,6 +148,19 @@ private void QuickFail(Transaction tx, BlockHeader block, ITxTracer txTracer, bo
}
}

private bool ShouldSkipGasPricing(IReleaseSpec spec, Transaction transaction)
{
if (!spec.IsEip1559Enabled) return transaction.GasPrice.IsZero;

if (transaction.IsEip1559)
{
return transaction.MaxFeePerGas.IsZero && transaction.MaxPriorityFeePerGas.IsZero;
}

return transaction.GasPrice.IsZero;

}

private void Execute(Transaction transaction, BlockHeader block, ITxTracer txTracer,
ExecutionOptions executionOptions)
{
Expand All @@ -152,15 +175,38 @@ private void Execute(Transaction transaction, BlockHeader block, ITxTracer txTra
//we commit only after all block is constructed
bool notSystemTransaction = !transaction.IsSystem();
bool deleteCallerAccount = false;
bool noBaseFee = (executionOptions & ExecutionOptions.NoBaseFee) == ExecutionOptions.NoBaseFee;
bool validatePricing = !noValidation || noBaseFee;

IReleaseSpec spec = _specProvider.GetSpec(block.Number);
if (!notSystemTransaction)
{
spec = new SystemTransactionReleaseSpec(spec);
}

bool skipGasPricing = ShouldSkipGasPricing(spec, transaction);

UInt256 value = transaction.Value;

if (!noBaseFee || transaction.MaxFeePerGas > 0 || transaction.MaxPriorityFeePerGas > 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

meaningful labels for bool checks

{
if (validatePricing && transaction.MaxFeePerGas < block.BaseFeePerGas)
{
TraceLogInvalidTx(transaction, "MAX FEE PER GAS LESS THAN BLOCK BASE FEE");
QuickFail(transaction, block, txTracer, eip658NotEnabled,
$"max fee per gas less than block base fee: address {transaction.SenderAddress}, maxFeePerGas: {transaction.MaxFeePerGas}, baseFee {block.BaseFeePerGas}");
return;
}

if (validatePricing && transaction.MaxFeePerGas < transaction.MaxPriorityFeePerGas)
{
TraceLogInvalidTx(transaction, "MAX FEE PER GAS LESS THAN MAX PRIORITY FEE PER GAS");
QuickFail(transaction, block, txTracer, eip658NotEnabled,
$"max fee per gas less than max priority fee per gas: address {transaction.SenderAddress}, maxFeePerGas: {transaction.MaxFeePerGas}, maxPriorityFeePerGas: {transaction.MaxPriorityFeePerGas}");
return;
}
}

if (!transaction.TryCalculatePremiumPerGas(block.BaseFeePerGas, out UInt256 premiumPerGas) && !noValidation)
{
TraceLogInvalidTx(transaction, "MINER_PREMIUM_IS_NEGATIVE");
Expand Down Expand Up @@ -200,15 +246,15 @@ private void Execute(Transaction transaction, BlockHeader block, ITxTracer txTra
if (gasLimit < intrinsicGas)
{
TraceLogInvalidTx(transaction, $"GAS_LIMIT_BELOW_INTRINSIC_GAS {gasLimit} < {intrinsicGas}");
QuickFail(transaction, block, txTracer, eip658NotEnabled, "gas limit below intrinsic gas");
QuickFail(transaction, block, txTracer, eip658NotEnabled, $"gas limit below intrinsic gas: have {gasLimit}, want {intrinsicGas}");
return;
}

if (!noValidation && gasLimit > block.GasLimit - block.GasUsed)
{
TraceLogInvalidTx(transaction,
$"BLOCK_GAS_LIMIT_EXCEEDED {gasLimit} > {block.GasLimit} - {block.GasUsed}");
QuickFail(transaction, block, txTracer, eip658NotEnabled, "block gas limit exceeded");
QuickFail(transaction, block, txTracer, eip658NotEnabled, $"block gas limit exceeded: gasLimit {gasLimit} > block gasLimit {block.GasLimit} - block gasUsed {block.GasUsed}");
return;
}
}
Expand Down Expand Up @@ -249,24 +295,26 @@ private void Execute(Transaction transaction, BlockHeader block, ITxTracer txTra

if (notSystemTransaction)
{
UInt256 senderBalance = _stateProvider.GetBalance(caller);
if (!noValidation && ((ulong)intrinsicGas * effectiveGasPrice + value > senderBalance ||
senderReservedGasPayment + value > senderBalance))
if (!noBaseFee || !skipGasPricing)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

meaningful labels for bool checks

{
TraceLogInvalidTx(transaction,
$"INSUFFICIENT_SENDER_BALANCE: ({caller})_BALANCE = {senderBalance}");
QuickFail(transaction, block, txTracer, eip658NotEnabled, "insufficient sender balance");
return;
}
UInt256 senderBalance = _stateProvider.GetBalance(caller);
if (validatePricing && ((ulong)intrinsicGas * effectiveGasPrice + value > senderBalance ||
senderReservedGasPayment + value > senderBalance))
{
TraceLogInvalidTx(transaction,
$"INSUFFICIENT_SENDER_BALANCE: ({caller})_BALANCE = {senderBalance}");
QuickFail(transaction, block, txTracer, eip658NotEnabled, $"insufficient funds for gas * price + value: address {caller}, have {senderBalance}, want {UInt256.Max(senderReservedGasPayment + value, (ulong)intrinsicGas * effectiveGasPrice + value)}");
return;
}

if (!noValidation && spec.IsEip1559Enabled && !transaction.IsFree() &&
senderBalance < (UInt256)transaction.GasLimit * transaction.MaxFeePerGas + value)
{
TraceLogInvalidTx(transaction,
$"INSUFFICIENT_MAX_FEE_PER_GAS_FOR_SENDER_BALANCE: ({caller})_BALANCE = {senderBalance}, MAX_FEE_PER_GAS: {transaction.MaxFeePerGas}");
QuickFail(transaction, block, txTracer, eip658NotEnabled,
"insufficient MaxFeePerGas for sender balance");
return;
if (validatePricing && spec.IsEip1559Enabled && !transaction.IsFree() &&
senderBalance < (UInt256)transaction.GasLimit * transaction.MaxFeePerGas + value)
{
TraceLogInvalidTx(transaction,
$"INSUFFICIENT_MAX_FEE_PER_GAS_FOR_SENDER_BALANCE: ({caller})_BALANCE = {senderBalance}, MAX_FEE_PER_GAS: {transaction.MaxFeePerGas}");
QuickFail(transaction, block, txTracer, eip658NotEnabled, $"insufficient MaxFeePerGas for sender balance: address {{caller}}, sender_balance = {senderBalance}, maxFeePerGas: {transaction.MaxFeePerGas}");
return;
}
}

if (transaction.Nonce != _stateProvider.GetNonce(caller))
Expand Down
22 changes: 16 additions & 6 deletions src/Nethermind/Nethermind.Facade/BlockchainBridge.cs
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,11 @@ public CallOutput(byte[] outputData, long gasSpent, string error, bool inputErro
public AccessList? AccessList { get; set; }
}

public CallOutput Call(BlockHeader header, Transaction tx, CancellationToken cancellationToken)
public CallOutput Call(BlockHeader header, Transaction tx, CancellationToken cancellationToken, bool noBaseFee = false)
{
CallOutputTracer callOutputTracer = new();
(bool Success, string Error) tryCallResult = TryCallAndRestore(header, header.Timestamp, tx, false,
callOutputTracer.WithCancellation(cancellationToken));
callOutputTracer.WithCancellation(cancellationToken), noBaseFee);
return new CallOutput
{
Error = tryCallResult.Success ? callOutputTracer.Error : tryCallResult.Error,
Expand Down Expand Up @@ -226,11 +226,12 @@ public CallOutput CreateAccessList(BlockHeader header, Transaction tx, Cancellat
in UInt256 timestamp,
Transaction transaction,
bool treatBlockHeaderAsParentBlock,
ITxTracer tracer)
ITxTracer tracer,
bool noBaseFee = false)
{
try
{
CallAndRestore(blockHeader, timestamp, transaction, treatBlockHeaderAsParentBlock, tracer);
CallAndRestore(blockHeader, timestamp, transaction, treatBlockHeaderAsParentBlock, tracer, noBaseFee);
return (true, string.Empty);
}
catch (InsufficientBalanceException ex)
Expand All @@ -244,7 +245,8 @@ private void CallAndRestore(
in UInt256 timestamp,
Transaction transaction,
bool treatBlockHeaderAsParentBlock,
ITxTracer tracer)
ITxTracer tracer,
bool noBaseFee = false)
{
if (transaction.SenderAddress == null)
{
Expand Down Expand Up @@ -274,7 +276,15 @@ private void CallAndRestore(
: blockHeader.BaseFeePerGas;

transaction.Hash = transaction.CalculateHash();
_transactionProcessor.CallAndRestore(transaction, callHeader, tracer);
if (noBaseFee)
{
_transactionProcessor.CallWithNoBaseFee(transaction, callHeader, tracer);
}
else
{
_transactionProcessor.CallAndRestore(transaction, callHeader, tracer);
}

}
finally
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public ConstantBridgeContract(Contract contract, IBlockchainBridge blockchainBri
public override object[] Call(CallInfo callInfo)
{
var transaction = GenerateTransaction(callInfo);
var result = _blockchainBridge.Call(callInfo.ParentHeader, transaction, CancellationToken.None);
var result = _blockchainBridge.Call(callInfo.ParentHeader, transaction, CancellationToken.None, false);
if (!string.IsNullOrEmpty(result.Error))
{
throw new AbiException(result.Error);
Expand Down
2 changes: 1 addition & 1 deletion src/Nethermind/Nethermind.Facade/IBlockchainBridge.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public interface IBlockchainBridge : ILogFinder
TxReceipt GetReceipt(Keccak txHash);
(TxReceipt Receipt, UInt256? EffectiveGasPrice) GetReceiptAndEffectiveGasPrice(Keccak txHash);
(TxReceipt Receipt, Transaction Transaction, UInt256? baseFee) GetTransaction(Keccak txHash);
BlockchainBridge.CallOutput Call(BlockHeader header, Transaction tx, CancellationToken cancellationToken);
BlockchainBridge.CallOutput Call(BlockHeader header, Transaction tx, CancellationToken cancellationToken, bool noBaseFee);
BlockchainBridge.CallOutput EstimateGas(BlockHeader header, Transaction tx, CancellationToken cancellationToken);
BlockchainBridge.CallOutput CreateAccessList(BlockHeader header, Transaction tx, CancellationToken cancellationToken, bool optimize);
ulong GetChainId();
Expand Down
Loading