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 @@ -2821,6 +2821,80 @@ public void PosterDataCost_WhenCalledWithNonBatchPosterOrArbitrumTxTypes_ShouldR
cost.Should().Be(UInt256.Zero);
}

[Test]
public void RefundOnFailContractCreation_ContractCollision_RefundsComputeHoldGas()
{
using ArbitrumRpcTestBlockchain chain = ArbitrumRpcTestBlockchain.CreateDefault(builder =>
{
builder.AddScoped(new ArbitrumTestBlockchainBase.Configuration
{
SuggestGenesisOnStart = true,
FillWithTestDataOnStart = true
});
});

FullChainSimulationSpecProvider fullChainSimulationSpecProvider = new();

ulong baseFeePerGas = 1_000;
chain.BlockTree.Head!.Header.BaseFeePerGas = baseFeePerGas;
chain.BlockTree.Head!.Header.Author = ArbosAddresses.BatchPosterAddress;
chain.TxProcessor.SetBlockExecutionContext(new BlockExecutionContext(chain.BlockTree.Head!.Header,
fullChainSimulationSpecProvider.GetSpec(chain.BlockTree.Head!.Header)));

using IDisposable dispose = chain.WorldStateManager.GlobalWorldState.BeginScope(chain.BlockTree.Head!.Header);
IWorldState worldState = chain.WorldStateManager.GlobalWorldState;

SystemBurner burner = new(readOnly: false);
ArbosState arbosState = ArbosState.OpenArbosState(worldState, burner, _logManager.GetClassLogger<ArbosState>());

ulong blockGasLimit = 50_000;
arbosState.L2PricingState.PerBlockGasLimitStorage.Set(blockGasLimit);

Address sender = TestItem.AddressA;
byte[] initCode = Prepare.EvmCode
.PushData(0)
.Op(Instruction.RETURN)
.Done;

long gasLimit = 200_000;
Address contractAddress = ContractAddress.From(sender, 0);

byte[] existingCode = [0x60, 0x00];
worldState.CreateAccount(contractAddress, 0, 1);
worldState.InsertCode(contractAddress, Keccak.Compute(existingCode), existingCode, fullChainSimulationSpecProvider.GenesisSpec);

Transaction tx = Build.A.Transaction
.WithChainId(chain.ChainSpec.ChainId)
.WithType(TxType.Legacy)
.WithTo(null)
.WithData(initCode)
.WithValue(0)
.WithGasPrice(baseFeePerGas)
.WithGasLimit(gasLimit)
.WithNonce(0)
.SignedAndResolved(TestItem.PrivateKeyA)
.TestObject;

UInt256 senderInitialBalance = 10.Ether();
worldState.CreateAccount(sender, senderInitialBalance, 0);

ArbitrumTransactionProcessor arbProcessor = (ArbitrumTransactionProcessor)chain.TxProcessor;
TestAllTracerWithOutput tracer = new();
TransactionResult result = arbProcessor.Execute(tx, tracer);

result.Should().Be(TransactionResult.Ok);

ulong actualComputeHoldGas = arbProcessor.TxExecContext.ComputeHoldGas;
actualComputeHoldGas.Should().BeGreaterThan(0);

long expectedGasSpent = gasLimit - (long)actualComputeHoldGas;
tracer.GasSpent.Should().Be(expectedGasSpent);

UInt256 senderFinalBalance = worldState.GetBalance(sender);
UInt256 expectedBalance = senderInitialBalance - (ulong)expectedGasSpent * baseFeePerGas;
senderFinalBalance.Should().Be(expectedBalance);
}

[TestCaseSource(nameof(PosterDataCostReturnsNonZeroCases))]
public void PosterDataCost_WhenCalledWithBatchPosterAndStandardTx_ShouldReturnNonZero(string posterHex, TxType txType)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,19 @@ protected override bool TryCalculatePremiumPerGas(Transaction tx, in UInt256 bas
return base.TryCalculatePremiumPerGas(tx, in effectiveBaseFee, out premiumPerGas);
}

protected override GasConsumed RefundOnFailContractCreation(Transaction tx, IReleaseSpec spec, ExecutionOptions opts, in UInt256 gasPrice)
{
long spentGas = tx.GasLimit;

// ComputeHoldGas should always be refunded, independently of the tx result (success or failure)
spentGas -= (long)TxExecContext.ComputeHoldGas;

if (!opts.HasFlag(ExecutionOptions.SkipValidation))
WorldState.AddToBalance(tx.SenderAddress!, (ulong)(tx.GasLimit - spentGas) * gasPrice, spec);

return spentGas;
}

private TransactionResult FinalizeTransaction(TransactionResult result, Transaction tx, ITxTracer tracer, bool isPreProcessing, IReadOnlyList<LogEntry>? additionalLogs = null)
{
//TODO - need to establish what should be the correct flags to handle here
Expand Down