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 @@ -500,11 +500,8 @@ public TransactionProcessingResult processTransaction(

// Refund the sender by what we should and pay the miner fee (note that we're doing them one
// after the other so that if it is the same account somehow, we end up with the right result)
final long selfDestructRefund =
gasCalculator.getSelfDestructRefundAmount() * initialFrame.getSelfDestructs().size();
final long baseRefundGas =
initialFrame.getGasRefund() + selfDestructRefund + codeDelegationRefund;
final long refundedGas = refunded(transaction, initialFrame.getRemainingGas(), baseRefundGas);
final long refundedGas =
gasCalculator.calculateGasRefund(transaction, initialFrame, codeDelegationRefund);
final Wei refundedWei = transactionGasPrice.multiply(refundedGas);
final Wei balancePriorToRefund = sender.getBalance();
sender.incrementBalance(refundedWei);
Expand Down Expand Up @@ -635,15 +632,6 @@ public AbstractMessageProcessor getMessageProcessor(final MessageFrame.Type type
};
}

protected long refunded(
final Transaction transaction, final long gasRemaining, final long gasRefund) {
// Integer truncation takes care of the floor calculation needed after the divide.
final long maxRefundAllowance =
(transaction.getGasLimit() - gasRemaining) / gasCalculator.getMaxRefundQuotient();
final long refundAllowance = Math.min(maxRefundAllowance, gasRefund);
return gasRemaining + refundAllowance;
}

private String printableStackTraceFromThrowable(final RuntimeException re) {
final StringBuilder builder = new StringBuilder();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import static org.hyperledger.besu.evm.internal.Words.numWords;

import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Transaction;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.frame.MessageFrame;
Expand Down Expand Up @@ -566,4 +567,25 @@ public long getMaximumTransactionCost(final int size) {
public long getMinimumTransactionCost() {
return TX_BASE_COST;
}

@Override
public long calculateGasRefund(
final Transaction transaction,
final MessageFrame initialFrame,
final long codeDelegationRefund) {
final long selfDestructRefund =
getSelfDestructRefundAmount() * initialFrame.getSelfDestructs().size();
final long baseRefundGas =
initialFrame.getGasRefund() + selfDestructRefund + codeDelegationRefund;
return refunded(transaction, initialFrame.getRemainingGas(), baseRefundGas);
}

private long refunded(
final Transaction transaction, final long gasRemaining, final long gasRefund) {
// Integer truncation takes care of the floor calculation needed after the divide.
final long maxRefundAllowance =
(transaction.getGasLimit() - gasRemaining) / getMaxRefundQuotient();
final long refundAllowance = Math.min(maxRefundAllowance, gasRefund);
return gasRemaining + refundAllowance;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import org.hyperledger.besu.datatypes.AccessListEntry;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Transaction;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.frame.MessageFrame;
Expand Down Expand Up @@ -664,4 +665,15 @@ default long delegateCodeGasCost(final int delegateCodeListLength) {
default long calculateDelegateCodeGasRefund(final long alreadyExistingAccountSize) {
return 0L;
}

/**
* Calculate the gas refund for a transaction.
*
* @param transaction the transaction
* @param initialFrame the initial frame
* @param codeDelegationRefund the code delegation refund
* @return the gas refund
*/
long calculateGasRefund(
Transaction transaction, MessageFrame initialFrame, long codeDelegationRefund);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.evm.gascalculator;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.mockito.Mockito.when;

import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Transaction;
import org.hyperledger.besu.evm.frame.MessageFrame;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import org.apache.tuweni.bytes.Bytes;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
public class FrontierGasCalculatorTest {
private final GasCalculator gasCalculator = new FrontierGasCalculator();

@Mock private Transaction transaction;
@Mock private MessageFrame messageFrame;

@Test
void shouldCalculateRefundWithNoSelfDestructs() {
// Arrange
when(messageFrame.getSelfDestructs()).thenReturn(Collections.emptySet());
when(messageFrame.getGasRefund()).thenReturn(1000L);
when(messageFrame.getRemainingGas()).thenReturn(5000L);
when(transaction.getGasLimit()).thenReturn(100000L);

// Act
long refund = gasCalculator.calculateGasRefund(transaction, messageFrame, 500L);

// Assert
assertThat(refund)
.isEqualTo(6500L); // 5000 (remaining) + min(1500 (total refund), 19000 (max allowance))
}

@Test
void shouldCalculateRefundWithMultipleSelfDestructs() {
// Arrange
Set<Address> selfDestructs = new HashSet<>();
selfDestructs.add(Address.wrap(Bytes.random(20)));
selfDestructs.add(Address.wrap(Bytes.random(20)));

when(messageFrame.getSelfDestructs()).thenReturn(selfDestructs);
when(messageFrame.getGasRefund()).thenReturn(1000L);
when(messageFrame.getRemainingGas()).thenReturn(5000L);
when(transaction.getGasLimit()).thenReturn(100000L);

// Act
long refund = gasCalculator.calculateGasRefund(transaction, messageFrame, 500L);

// Assert
assertThat(refund)
.isEqualTo(52500L); // 5000 (remaining) + min(47500 (total refund), 49500 (max allowance))
}

@Test
void shouldRespectMaxRefundAllowance() {
// Arrange
when(messageFrame.getSelfDestructs()).thenReturn(Collections.emptySet());
when(messageFrame.getGasRefund()).thenReturn(100000L);
when(messageFrame.getRemainingGas()).thenReturn(20000L);
when(transaction.getGasLimit()).thenReturn(100000L);

// Act
long refund = gasCalculator.calculateGasRefund(transaction, messageFrame, 1000L);

// Assert
assertThat(refund)
.isEqualTo(60000L); // 20000 (remaining) + min(101000 (total refund), 40000 (max allowance))
}

@Test
void shouldHandleZeroValuesCorrectly() {
// Arrange
when(messageFrame.getSelfDestructs()).thenReturn(Collections.emptySet());
when(messageFrame.getGasRefund()).thenReturn(0L);
when(messageFrame.getRemainingGas()).thenReturn(0L);
when(transaction.getGasLimit()).thenReturn(100000L);

// Act
long refund = gasCalculator.calculateGasRefund(transaction, messageFrame, 0L);

// Assert
assertThat(refund).isEqualTo(0L);
}
}