mirror of
https://github.com/vacp2p/status-linea-besu.git
synced 2026-01-08 23:08:15 -05:00
Merge branch 'main' into zkbesu
This commit is contained in:
@@ -16,7 +16,6 @@ package org.hyperledger.besu.evm.account;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
|
||||
|
||||
@@ -24,14 +23,14 @@ import java.util.Optional;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
class BaseDelegatedCodeAccount {
|
||||
abstract class AbstractDelegatedCodeAccount implements Account {
|
||||
private final WorldUpdater worldUpdater;
|
||||
private final GasCalculator gasCalculator;
|
||||
|
||||
/** The address of the account that has delegated code to be loaded into it. */
|
||||
protected final Address delegatedCodeAddress;
|
||||
|
||||
protected BaseDelegatedCodeAccount(
|
||||
protected AbstractDelegatedCodeAccount(
|
||||
final WorldUpdater worldUpdater,
|
||||
final Address delegatedCodeAddress,
|
||||
final GasCalculator gasCalculator) {
|
||||
@@ -45,7 +44,8 @@ class BaseDelegatedCodeAccount {
|
||||
*
|
||||
* @return the delegated code.
|
||||
*/
|
||||
protected Bytes getCode() {
|
||||
@Override
|
||||
public Optional<Bytes> getDelegatedCode() {
|
||||
return resolveDelegatedCode();
|
||||
}
|
||||
|
||||
@@ -54,27 +54,9 @@ class BaseDelegatedCodeAccount {
|
||||
*
|
||||
* @return the hash of the delegated code.
|
||||
*/
|
||||
protected Hash getCodeHash() {
|
||||
final Bytes code = getCode();
|
||||
return (code == null || code.isEmpty()) ? Hash.EMPTY : Hash.hash(code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the balance of the delegated account.
|
||||
*
|
||||
* @return the balance of the delegated account.
|
||||
*/
|
||||
protected Wei getDelegatedBalance() {
|
||||
return getDelegatedAccount().map(Account::getBalance).orElse(Wei.ZERO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the nonce of the delegated account.
|
||||
*
|
||||
* @return the nonce of the delegated account.
|
||||
*/
|
||||
protected long getDelegatedNonce() {
|
||||
return getDelegatedAccount().map(Account::getNonce).orElse(Account.DEFAULT_NONCE);
|
||||
@Override
|
||||
public Optional<Hash> getDelegatedCodeHash() {
|
||||
return getDelegatedCode().map(Hash::hash);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,19 +64,27 @@ class BaseDelegatedCodeAccount {
|
||||
*
|
||||
* @return the address of the delegated code.
|
||||
*/
|
||||
protected Optional<Address> delegatedCodeAddress() {
|
||||
@Override
|
||||
public Optional<Address> delegatedCodeAddress() {
|
||||
return Optional.of(delegatedCodeAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasDelegatedCode() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private Optional<Account> getDelegatedAccount() {
|
||||
return Optional.ofNullable(worldUpdater.getAccount(delegatedCodeAddress));
|
||||
}
|
||||
|
||||
private Bytes resolveDelegatedCode() {
|
||||
if (gasCalculator.isPrecompile(delegatedCodeAddress)) {
|
||||
return Bytes.EMPTY;
|
||||
private Optional<Bytes> resolveDelegatedCode() {
|
||||
final Optional<Account> maybeDelegatedAccount = getDelegatedAccount();
|
||||
|
||||
if (gasCalculator.isPrecompile(delegatedCodeAddress) || maybeDelegatedAccount.isEmpty()) {
|
||||
return Optional.of(Bytes.EMPTY);
|
||||
}
|
||||
|
||||
return getDelegatedAccount().map(Account::getUnprocessedCode).orElse(Bytes.EMPTY);
|
||||
return Optional.of(maybeDelegatedAccount.get().getCode());
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,6 @@ import org.hyperledger.besu.datatypes.Wei;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
/**
|
||||
* A world state account.
|
||||
*
|
||||
@@ -71,13 +69,4 @@ public interface Account extends AccountState {
|
||||
default boolean hasDelegatedCode() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the code as it is stored in the trie even if it's a delegated code account.
|
||||
*
|
||||
* @return the code as it is stored in the trie.
|
||||
*/
|
||||
default Bytes getUnprocessedCode() {
|
||||
return getCode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
|
||||
import java.util.NavigableMap;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
@@ -74,6 +75,15 @@ public interface AccountState {
|
||||
*/
|
||||
Bytes getCode();
|
||||
|
||||
/**
|
||||
* The optional EVM bytecode if the account has set a 7702 code delegation.
|
||||
*
|
||||
* @return the delegated code (which may be empty).
|
||||
*/
|
||||
default Optional<Bytes> getDelegatedCode() {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* The hash of the EVM bytecode associated with this account.
|
||||
*
|
||||
@@ -81,6 +91,15 @@ public interface AccountState {
|
||||
*/
|
||||
Hash getCodeHash();
|
||||
|
||||
/**
|
||||
* The optional hash of the delegated EVM bytecode if the account has set a 7702 code delegation.
|
||||
*
|
||||
* @return the hash of the delegated code (which may be empty).
|
||||
*/
|
||||
default Optional<Hash> getDelegatedCodeHash() {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the account has (non empty) EVM bytecode associated to it.
|
||||
*
|
||||
|
||||
@@ -28,7 +28,7 @@ import org.apache.tuweni.bytes.Bytes32;
|
||||
import org.apache.tuweni.units.bigints.UInt256;
|
||||
|
||||
/** Wraps an EOA account and includes delegated code to be run on behalf of it. */
|
||||
public class DelegatedCodeAccount extends BaseDelegatedCodeAccount implements Account {
|
||||
public class DelegatedCodeAccount extends AbstractDelegatedCodeAccount implements Account {
|
||||
|
||||
private final Account wrappedAccount;
|
||||
|
||||
@@ -81,17 +81,12 @@ public class DelegatedCodeAccount extends BaseDelegatedCodeAccount implements Ac
|
||||
|
||||
@Override
|
||||
public Bytes getCode() {
|
||||
return super.getCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bytes getUnprocessedCode() {
|
||||
return wrappedAccount.getCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hash getCodeHash() {
|
||||
return super.getCodeHash();
|
||||
return wrappedAccount.getCodeHash();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -106,7 +101,7 @@ public class DelegatedCodeAccount extends BaseDelegatedCodeAccount implements Ac
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return getDelegatedNonce() == 0 && getDelegatedBalance().isZero() && !hasCode();
|
||||
return wrappedAccount.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -119,9 +114,4 @@ public class DelegatedCodeAccount extends BaseDelegatedCodeAccount implements Ac
|
||||
final Bytes32 startKeyHash, final int limit) {
|
||||
return wrappedAccount.storageEntriesFrom(startKeyHash, limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasDelegatedCode() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import org.apache.tuweni.bytes.Bytes32;
|
||||
import org.apache.tuweni.units.bigints.UInt256;
|
||||
|
||||
/** Wraps an EOA account and includes delegated code to be run on behalf of it. */
|
||||
public class MutableDelegatedCodeAccount extends BaseDelegatedCodeAccount
|
||||
public class MutableDelegatedCodeAccount extends AbstractDelegatedCodeAccount
|
||||
implements MutableAccount {
|
||||
|
||||
private final MutableAccount wrappedAccount;
|
||||
@@ -83,17 +83,12 @@ public class MutableDelegatedCodeAccount extends BaseDelegatedCodeAccount
|
||||
|
||||
@Override
|
||||
public Bytes getCode() {
|
||||
return super.getCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bytes getUnprocessedCode() {
|
||||
return wrappedAccount.getCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Hash getCodeHash() {
|
||||
return super.getCodeHash();
|
||||
return wrappedAccount.getCodeHash();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -108,7 +103,7 @@ public class MutableDelegatedCodeAccount extends BaseDelegatedCodeAccount
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return getDelegatedNonce() == 0 && getDelegatedBalance().isZero() && !hasCode();
|
||||
return wrappedAccount.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -156,9 +151,4 @@ public class MutableDelegatedCodeAccount extends BaseDelegatedCodeAccount
|
||||
public void becomeImmutable() {
|
||||
wrappedAccount.becomeImmutable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasDelegatedCode() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,8 @@ public class FrontierGasCalculator implements GasCalculator {
|
||||
|
||||
private static final long TX_DATA_NON_ZERO_COST = 68L;
|
||||
|
||||
private static final long TX_BASE_COST = 21_000L;
|
||||
/** Minimum base cost that every transaction needs to pay */
|
||||
protected static final long TX_BASE_COST = 21_000L;
|
||||
|
||||
private static final long TX_CREATE_EXTRA_COST = 0L;
|
||||
|
||||
@@ -129,18 +130,82 @@ public class FrontierGasCalculator implements GasCalculator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long transactionIntrinsicGasCost(final Bytes payload, final boolean isContractCreate) {
|
||||
public long transactionIntrinsicGasCost(
|
||||
final Bytes payload, final boolean isContractCreation, final long baselineGas) {
|
||||
final long dynamicIntrinsicGasCost =
|
||||
dynamicIntrinsicGasCost(payload, isContractCreation, baselineGas);
|
||||
|
||||
if (dynamicIntrinsicGasCost == Long.MIN_VALUE || dynamicIntrinsicGasCost == Long.MAX_VALUE) {
|
||||
return dynamicIntrinsicGasCost;
|
||||
}
|
||||
return clampedAdd(getMinimumTransactionCost(), dynamicIntrinsicGasCost);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the dynamic part of the intrinsic gas cost
|
||||
*
|
||||
* @param payload the call data payload
|
||||
* @param isContractCreation whether the transaction is a contract creation
|
||||
* @param baselineGas how much gas is used by access lists and code delegations
|
||||
* @return the dynamic part of the intrinsic gas cost
|
||||
*/
|
||||
protected long dynamicIntrinsicGasCost(
|
||||
final Bytes payload, final boolean isContractCreation, final long baselineGas) {
|
||||
final int payloadSize = payload.size();
|
||||
final long zeroBytes = zeroBytes(payload);
|
||||
long cost = clampedAdd(callDataCost(payloadSize, zeroBytes), baselineGas);
|
||||
|
||||
if (cost == Long.MIN_VALUE || cost == Long.MAX_VALUE) {
|
||||
return cost;
|
||||
}
|
||||
|
||||
if (isContractCreation) {
|
||||
cost = clampedAdd(cost, contractCreationCost(payloadSize));
|
||||
|
||||
if (cost == Long.MIN_VALUE || cost == Long.MAX_VALUE) {
|
||||
return cost;
|
||||
}
|
||||
}
|
||||
|
||||
return cost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the cost of the call data
|
||||
*
|
||||
* @param payloadSize the total size of the payload
|
||||
* @param zeroBytes the number of zero bytes in the payload
|
||||
* @return the cost of the call data
|
||||
*/
|
||||
protected long callDataCost(final long payloadSize, final long zeroBytes) {
|
||||
return clampedAdd(
|
||||
TX_DATA_NON_ZERO_COST * (payloadSize - zeroBytes), TX_DATA_ZERO_COST * zeroBytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the zero bytes in the payload
|
||||
*
|
||||
* @param payload the payload
|
||||
* @return the number of zero bytes in the payload
|
||||
*/
|
||||
protected static long zeroBytes(final Bytes payload) {
|
||||
int zeros = 0;
|
||||
for (int i = 0; i < payload.size(); i++) {
|
||||
if (payload.get(i) == 0) {
|
||||
++zeros;
|
||||
zeros += 1;
|
||||
}
|
||||
}
|
||||
final int nonZeros = payload.size() - zeros;
|
||||
return zeros;
|
||||
}
|
||||
|
||||
final long cost = TX_BASE_COST + TX_DATA_ZERO_COST * zeros + TX_DATA_NON_ZERO_COST * nonZeros;
|
||||
|
||||
return isContractCreate ? (cost + txCreateExtraGasCost()) : cost;
|
||||
/**
|
||||
* Returns the gas cost for contract creation transactions
|
||||
*
|
||||
* @param ignored the size of the contract creation code (ignored in Frontier)
|
||||
* @return the gas cost for contract creation transactions
|
||||
*/
|
||||
protected long contractCreationCost(final int ignored) {
|
||||
return txCreateExtraGasCost();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,6 +217,11 @@ public class FrontierGasCalculator implements GasCalculator {
|
||||
return TX_CREATE_EXTRA_COST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long transactionFloorCost(final Bytes payload) {
|
||||
return 0L;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long codeDepositGasCost(final int codeSize) {
|
||||
return CODE_DEPOSIT_BYTE_COST * codeSize;
|
||||
@@ -558,11 +628,6 @@ public class FrontierGasCalculator implements GasCalculator {
|
||||
return clampedAdd(clampedMultiply(MEMORY_WORD_GAS_COST, length), base);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMaximumTransactionCost(final int size) {
|
||||
return TX_BASE_COST + TX_DATA_NON_ZERO_COST * size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMinimumTransactionCost() {
|
||||
return TX_BASE_COST;
|
||||
@@ -572,20 +637,21 @@ public class FrontierGasCalculator implements GasCalculator {
|
||||
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);
|
||||
final long ignoredCodeDelegationRefund) {
|
||||
|
||||
final long refundAllowance = calculateRefundAllowance(transaction, initialFrame);
|
||||
|
||||
return initialFrame.getRemainingGas() + refundAllowance;
|
||||
}
|
||||
|
||||
private long refunded(
|
||||
final Transaction transaction, final long gasRemaining, final long gasRefund) {
|
||||
private long calculateRefundAllowance(
|
||||
final Transaction transaction, final MessageFrame initialFrame) {
|
||||
final long selfDestructRefund =
|
||||
getSelfDestructRefundAmount() * initialFrame.getSelfDestructs().size();
|
||||
final long executionRefund = initialFrame.getGasRefund() + selfDestructRefund;
|
||||
// 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;
|
||||
(transaction.getGasLimit() - initialFrame.getRemainingGas()) / getMaxRefundQuotient();
|
||||
return Math.min(executionRefund, maxRefundAllowance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -543,10 +543,21 @@ public interface GasCalculator {
|
||||
* encoded binary representation when stored on-chain.
|
||||
*
|
||||
* @param transactionPayload The encoded transaction, as bytes
|
||||
* @param isContractCreate Is this transaction a contract creation transaction?
|
||||
* @param isContractCreation Is this transaction a contract creation transaction?
|
||||
* @param baselineGas The gas used by access lists and code delegation authorizations
|
||||
* @return the transaction's intrinsic gas cost
|
||||
*/
|
||||
long transactionIntrinsicGasCost(Bytes transactionPayload, boolean isContractCreate);
|
||||
long transactionIntrinsicGasCost(
|
||||
Bytes transactionPayload, boolean isContractCreation, long baselineGas);
|
||||
|
||||
/**
|
||||
* Returns the floor gas cost of a transaction payload, i.e. the minimum gas cost that a
|
||||
* transaction will be charged based on its calldata. Introduced in EIP-7623 in Prague.
|
||||
*
|
||||
* @param transactionPayload The encoded transaction, as bytes
|
||||
* @return the transaction's floor gas cost
|
||||
*/
|
||||
long transactionFloorCost(final Bytes transactionPayload);
|
||||
|
||||
/**
|
||||
* Returns the gas cost of the explicitly declared access list.
|
||||
@@ -580,15 +591,6 @@ public interface GasCalculator {
|
||||
return 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maximum Cost of a Transaction of a certain length.
|
||||
*
|
||||
* @param size the length of the transaction, in bytes
|
||||
* @return the maximum gas cost
|
||||
*/
|
||||
// what would be the gas for a PMT with hash of all non-zeros
|
||||
long getMaximumTransactionCost(int size);
|
||||
|
||||
/**
|
||||
* Minimum gas cost of a transaction.
|
||||
*
|
||||
|
||||
@@ -14,9 +14,10 @@
|
||||
*/
|
||||
package org.hyperledger.besu.evm.gascalculator;
|
||||
|
||||
import static org.hyperledger.besu.evm.internal.Words.clampedAdd;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.units.bigints.UInt256;
|
||||
|
||||
/** The Istanbul gas calculator. */
|
||||
@@ -24,7 +25,6 @@ public class IstanbulGasCalculator extends PetersburgGasCalculator {
|
||||
|
||||
private static final long TX_DATA_ZERO_COST = 4L;
|
||||
private static final long ISTANBUL_TX_DATA_NON_ZERO_COST = 16L;
|
||||
private static final long TX_BASE_COST = 21_000L;
|
||||
|
||||
private static final long SLOAD_GAS = 800L;
|
||||
private static final long BALANCE_OPERATION_GAS_COST = 700L;
|
||||
@@ -42,19 +42,9 @@ public class IstanbulGasCalculator extends PetersburgGasCalculator {
|
||||
public IstanbulGasCalculator() {}
|
||||
|
||||
@Override
|
||||
public long transactionIntrinsicGasCost(final Bytes payload, final boolean isContractCreation) {
|
||||
int zeros = 0;
|
||||
for (int i = 0; i < payload.size(); i++) {
|
||||
if (payload.get(i) == 0) {
|
||||
++zeros;
|
||||
}
|
||||
}
|
||||
final int nonZeros = payload.size() - zeros;
|
||||
|
||||
final long cost =
|
||||
TX_BASE_COST + (TX_DATA_ZERO_COST * zeros) + (ISTANBUL_TX_DATA_NON_ZERO_COST * nonZeros);
|
||||
|
||||
return isContractCreation ? (cost + txCreateExtraGasCost()) : cost;
|
||||
protected long callDataCost(final long payloadSize, final long zeroBytes) {
|
||||
return clampedAdd(
|
||||
ISTANBUL_TX_DATA_NON_ZERO_COST * (payloadSize - zeroBytes), TX_DATA_ZERO_COST * zeroBytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -136,9 +126,4 @@ public class IstanbulGasCalculator extends PetersburgGasCalculator {
|
||||
public long extCodeHashOperationGasCost() {
|
||||
return EXTCODE_HASH_COST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMaximumTransactionCost(final int size) {
|
||||
return TX_BASE_COST + (ISTANBUL_TX_DATA_NON_ZERO_COST * size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,13 @@
|
||||
package org.hyperledger.besu.evm.gascalculator;
|
||||
|
||||
import static org.hyperledger.besu.datatypes.Address.BLS12_MAP_FP2_TO_G2;
|
||||
import static org.hyperledger.besu.evm.internal.Words.clampedAdd;
|
||||
|
||||
import org.hyperledger.besu.datatypes.CodeDelegation;
|
||||
import org.hyperledger.besu.datatypes.Transaction;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
/**
|
||||
* Gas Calculator for Prague
|
||||
@@ -26,6 +31,8 @@ import org.hyperledger.besu.datatypes.CodeDelegation;
|
||||
* </UL>
|
||||
*/
|
||||
public class PragueGasCalculator extends CancunGasCalculator {
|
||||
private static final long TOTAL_COST_FLOOR_PER_TOKEN = 10L;
|
||||
|
||||
final long existingAccountGasRefund;
|
||||
|
||||
/**
|
||||
@@ -68,4 +75,47 @@ public class PragueGasCalculator extends CancunGasCalculator {
|
||||
public long calculateDelegateCodeGasRefund(final long alreadyExistingAccounts) {
|
||||
return existingAccountGasRefund * alreadyExistingAccounts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long calculateGasRefund(
|
||||
final Transaction transaction,
|
||||
final MessageFrame initialFrame,
|
||||
final long codeDelegationRefund) {
|
||||
|
||||
final long refundAllowance =
|
||||
calculateRefundAllowance(transaction, initialFrame, codeDelegationRefund);
|
||||
|
||||
final long executionGasUsed =
|
||||
transaction.getGasLimit() - initialFrame.getRemainingGas() - refundAllowance;
|
||||
final long transactionFloorCost = transactionFloorCost(transaction.getPayload());
|
||||
final long totalGasUsed = Math.max(executionGasUsed, transactionFloorCost);
|
||||
return transaction.getGasLimit() - totalGasUsed;
|
||||
}
|
||||
|
||||
private long calculateRefundAllowance(
|
||||
final Transaction transaction,
|
||||
final MessageFrame initialFrame,
|
||||
final long codeDelegationRefund) {
|
||||
final long selfDestructRefund =
|
||||
getSelfDestructRefundAmount() * initialFrame.getSelfDestructs().size();
|
||||
final long executionRefund =
|
||||
initialFrame.getGasRefund() + selfDestructRefund + codeDelegationRefund;
|
||||
// Integer truncation takes care of the floor calculation needed after the divide.
|
||||
final long maxRefundAllowance =
|
||||
(transaction.getGasLimit() - initialFrame.getRemainingGas()) / getMaxRefundQuotient();
|
||||
return Math.min(executionRefund, maxRefundAllowance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long transactionFloorCost(final Bytes transactionPayload) {
|
||||
return clampedAdd(
|
||||
getMinimumTransactionCost(),
|
||||
tokensInCallData(transactionPayload.size(), zeroBytes(transactionPayload))
|
||||
* TOTAL_COST_FLOOR_PER_TOKEN);
|
||||
}
|
||||
|
||||
private long tokensInCallData(final long payloadSize, final long zeroBytes) {
|
||||
// as defined in https://eips.ethereum.org/EIPS/eip-7623#specification
|
||||
return clampedAdd(zeroBytes, (payloadSize - zeroBytes) * 4);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,11 +14,8 @@
|
||||
*/
|
||||
package org.hyperledger.besu.evm.gascalculator;
|
||||
|
||||
import static org.hyperledger.besu.evm.internal.Words.clampedAdd;
|
||||
import static org.hyperledger.besu.evm.internal.Words.numWords;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
/** The Shanghai gas calculator. */
|
||||
public class ShanghaiGasCalculator extends LondonGasCalculator {
|
||||
|
||||
@@ -39,13 +36,8 @@ public class ShanghaiGasCalculator extends LondonGasCalculator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long transactionIntrinsicGasCost(final Bytes payload, final boolean isContractCreation) {
|
||||
long intrinsicGasCost = super.transactionIntrinsicGasCost(payload, isContractCreation);
|
||||
if (isContractCreation) {
|
||||
return clampedAdd(intrinsicGasCost, initcodeCost(payload.size()));
|
||||
} else {
|
||||
return intrinsicGasCost;
|
||||
}
|
||||
protected long contractCreationCost(final int initCodeLength) {
|
||||
return txCreateExtraGasCost() + initcodeCost(initCodeLength);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
package org.hyperledger.besu.evm.operation;
|
||||
|
||||
import static org.hyperledger.besu.evm.internal.Words.clampedToLong;
|
||||
import static org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper.deductDelegatedCodeGasCost;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
@@ -192,13 +191,24 @@ public abstract class AbstractCallOperation extends AbstractOperation {
|
||||
|
||||
final Account contract = frame.getWorldUpdater().get(to);
|
||||
|
||||
if (contract != null) {
|
||||
final DelegatedCodeGasCostHelper.Result result =
|
||||
deductDelegatedCodeGasCost(frame, gasCalculator(), contract);
|
||||
if (result.status() != DelegatedCodeGasCostHelper.Status.SUCCESS) {
|
||||
return new Operation.OperationResult(
|
||||
result.gasCost(), ExceptionalHaltReason.INSUFFICIENT_GAS);
|
||||
if (contract != null && contract.hasDelegatedCode()) {
|
||||
if (contract.getDelegatedCode().isEmpty()) {
|
||||
throw new RuntimeException("A delegated code account must have delegated code");
|
||||
}
|
||||
|
||||
if (contract.getDelegatedCodeHash().isEmpty()) {
|
||||
throw new RuntimeException("A delegated code account must have a delegated code hash");
|
||||
}
|
||||
|
||||
final long delegatedCodeResolutionGas =
|
||||
DelegatedCodeGasCostHelper.delegatedCodeGasCost(frame, gasCalculator(), contract);
|
||||
|
||||
if (frame.getRemainingGas() < delegatedCodeResolutionGas) {
|
||||
return new Operation.OperationResult(
|
||||
delegatedCodeResolutionGas, ExceptionalHaltReason.INSUFFICIENT_GAS);
|
||||
}
|
||||
|
||||
frame.decrementRemainingGas(delegatedCodeResolutionGas);
|
||||
}
|
||||
|
||||
final Account account = frame.getWorldUpdater().get(frame.getRecipientAddress());
|
||||
@@ -218,10 +228,7 @@ public abstract class AbstractCallOperation extends AbstractOperation {
|
||||
|
||||
final Bytes inputData = frame.readMutableMemory(inputDataOffset(frame), inputDataLength(frame));
|
||||
|
||||
final Code code =
|
||||
contract == null
|
||||
? CodeV0.EMPTY_CODE
|
||||
: evm.getCode(contract.getCodeHash(), contract.getCode());
|
||||
final Code code = getCode(evm, contract);
|
||||
|
||||
// invalid code results in a quick exit
|
||||
if (!code.isValid()) {
|
||||
@@ -337,4 +344,23 @@ public abstract class AbstractCallOperation extends AbstractOperation {
|
||||
return LEGACY_FAILURE_STACK_ITEM;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the code from the contract or EOA with delegated code.
|
||||
*
|
||||
* @param evm the evm
|
||||
* @param account the account which needs to be retrieved
|
||||
* @return the code
|
||||
*/
|
||||
protected static Code getCode(final EVM evm, final Account account) {
|
||||
if (account == null) {
|
||||
return CodeV0.EMPTY_CODE;
|
||||
}
|
||||
|
||||
if (account.hasDelegatedCode()) {
|
||||
return evm.getCode(account.getDelegatedCodeHash().get(), account.getDelegatedCode().get());
|
||||
}
|
||||
|
||||
return evm.getCode(account.getCodeHash(), account.getCode());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,14 +15,12 @@
|
||||
package org.hyperledger.besu.evm.operation;
|
||||
|
||||
import static org.hyperledger.besu.evm.internal.Words.clampedAdd;
|
||||
import static org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper.deductDelegatedCodeGasCost;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Wei;
|
||||
import org.hyperledger.besu.evm.Code;
|
||||
import org.hyperledger.besu.evm.EVM;
|
||||
import org.hyperledger.besu.evm.account.Account;
|
||||
import org.hyperledger.besu.evm.code.CodeV0;
|
||||
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
@@ -127,16 +125,27 @@ public abstract class AbstractExtCallOperation extends AbstractCallOperation {
|
||||
Address to = Words.toAddress(toBytes);
|
||||
final Account contract = frame.getWorldUpdater().get(to);
|
||||
|
||||
if (contract != null) {
|
||||
final DelegatedCodeGasCostHelper.Result result =
|
||||
deductDelegatedCodeGasCost(frame, gasCalculator, contract);
|
||||
if (result.status() != DelegatedCodeGasCostHelper.Status.SUCCESS) {
|
||||
return new Operation.OperationResult(
|
||||
result.gasCost(), ExceptionalHaltReason.INSUFFICIENT_GAS);
|
||||
if (contract != null && contract.hasDelegatedCode()) {
|
||||
if (contract.getDelegatedCode().isEmpty()) {
|
||||
throw new RuntimeException("A delegated code account must have delegated code");
|
||||
}
|
||||
|
||||
if (contract.getDelegatedCodeHash().isEmpty()) {
|
||||
throw new RuntimeException("A delegated code account must have a delegated code hash");
|
||||
}
|
||||
|
||||
final long delegatedCodeResolutionGas =
|
||||
DelegatedCodeGasCostHelper.delegatedCodeGasCost(frame, gasCalculator(), contract);
|
||||
|
||||
if (frame.getRemainingGas() < delegatedCodeResolutionGas) {
|
||||
return new Operation.OperationResult(
|
||||
delegatedCodeResolutionGas, ExceptionalHaltReason.INSUFFICIENT_GAS);
|
||||
}
|
||||
|
||||
frame.decrementRemainingGas(delegatedCodeResolutionGas);
|
||||
}
|
||||
|
||||
boolean accountCreation = contract == null && !zeroValue;
|
||||
boolean accountCreation = (contract == null || contract.isEmpty()) && !zeroValue;
|
||||
long cost =
|
||||
clampedAdd(
|
||||
clampedAdd(
|
||||
@@ -154,10 +163,7 @@ public abstract class AbstractExtCallOperation extends AbstractCallOperation {
|
||||
currentGas -= cost;
|
||||
frame.expandMemory(inputOffset, inputLength);
|
||||
|
||||
final Code code =
|
||||
contract == null
|
||||
? CodeV0.EMPTY_CODE
|
||||
: evm.getCode(contract.getCodeHash(), contract.getCode());
|
||||
final Code code = getCode(evm, contract);
|
||||
|
||||
// invalid code results in a quick exit
|
||||
if (!code.isValid()) {
|
||||
|
||||
@@ -16,7 +16,6 @@ package org.hyperledger.besu.evm.operation;
|
||||
|
||||
import static org.hyperledger.besu.evm.internal.Words.clampedAdd;
|
||||
import static org.hyperledger.besu.evm.internal.Words.clampedToLong;
|
||||
import static org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper.deductDelegatedCodeGasCost;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.evm.EVM;
|
||||
@@ -26,7 +25,7 @@ import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
|
||||
import org.hyperledger.besu.evm.frame.MessageFrame;
|
||||
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
import org.hyperledger.besu.evm.internal.Words;
|
||||
import org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper;
|
||||
import org.hyperledger.besu.evm.worldstate.DelegateCodeHelper;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
@@ -96,16 +95,7 @@ public class ExtCodeCopyOperation extends AbstractOperation {
|
||||
|
||||
final Account account = frame.getWorldUpdater().get(address);
|
||||
|
||||
if (account != null) {
|
||||
final DelegatedCodeGasCostHelper.Result result =
|
||||
deductDelegatedCodeGasCost(frame, gasCalculator(), account);
|
||||
if (result.status() != DelegatedCodeGasCostHelper.Status.SUCCESS) {
|
||||
return new Operation.OperationResult(
|
||||
result.gasCost(), ExceptionalHaltReason.INSUFFICIENT_GAS);
|
||||
}
|
||||
}
|
||||
|
||||
final Bytes code = account != null ? account.getCode() : Bytes.EMPTY;
|
||||
final Bytes code = getCode(account);
|
||||
|
||||
if (enableEIP3540
|
||||
&& code.size() >= 2
|
||||
@@ -118,4 +108,14 @@ public class ExtCodeCopyOperation extends AbstractOperation {
|
||||
|
||||
return new OperationResult(cost, null);
|
||||
}
|
||||
|
||||
private static Bytes getCode(final Account account) {
|
||||
if (account == null) {
|
||||
return Bytes.EMPTY;
|
||||
}
|
||||
|
||||
return account.hasDelegatedCode()
|
||||
? DelegateCodeHelper.getDelegatedCodeForRead()
|
||||
: account.getCode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
*/
|
||||
package org.hyperledger.besu.evm.operation;
|
||||
|
||||
import static org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper.deductDelegatedCodeGasCost;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.evm.EVM;
|
||||
@@ -27,7 +25,7 @@ import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
import org.hyperledger.besu.evm.internal.OverflowException;
|
||||
import org.hyperledger.besu.evm.internal.UnderflowException;
|
||||
import org.hyperledger.besu.evm.internal.Words;
|
||||
import org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper;
|
||||
import org.hyperledger.besu.evm.worldstate.DelegateCodeHelper;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
@@ -85,19 +83,10 @@ public class ExtCodeHashOperation extends AbstractOperation {
|
||||
|
||||
final Account account = frame.getWorldUpdater().get(address);
|
||||
|
||||
if (account != null) {
|
||||
final DelegatedCodeGasCostHelper.Result result =
|
||||
deductDelegatedCodeGasCost(frame, gasCalculator(), account);
|
||||
if (result.status() != DelegatedCodeGasCostHelper.Status.SUCCESS) {
|
||||
return new Operation.OperationResult(
|
||||
result.gasCost(), ExceptionalHaltReason.INSUFFICIENT_GAS);
|
||||
}
|
||||
}
|
||||
|
||||
if (account == null || account.isEmpty()) {
|
||||
frame.pushStackItem(Bytes.EMPTY);
|
||||
} else {
|
||||
final Bytes code = account.getCode();
|
||||
final Bytes code = getCode(account);
|
||||
if (enableEIP3540
|
||||
&& code.size() >= 2
|
||||
&& code.get(0) == EOFLayout.EOF_PREFIX_BYTE
|
||||
@@ -115,4 +104,12 @@ public class ExtCodeHashOperation extends AbstractOperation {
|
||||
return new OperationResult(cost(true), ExceptionalHaltReason.TOO_MANY_STACK_ITEMS);
|
||||
}
|
||||
}
|
||||
|
||||
private static Bytes getCode(final Account account) {
|
||||
if (!account.hasDelegatedCode()) {
|
||||
return account.getCode();
|
||||
}
|
||||
|
||||
return DelegateCodeHelper.getDelegatedCodeForRead();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
*/
|
||||
package org.hyperledger.besu.evm.operation;
|
||||
|
||||
import static org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper.deductDelegatedCodeGasCost;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.evm.EVM;
|
||||
import org.hyperledger.besu.evm.account.Account;
|
||||
@@ -26,7 +24,7 @@ import org.hyperledger.besu.evm.gascalculator.GasCalculator;
|
||||
import org.hyperledger.besu.evm.internal.OverflowException;
|
||||
import org.hyperledger.besu.evm.internal.UnderflowException;
|
||||
import org.hyperledger.besu.evm.internal.Words;
|
||||
import org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper;
|
||||
import org.hyperledger.besu.evm.worldstate.DelegateCodeHelper;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
@@ -82,20 +80,11 @@ public class ExtCodeSizeOperation extends AbstractOperation {
|
||||
} else {
|
||||
final Account account = frame.getWorldUpdater().get(address);
|
||||
|
||||
if (account != null) {
|
||||
final DelegatedCodeGasCostHelper.Result result =
|
||||
deductDelegatedCodeGasCost(frame, gasCalculator(), account);
|
||||
if (result.status() != DelegatedCodeGasCostHelper.Status.SUCCESS) {
|
||||
return new Operation.OperationResult(
|
||||
result.gasCost(), ExceptionalHaltReason.INSUFFICIENT_GAS);
|
||||
}
|
||||
}
|
||||
|
||||
Bytes codeSize;
|
||||
if (account == null) {
|
||||
codeSize = Bytes.EMPTY;
|
||||
} else {
|
||||
final Bytes code = account.getCode();
|
||||
final Bytes code = getCode(account);
|
||||
if (enableEIP3540
|
||||
&& code.size() >= 2
|
||||
&& code.get(0) == EOFLayout.EOF_PREFIX_BYTE
|
||||
@@ -114,4 +103,14 @@ public class ExtCodeSizeOperation extends AbstractOperation {
|
||||
return new OperationResult(cost(true), ExceptionalHaltReason.TOO_MANY_STACK_ITEMS);
|
||||
}
|
||||
}
|
||||
|
||||
private static Bytes getCode(final Account account) {
|
||||
if (account == null) {
|
||||
return Bytes.EMPTY;
|
||||
}
|
||||
|
||||
return account.hasDelegatedCode()
|
||||
? DelegateCodeHelper.getDelegatedCodeForRead()
|
||||
: account.getCode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.worldstate;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
/** Helper class for 7702 delegated code interactions */
|
||||
public class DelegateCodeHelper {
|
||||
/**
|
||||
* The designator that is returned when a ExtCode* operation calls a contract with delegated code
|
||||
*/
|
||||
public static final Bytes DELEGATED_CODE_DESIGNATOR = Bytes.fromHexString("ef01");
|
||||
|
||||
/** The prefix that is used to identify delegated code */
|
||||
public static final Bytes DELEGATED_CODE_PREFIX = Bytes.fromHexString("ef0100");
|
||||
|
||||
/** The size of the delegated code */
|
||||
public static final int DELEGATED_CODE_SIZE = DELEGATED_CODE_PREFIX.size() + Address.SIZE;
|
||||
|
||||
/** create a new DelegateCodeHelper */
|
||||
public DelegateCodeHelper() {
|
||||
// empty
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the provided code is delegated code.
|
||||
*
|
||||
* @param code the code to check.
|
||||
* @return {@code true} if the code is delegated code, {@code false} otherwise.
|
||||
*/
|
||||
public static boolean hasDelegatedCode(final Bytes code) {
|
||||
return code != null
|
||||
&& code.size() == DELEGATED_CODE_SIZE
|
||||
&& code.slice(0, DELEGATED_CODE_PREFIX.size()).equals(DELEGATED_CODE_PREFIX);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the delegated code designator
|
||||
*
|
||||
* @return the hardcoded designator for delegated code: ef01
|
||||
*/
|
||||
public static Bytes getDelegatedCodeForRead() {
|
||||
return DELEGATED_CODE_DESIGNATOR;
|
||||
}
|
||||
}
|
||||
@@ -33,22 +33,6 @@ public class DelegatedCodeGasCostHelper {
|
||||
// empty constructor
|
||||
}
|
||||
|
||||
/** The status of the operation. */
|
||||
public enum Status {
|
||||
/** The operation failed due to insufficient gas. */
|
||||
INSUFFICIENT_GAS,
|
||||
/** The operation was successful. */
|
||||
SUCCESS
|
||||
}
|
||||
|
||||
/**
|
||||
* The result of the operation.
|
||||
*
|
||||
* @param gasCost the gas cost
|
||||
* @param status of the operation
|
||||
*/
|
||||
public record Result(long gasCost, Status status) {}
|
||||
|
||||
/**
|
||||
* Deducts the gas cost for delegated code resolution.
|
||||
*
|
||||
@@ -57,26 +41,18 @@ public class DelegatedCodeGasCostHelper {
|
||||
* @param account the account
|
||||
* @return the gas cost and result of the operation
|
||||
*/
|
||||
public static Result deductDelegatedCodeGasCost(
|
||||
public static long delegatedCodeGasCost(
|
||||
final MessageFrame frame, final GasCalculator gasCalculator, final Account account) {
|
||||
if (!account.hasDelegatedCode()) {
|
||||
return new Result(0, Status.SUCCESS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (account.delegatedCodeAddress().isEmpty()) {
|
||||
throw new RuntimeException("A delegated code account must have a delegated code address");
|
||||
}
|
||||
|
||||
final long delegatedCodeResolutionGas =
|
||||
calculateDelegatedCodeResolutionGas(
|
||||
frame, gasCalculator, account.delegatedCodeAddress().get());
|
||||
|
||||
if (frame.getRemainingGas() < delegatedCodeResolutionGas) {
|
||||
return new Result(delegatedCodeResolutionGas, Status.INSUFFICIENT_GAS);
|
||||
}
|
||||
|
||||
frame.decrementRemainingGas(delegatedCodeResolutionGas);
|
||||
return new Result(delegatedCodeResolutionGas, Status.SUCCESS);
|
||||
return calculateDelegatedCodeResolutionGas(
|
||||
frame, gasCalculator, account.delegatedCodeAddress().get());
|
||||
}
|
||||
|
||||
private static long calculateDelegatedCodeResolutionGas(
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
*/
|
||||
package org.hyperledger.besu.evm.worldstate;
|
||||
|
||||
import static org.hyperledger.besu.evm.worldstate.DelegateCodeHelper.DELEGATED_CODE_PREFIX;
|
||||
import static org.hyperledger.besu.evm.worldstate.DelegateCodeHelper.hasDelegatedCode;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.evm.account.Account;
|
||||
import org.hyperledger.besu.evm.account.DelegatedCodeAccount;
|
||||
@@ -25,8 +28,6 @@ import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
/** A service that manages the code injection of delegated code. */
|
||||
public class DelegatedCodeService {
|
||||
private static final Bytes DELEGATED_CODE_PREFIX = Bytes.fromHexString("ef0100");
|
||||
private static final int DELEGATED_CODE_SIZE = DELEGATED_CODE_PREFIX.size() + Address.SIZE;
|
||||
|
||||
private final GasCalculator gasCalculator;
|
||||
|
||||
@@ -64,7 +65,7 @@ public class DelegatedCodeService {
|
||||
* @return {@code true} if the account can set delegated code, {@code false} otherwise.
|
||||
*/
|
||||
public boolean canSetDelegatedCode(final Account account) {
|
||||
return account.getCode().isEmpty() || hasDelegatedCode(account.getUnprocessedCode());
|
||||
return account.getCode().isEmpty() || hasDelegatedCode(account.getCode());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -102,18 +103,6 @@ public class DelegatedCodeService {
|
||||
worldUpdater, account, resolveDelegatedAddress(account.getCode()), gasCalculator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the provided code is delegated code.
|
||||
*
|
||||
* @param code the code to check.
|
||||
* @return {@code true} if the code is delegated code, {@code false} otherwise.
|
||||
*/
|
||||
public static boolean hasDelegatedCode(final Bytes code) {
|
||||
return code != null
|
||||
&& code.size() == DELEGATED_CODE_SIZE
|
||||
&& code.slice(0, DELEGATED_CODE_PREFIX.size()).equals(DELEGATED_CODE_PREFIX);
|
||||
}
|
||||
|
||||
private Address resolveDelegatedAddress(final Bytes code) {
|
||||
return Address.wrap(code.slice(DELEGATED_CODE_PREFIX.size()));
|
||||
}
|
||||
|
||||
@@ -51,11 +51,11 @@ public class FrontierGasCalculatorTest {
|
||||
|
||||
// Assert
|
||||
assertThat(refund)
|
||||
.isEqualTo(6500L); // 5000 (remaining) + min(1500 (total refund), 19000 (max allowance))
|
||||
.isEqualTo(6000L); // 5000 (remaining) + min(1000 (execution refund), 47500 (max allowance))
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCalculateRefundWithMultipleSelfDestructs() {
|
||||
void shouldCalculateRefundWithMultipleSelfDestructsAndIgnoreCodeDelegation() {
|
||||
// Arrange
|
||||
Set<Address> selfDestructs = new HashSet<>();
|
||||
selfDestructs.add(Address.wrap(Bytes.random(20)));
|
||||
@@ -71,7 +71,8 @@ public class FrontierGasCalculatorTest {
|
||||
|
||||
// Assert
|
||||
assertThat(refund)
|
||||
.isEqualTo(52500L); // 5000 (remaining) + min(47500 (total refund), 49500 (max allowance))
|
||||
.isEqualTo(
|
||||
52500L); // 5000 (remaining) + min(49500 (execution refund), 47500 (max allowance))
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -87,7 +88,8 @@ public class FrontierGasCalculatorTest {
|
||||
|
||||
// Assert
|
||||
assertThat(refund)
|
||||
.isEqualTo(60000L); // 20000 (remaining) + min(101000 (total refund), 40000 (max allowance))
|
||||
.isEqualTo(
|
||||
60000L); // 20000 (remaining) + min(101000 (execution refund), 40000 (max allowance))
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -99,9 +101,23 @@ public class FrontierGasCalculatorTest {
|
||||
when(transaction.getGasLimit()).thenReturn(100000L);
|
||||
|
||||
// Act
|
||||
long refund = gasCalculator.calculateGasRefund(transaction, messageFrame, 0L);
|
||||
long refund =
|
||||
gasCalculator.calculateGasRefund(
|
||||
transaction,
|
||||
messageFrame,
|
||||
0L); // 0 (remaining) + min(0 (execution refund), 50000 (max allowance))
|
||||
|
||||
// Assert
|
||||
assertThat(refund).isEqualTo(0L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void transactionFloorCostShouldAlwaysBeZero() {
|
||||
assertThat(gasCalculator.transactionFloorCost(Bytes.EMPTY)).isEqualTo(0L);
|
||||
assertThat(gasCalculator.transactionFloorCost(Bytes.random(256))).isEqualTo(0L);
|
||||
assertThat(gasCalculator.transactionFloorCost(Bytes.repeat((byte) 0x0, Integer.MAX_VALUE)))
|
||||
.isEqualTo(0L);
|
||||
assertThat(gasCalculator.transactionFloorCost(Bytes.repeat((byte) 0x1, Integer.MAX_VALUE)))
|
||||
.isEqualTo(0L);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,22 +15,36 @@
|
||||
package org.hyperledger.besu.evm.gascalculator;
|
||||
|
||||
import static org.assertj.core.api.Assertions.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.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.assertj.core.api.AssertionsForClassTypes;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class PragueGasCalculatorTest {
|
||||
|
||||
private static final long TARGET_BLOB_GAS_PER_BLOCK_PRAGUE = 0xC0000;
|
||||
private final PragueGasCalculator pragueGasCalculator = new PragueGasCalculator();
|
||||
@Mock private Transaction transaction;
|
||||
@Mock private MessageFrame messageFrame;
|
||||
|
||||
@Test
|
||||
void testPrecompileSize() {
|
||||
@@ -66,4 +80,125 @@ class PragueGasCalculatorTest {
|
||||
pragueGasCalculator.getBlobGasPerBlob()),
|
||||
Arguments.of(sixBlobTargetGas, newTargetCount, sixBlobTargetGas));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCalculateRefundWithCodeDelegationAndNoSelfDestructs() {
|
||||
// Arrange
|
||||
when(messageFrame.getSelfDestructs()).thenReturn(Collections.emptySet());
|
||||
when(messageFrame.getGasRefund()).thenReturn(1000L);
|
||||
when(messageFrame.getRemainingGas()).thenReturn(5000L);
|
||||
when(transaction.getGasLimit()).thenReturn(100000L);
|
||||
when(transaction.getPayload()).thenReturn(Bytes.EMPTY);
|
||||
|
||||
// Act
|
||||
long refund = pragueGasCalculator.calculateGasRefund(transaction, messageFrame, 500L);
|
||||
|
||||
// Assert
|
||||
// execution refund = 1000 + 0 (self destructs) + 500 (code delegation) = 1500
|
||||
AssertionsForClassTypes.assertThat(refund)
|
||||
.isEqualTo(6500L); // 5000 (remaining) + min(1500 (execution 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);
|
||||
when(transaction.getPayload()).thenReturn(Bytes.EMPTY);
|
||||
|
||||
// Act
|
||||
long refund = pragueGasCalculator.calculateGasRefund(transaction, messageFrame, 1000L);
|
||||
|
||||
// Assert
|
||||
// execution refund = 1000 + 0 (self destructs EIP-3529) + 1000 (code delegation) = 2000
|
||||
AssertionsForClassTypes.assertThat(refund)
|
||||
.isEqualTo(7000L); // 5000 (remaining) + min(2000 (execution refund), 1900 (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);
|
||||
when(transaction.getPayload()).thenReturn(Bytes.EMPTY);
|
||||
|
||||
// Act
|
||||
long refund = pragueGasCalculator.calculateGasRefund(transaction, messageFrame, 1000L);
|
||||
|
||||
// Assert
|
||||
// execution refund = 100000 + 1000 (code delegation) = 101000
|
||||
AssertionsForClassTypes.assertThat(refund)
|
||||
.isEqualTo(
|
||||
36000L); // 20000 (remaining) + min(101000 (execution refund), 16000 (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);
|
||||
when(transaction.getPayload()).thenReturn(Bytes.EMPTY);
|
||||
|
||||
// Act
|
||||
long refund =
|
||||
pragueGasCalculator.calculateGasRefund(
|
||||
transaction,
|
||||
messageFrame,
|
||||
0L); // 0 (remaining) + min(0 (execution refund), 20000 (max allowance))
|
||||
|
||||
// Assert
|
||||
AssertionsForClassTypes.assertThat(refund).isEqualTo(0L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRespectTransactionFloorCost() {
|
||||
// Arrange
|
||||
when(messageFrame.getSelfDestructs()).thenReturn(Collections.emptySet());
|
||||
when(messageFrame.getGasRefund()).thenReturn(100000L);
|
||||
when(messageFrame.getRemainingGas()).thenReturn(90000L);
|
||||
when(transaction.getGasLimit()).thenReturn(100000L);
|
||||
when(transaction.getPayload()).thenReturn(Bytes.EMPTY);
|
||||
|
||||
// Act
|
||||
long refund = pragueGasCalculator.calculateGasRefund(transaction, messageFrame, 1000L);
|
||||
|
||||
// Assert
|
||||
// refund allowance = 16000
|
||||
// execution gas used = 100000 (gas limit) - 20000 (remaining gas) - 16000 (refund allowance) =
|
||||
// 64000
|
||||
// floor cost = 21000 (base cost) + 0 (tokensInCallData * floor cost per token) = 21000
|
||||
AssertionsForClassTypes.assertThat(refund)
|
||||
.isEqualTo(
|
||||
79000L); // 100000 (gas limit) - max(8000 (execution gas used), 21000 (floor cost))
|
||||
}
|
||||
|
||||
@Test
|
||||
void transactionFloorCostShouldBeAtLeastTransactionBaseCost() {
|
||||
// floor cost = 21000 (base cost) + 0
|
||||
AssertionsForClassTypes.assertThat(pragueGasCalculator.transactionFloorCost(Bytes.EMPTY))
|
||||
.isEqualTo(21000);
|
||||
// floor cost = 21000 (base cost) + 256 (tokensInCallData) * 10 (cost per token)
|
||||
AssertionsForClassTypes.assertThat(
|
||||
pragueGasCalculator.transactionFloorCost(Bytes.repeat((byte) 0x0, 256)))
|
||||
.isEqualTo(23560L);
|
||||
// floor cost = 21000 (base cost) + 256 * 4 (tokensInCallData) * 10 (cost per token)
|
||||
AssertionsForClassTypes.assertThat(
|
||||
pragueGasCalculator.transactionFloorCost(Bytes.repeat((byte) 0x1, 256)))
|
||||
.isEqualTo(31240L);
|
||||
// floor cost = 21000 (base cost) + 5 + (6 * 4) (tokensInCallData) * 10 (cost per token)
|
||||
AssertionsForClassTypes.assertThat(
|
||||
pragueGasCalculator.transactionFloorCost(
|
||||
Bytes.fromHexString("0x0001000100010001000101")))
|
||||
.isEqualTo(21290L);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user