In Nethermind we have only Message's gas price equivalent. That leads to an incorrect gas price being assigned to EvmData at StylusPrograms.
protected virtual TransactionResult Execute(Transaction tx, ITxTracer tracer, ExecutionOptions opts)
{
if (Out.IsTargetBlock)
Out.Log($"evm execute options={opts}");
BlockHeader header = VirtualMachine.BlockExecutionContext.Header;
IReleaseSpec spec = GetSpec(header);
// restore is CallAndRestore - previous call, we will restore state after the execution
bool restore = opts.HasFlag(ExecutionOptions.Restore);
// commit - is for standard execute, we will commit thee state after execution
// !commit - is for build up during block production, we won't commit state after each transaction to support rollbacks
// we commit only after all block is constructed
bool commit = opts.HasFlag(ExecutionOptions.Commit) || (!opts.HasFlag(ExecutionOptions.SkipValidation) && !spec.IsEip658Enabled);
TransactionResult result;
IntrinsicGas intrinsicGas = CalculateIntrinsicGas(tx, spec);
if (Out.IsTargetBlock)
Out.Log($"intrinsic gas standard={intrinsicGas.Standard} floor={intrinsicGas.FloorGas}");
if (!(result = ValidateStatic(tx, header, spec, opts, in intrinsicGas))) return result;
UInt256 effectiveGasPriceForTxContext = tx.CalculateEffectiveGasPrice(spec.IsEip1559Enabled, in header.BaseFeePerGas);
UInt256 effectiveGasPrice = CalculateEffectiveGasPrice(tx, spec.IsEip1559Enabled, header.BaseFeePerGas);
if (Out.IsTargetBlock)
Out.Log($"evm call effectiveGasPrice msg={effectiveGasPrice} txContext={effectiveGasPriceForTxContext} " +
$"maxPriorityFeePerGas={tx.MaxPriorityFeePerGas} maxFeePerGas={tx.MaxFeePerGas} " +
$"baseFeePerGas={header.BaseFeePerGas} eip1559Enabled={spec.IsEip1559Enabled}");
VirtualMachine.SetTxExecutionContext(new(tx.SenderAddress, _codeInfoRepository, tx.BlobVersionedHashes, in effectiveGasPriceForTxContext));
UpdateMetrics(opts, effectiveGasPrice);
bool deleteCallerAccount = RecoverSenderIfNeeded(tx, spec, opts, effectiveGasPrice);
if (!(result = ValidateSender(tx, header, spec, tracer, opts))) return result;
if (!(result = BuyGas(tx, spec, tracer, opts, effectiveGasPrice, out UInt256 premiumPerGas, out UInt256 senderReservedGasPayment, out UInt256 blobBaseFee))) return result;
if (!(result = IncrementNonce(tx, header, spec, tracer, opts))) return result;
if (commit) WorldState.Commit(spec, tracer.IsTracingState ? tracer : NullTxTracer.Instance, commitRoots: false);
// substate.Logs contains a reference to accessTracker.Logs so we can't Dispose until end of the method
using StackAccessTracker accessTracker = new();
int delegationRefunds = (!spec.IsEip7702Enabled || !tx.HasAuthorizationList) ? 0 : ProcessDelegations(tx, spec, accessTracker);
if (!(result = CalculateAvailableGas(tx, intrinsicGas, out long gasAvailable))) return result;
if (!(result = BuildExecutionEnvironment(tx, spec, _codeInfoRepository, accessTracker, out ExecutionEnvironment env))) return result;
if (Out.IsTargetBlock)
Out.Log($"evm call from={tx.SenderAddress} to={tx.To} gasAvailable={gasAvailable} value={tx.Value}");
int statusCode = !tracer.IsTracingInstructions ?
ExecuteEvmCall<OffFlag>(tx, header, spec, tracer, opts, delegationRefunds, intrinsicGas, accessTracker, gasAvailable, env, out TransactionSubstate substate, out GasConsumed spentGas, in effectiveGasPrice) :
ExecuteEvmCall<OnFlag>(tx, header, spec, tracer, opts, delegationRefunds, intrinsicGas, accessTracker, gasAvailable, env, out substate, out spentGas, in effectiveGasPrice);
PayFees(tx, header, spec, tracer, in substate, spentGas.SpentGas, premiumPerGas, blobBaseFee, statusCode);
tx.SpentGas = spentGas.SpentGas;
// Finalize
if (restore)
{
WorldState.Reset(resetBlockChanges: false);
if (deleteCallerAccount)
{
WorldState.DeleteAccount(tx.SenderAddress!);
}
else
{
if (!opts.HasFlag(ExecutionOptions.SkipValidation))
WorldState.AddToBalance(tx.SenderAddress!, senderReservedGasPayment, spec);
DecrementNonce(tx);
WorldState.Commit(spec, commitRoots: false);
}
}
else if (commit)
{
WorldState.Commit(spec, tracer.IsTracingState ? tracer : NullStateTracer.Instance, commitRoots: !spec.IsEip658Enabled);
}
else
{
WorldState.ResetTransient();
}
if (tracer.IsTracingReceipt)
{
Hash256 stateRoot = null;
if (!spec.IsEip658Enabled)
{
WorldState.RecalculateStateRoot();
stateRoot = WorldState.StateRoot;
}
if (Out.IsTargetBlock)
Out.Log($"receipt statusCode={statusCode} spentGas={spentGas} error={substate.Error} " +
$"logsCount={substate.Logs.Count} evmExceptionType={substate.EvmExceptionType}");
if (statusCode == StatusCode.Failure)
{
byte[] output = substate.ShouldRevert ? substate.Output.Bytes.ToArray() : [];
tracer.MarkAsFailed(env.ExecutingAccount, spentGas, output, substate.Error, stateRoot);
if (Out.IsTargetBlock)
Out.Log($"receipt markAsFailed evmExceptionType={result.EvmExceptionType} result={result.ToString()}");
}
else
{
LogEntry[] logs = substate.Logs.Count != 0 ? substate.Logs.ToArray() : [];
tracer.MarkAsSuccess(env.ExecutingAccount, spentGas, substate.Output.Bytes.ToArray(), logs, stateRoot);
if (Out.IsTargetBlock)
Out.Log($"receipt markAsSuccess evmExceptionType={result.EvmExceptionType} result={result.ToString()} " +
$"logs={string.Join(";", logs?.Select(l => $"a={l.Address}, d={l.Data.ToHexString()}") ?? [])}");
}
}
if (substate.EvmExceptionType != EvmExceptionType.None)
return TransactionResult.EvmException(substate.EvmExceptionType);
return TransactionResult.Ok;
}
The issue faced at
55713322.Nitro has two different gas prices:
Message's gas price is being set in https://github.com/OffchainLabs/go-ethereum/blob/master/core/state_transition.go#L337
TxContext's price is set from Message's https://github.com/OffchainLabs/go-ethereum/blob/master/core/evm.go#L89
Then... Message's gas price can be overridden https://github.com/OffchainLabs/go-ethereum/blob/master/core/state_transition.go#L601
In Nethermind we have only Message's gas price equivalent. That leads to an incorrect gas price being assigned to
EvmDataatStylusPrograms.Quick fix of
TransactionProcessorBaselike below resolves the issue (seeeffectiveGasPriceForTxContextvariable added)