Eip4762 fix gas costs (#7802)

Signed-off-by: Luis Pinto <luis.pinto@consensys.net>
pull/7824/head
Luis Pinto 1 month ago committed by GitHub
parent dd1d73ded9
commit d000c7a880
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 134
      datatypes/src/main/java/org/hyperledger/besu/datatypes/AccessWitness.java
  2. 15
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java
  3. 3
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/verkle/worldview/VerkleWorldState.java
  4. 2
      ethereum/referencetests/build.gradle
  5. 20
      evm/src/main/java/org/hyperledger/besu/evm/EVM.java
  6. 2
      evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java
  7. 2
      evm/src/main/java/org/hyperledger/besu/evm/fluent/EVMExecutor.java
  8. 3
      evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java
  9. 15
      evm/src/main/java/org/hyperledger/besu/evm/gascalculator/BerlinGasCalculator.java
  10. 3
      evm/src/main/java/org/hyperledger/besu/evm/gascalculator/ConstantinopleGasCalculator.java
  11. 421
      evm/src/main/java/org/hyperledger/besu/evm/gascalculator/Eip4762GasCalculator.java
  12. 13
      evm/src/main/java/org/hyperledger/besu/evm/gascalculator/FrontierGasCalculator.java
  13. 39
      evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java
  14. 9
      evm/src/main/java/org/hyperledger/besu/evm/gascalculator/IstanbulGasCalculator.java
  15. 8
      evm/src/main/java/org/hyperledger/besu/evm/gascalculator/TangerineWhistleGasCalculator.java
  16. 69
      evm/src/main/java/org/hyperledger/besu/evm/gascalculator/stateless/AccessEvents.java
  17. 49
      evm/src/main/java/org/hyperledger/besu/evm/gascalculator/stateless/AccessMode.java
  18. 489
      evm/src/main/java/org/hyperledger/besu/evm/gascalculator/stateless/Eip4762AccessWitness.java
  19. 58
      evm/src/main/java/org/hyperledger/besu/evm/gascalculator/stateless/NoopAccessWitness.java
  20. 3
      evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCreateOperation.java
  21. 41
      evm/src/main/java/org/hyperledger/besu/evm/operation/BalanceOperation.java
  22. 26
      evm/src/main/java/org/hyperledger/besu/evm/operation/CallCodeOperation.java
  23. 7
      evm/src/main/java/org/hyperledger/besu/evm/operation/Create2Operation.java
  24. 7
      evm/src/main/java/org/hyperledger/besu/evm/operation/CreateOperation.java
  25. 7
      evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCreateOperation.java
  26. 1
      evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeCopyOperation.java
  27. 75
      evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeHashOperation.java
  28. 75
      evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeSizeOperation.java
  29. 2
      evm/src/main/java/org/hyperledger/besu/evm/operation/PushOperation.java
  30. 2
      evm/src/main/java/org/hyperledger/besu/evm/operation/SLoadOperation.java
  31. 3
      evm/src/main/java/org/hyperledger/besu/evm/processor/ContractCreationProcessor.java
  32. 6
      evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java

@ -14,40 +14,122 @@
*/
package org.hyperledger.besu.datatypes;
import java.util.List;
import java.util.Optional;
import org.apache.tuweni.units.bigints.UInt256;
/**
* Manages and records all accesses to the witness for stateless access to the storage and it might
* return back the corresponding gas charges for such access.
*/
public interface AccessWitness {
List<Address> keys();
/**
* Touch witness for all required accesses during a CALL when value is transferred.
*
* @param caller address of whom is doing the CALL operation
* @param target address of the account being called
* @param isAccountCreation will there be any account creation as a result of the operation
* @param warmReadCost cost to deduct in case the witness was touched before with {@code target}
* address
* @return stateless costs associated with accesses
*/
long touchAndChargeValueTransfer(
Address caller, Address target, boolean isAccountCreation, long warmReadCost);
/**
* Touch witness for all required accesses during a SELFDESTRUCT when value is transferred.
*
* @param caller address of whom is doing the SELFDESTRUCT operation
* @param target address of the account beneficiary
* @param isAccountCreation will there be any account creation as a result of the operation
* @param warmReadCost cost to deduct in case the witness was touched before with {@code target}
* address
* @return stateless costs associated with accesses
*/
long touchAndChargeValueTransferSelfDestruct(
Address caller, Address target, boolean isAccountCreation, long warmReadCost);
/**
* Touch witness as if accessing the {@code leafKey} index on the 0x0 branch index in the trie of
* an account.
*
* @param address address of the account to record the read access
* @param leafKey leaf index of the tree to record the read access
* @return stateless costs associated with accesses
*/
long touchAddressAndChargeRead(Address address, UInt256 leafKey);
/**
* Touch witness for all required accesses during a base transaction from an EOA. This does not
* return any cost because there are no charges.
*
* @param origin EOA from which transaction originates
* @param target address of the account being called
* @param value value being transferred as part of the transaction
*/
void touchBaseTx(Address origin, Optional<Address> target, Wei value);
/**
* Touch witness for checking if a contract address already exists upon contract creation.
*
* @param address contract address being created
* @return stateless costs associated with access
*/
long touchAndChargeProofOfAbsence(Address address);
long touchAndChargeValueTransfer(Address caller, Address target);
long touchAndChargeMessageCall(Address address);
long touchTxOriginAndComputeGas(Address origin);
long touchTxExistingAndComputeGas(Address target, boolean sendsValue);
long touchAndChargeContractCreateInit(Address address, boolean createSendsValue);
long touchAndChargeContractCreateCompleted(final Address address);
long touchAddressOnWriteAndComputeGas(Address address, UInt256 treeIndex, UInt256 subIndex);
long touchAddressOnReadAndComputeGas(Address address, UInt256 treeIndex, UInt256 subIndex);
List<UInt256> getStorageSlotTreeIndexes(UInt256 storageKey);
/**
* Touch witness for all required accesses after a contract has been deposited in the account.
* This implies all accesses required to finish contract creation after code has been added stored
* in the account.
*
* @param address where the contract is being deployed
* @return stateless costs associated with accesses
*/
long touchCodeChunksUponContractCreation(Address address, long codeLength);
long touchCodeChunks(Address address, long offset, long readSize, long codeLength);
/**
* Touch witness for all required accesses for completing contract creation. This does not include
* accesses related to the contract's code being deposited in the account.
*
* @param address where the contract is deployed
* @return stateless costs associated with accesses
*/
long touchAndChargeContractCreateCompleted(final Address address);
default long touchCodeChunksWithoutAccessCost(
final Address address, final long offset, final long readSize, final long codeLength) {
return touchCodeChunks(address, offset, readSize, codeLength);
}
/**
* Touch witness for read access to a given account's storage location during an SLOAD operation.
*
* @param address account address to read the storage for
* @param storageKey key to the account storage location in the trie
* @return stateless costs associated with storage access for the given key
*/
long touchAndChargeStorageLoad(Address address, UInt256 storageKey);
/**
* Touch witness for write access to a given account's storage location during an SSTORE
* operation. Depending on whether a storage location already holds a value, write access will be
* different for either setting or resetting the trie leaf holding the value.
*
* @param address account address to write the storage for
* @param storageKey key to the account storage location in the trie
* @param hasPreviousValue true if account previously stored a value at the given location, false
* otherwise
* @return stateless costs associated with storage access for the given key
*/
long touchAndChargeStorageStore(Address address, UInt256 storageKey, boolean hasPreviousValue);
/**
* Touch witness for reading code chunks of an account.
*
* @param address contract address to read the code for
* @param isContractInDeployment whether contract, which code is being read, is being deployed in
* the current transaction
* @param offset index of the code that the read event starting from
* @param readSize how many bytes are being read from the contract's code
* @param codeLength total size of the contract's code
* @return stateless costs associated with accesses
*/
long touchCodeChunks(
Address address, boolean isContractInDeployment, long offset, long readSize, long codeLength);
}

@ -379,22 +379,15 @@ public class MainnetTransactionProcessor {
if (warmCoinbase) {
warmAddressList.add(miningBeneficiary);
}
final AccessWitness accessWitness = gasCalculator.newAccessWitness();
final long intrinsicGas =
gasCalculator.transactionIntrinsicGasCost(
transaction.getPayload(), transaction.isContractCreation());
final long accessListGas =
gasCalculator.accessListGasCost(accessListEntries.size(), accessListStorageCount);
final long accessEventCost =
gasCalculator.computeBaseAccessEventsCost(accessWitness, transaction, sender);
final long codeDelegationGas =
gasCalculator.delegateCodeGasCost(transaction.codeDelegationListSize());
final long gasAvailable =
transaction.getGasLimit()
- intrinsicGas
- accessListGas
- codeDelegationGas
- accessEventCost;
transaction.getGasLimit() - intrinsicGas - accessListGas - codeDelegationGas;
LOG.trace(
"Gas available for execution {} = {} - {} - {} - {} (limit - intrinsic - accessList - codeDelegation)",
gasAvailable,
@ -403,6 +396,10 @@ public class MainnetTransactionProcessor {
accessListGas,
codeDelegationGas);
final AccessWitness accessWitness = gasCalculator.newAccessWitness();
accessWitness.touchBaseTx(
transaction.getSender(), transaction.getTo(), transaction.getValue());
final WorldUpdater worldUpdater = evmWorldUpdater.updater();
final ImmutableMap.Builder<String, Object> contextVariablesBuilder =
ImmutableMap.<String, Object>builder()
@ -526,8 +523,6 @@ public class MainnetTransactionProcessor {
.log();
final long gasUsedByTransaction = transaction.getGasLimit() - initialFrame.getRemainingGas();
// checkTransactionGas(transaction.getHash(), gasUsedByTransaction);
// update the coinbase
final long usedGas = transaction.getGasLimit() - refundedGas;
final CoinbaseFeePriceCalculator coinbaseCalculator;

@ -44,6 +44,7 @@ import org.hyperledger.besu.plugin.services.storage.SegmentIdentifier;
import org.hyperledger.besu.plugin.services.storage.SegmentedKeyValueStorageTransaction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@ -435,7 +436,7 @@ public class VerkleWorldState extends DiffBasedWorldState {
@Override
public Map<Bytes32, Bytes> getAllAccountStorage(final Address address, final Hash rootHash) {
throw new UnsupportedOperationException("getAllAccountStorage not yet available for verkle");
return Collections.emptyMap();
}
private VerkleTrie createTrie(final NodeLoader nodeLoader, final Bytes32 rootHash) {

@ -255,7 +255,7 @@ dependencies {
}
verkleRefTestImplemention dependencies.create('ethereum:execution-spec-tests') {
version {
strictly 'verkle@v0.0.4'
strictly 'verkle@v0.0.5'
}
artifact {
name = 'fixtures'

@ -17,6 +17,7 @@ package org.hyperledger.besu.evm;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.hyperledger.besu.evm.operation.SwapOperation.SWAP_BASE;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.evm.code.CodeFactory;
import org.hyperledger.besu.evm.code.EOFLayout;
@ -213,18 +214,21 @@ public class EVM {
int opcode;
int pc = frame.getPC();
long statelessGas;
try {
opcode = code[pc] & 0xff;
currentOperation = operationArray[opcode];
if (!frame.wasCreatedInTransaction(frame.getContractAddress())) {
statelessGas =
frame
.getAccessWitness()
.touchCodeChunks(frame.getContractAddress(), pc, 1, code.length);
frame.decrementRemainingGas(statelessGas);
}
final Address contractAddress = frame.getContractAddress();
long statelessGas =
frame
.getAccessWitness()
.touchCodeChunks(
contractAddress,
frame.wasCreatedInTransaction(contractAddress),
frame.getPC(),
1,
code.length);
frame.decrementRemainingGas(statelessGas);
} catch (ArrayIndexOutOfBoundsException aiiobe) {
opcode = 0;

@ -1526,6 +1526,8 @@ public class MainnetEVMs {
final BigInteger chainID) {
// basing off of shanghai for devnet-6
registerShanghaiOperations(registry, gasCalculator, chainID);
// EIP-6780 nerf self destruct
registry.put(new SelfDestructOperation(gasCalculator, true));
// mimic a weird behavior by geth that ignores eip-1706
registry.put(new SStoreOperation(gasCalculator, 272L));
}

@ -90,7 +90,7 @@ public class EVMExecutor {
private ContractCreationProcessor contractCreationProcessor = null;
private MessageFrame.Type messageFrameType = MessageFrame.Type.MESSAGE_CALL;
private final AccessWitness accessWitness = new NoopAccessWitness();
private final AccessWitness accessWitness = NoopAccessWitness.get();
private EVMExecutor(final EVM evm) {
checkNotNull(evm, "evm must not be null");

@ -26,6 +26,7 @@ import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.VersionedHash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.gascalculator.stateless.NoopAccessWitness;
import org.hyperledger.besu.evm.internal.MemoryEntry;
import org.hyperledger.besu.evm.internal.OperandStack;
import org.hyperledger.besu.evm.internal.ReturnStack;
@ -1364,7 +1365,7 @@ public class MessageFrame {
private Optional<List<VersionedHash>> versionedHashes = Optional.empty();
private AccessWitness accessWitness = null;
private AccessWitness accessWitness = NoopAccessWitness.get();
/** Instantiates a new Builder. */
public Builder() {

@ -27,7 +27,6 @@ import org.hyperledger.besu.evm.internal.Words;
import org.hyperledger.besu.evm.precompile.BigIntegerModularExponentiationPrecompiledContract;
import java.math.BigInteger;
import java.util.Optional;
import java.util.function.Supplier;
import org.apache.tuweni.bytes.Bytes;
@ -49,7 +48,7 @@ public class BerlinGasCalculator extends IstanbulGasCalculator {
protected static final long ACCESS_LIST_STORAGE_COST = 1900L;
// redefinitions for EIP-2929
private static final long SLOAD_GAS = WARM_STORAGE_READ_COST;
protected static final long SLOAD_GAS = WARM_STORAGE_READ_COST;
/** The constant SSTORE_RESET_GAS. */
protected static final long SSTORE_RESET_GAS = 5000L - COLD_SLOAD_COST;
@ -120,8 +119,8 @@ public class BerlinGasCalculator extends IstanbulGasCalculator {
// Zeroed out old costs
@Override
public long getBalanceOperationGasCost(
final MessageFrame frame, final boolean accountIsWarm, final Optional<Address> maybeAddress) {
public long balanceOperationGasCost(
final MessageFrame frame, final boolean accountIsWarm, final Address address) {
return accountIsWarm ? getWarmStorageReadCost() : getColdAccountAccessCost();
}
@ -132,18 +131,18 @@ public class BerlinGasCalculator extends IstanbulGasCalculator {
@Override
public long extCodeHashOperationGasCost(
final MessageFrame frame, final boolean accountIsWarm, final Optional<Address> address) {
final MessageFrame frame, final boolean accountIsWarm, final Address address) {
return (accountIsWarm ? getWarmStorageReadCost() : getColdAccountAccessCost());
}
@Override
public long getExtCodeSizeOperationGasCost(
final MessageFrame frame, final boolean accountIsWarm, final Optional<Address> maybeAddress) {
public long extCodeSizeOperationGasCost(
final MessageFrame frame, final boolean accountIsWarm, final Address address) {
return (accountIsWarm ? getWarmStorageReadCost() : getColdAccountAccessCost());
}
@Override
public long getSloadOperationGasCost(
public long sloadOperationGasCost(
final MessageFrame frame, final UInt256 key, final boolean slotIsWarm) {
return (slotIsWarm ? getWarmStorageReadCost() : getColdSloadCost());
}

@ -20,7 +20,6 @@ import static org.hyperledger.besu.evm.internal.Words.clampedToInt;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.evm.frame.MessageFrame;
import java.util.Optional;
import java.util.function.Supplier;
import org.apache.tuweni.units.bigints.UInt256;
@ -131,7 +130,7 @@ public class ConstantinopleGasCalculator extends ByzantiumGasCalculator {
@Override
public long extCodeHashOperationGasCost(
final MessageFrame frame, final boolean accountIsWarm, final Optional<Address> address) {
final MessageFrame frame, final boolean accountIsWarm, final Address address) {
return EXTCODE_HASH_COST;
}
}

@ -15,25 +15,24 @@
package org.hyperledger.besu.evm.gascalculator;
import static org.hyperledger.besu.datatypes.Address.KZG_POINT_EVAL;
import static org.hyperledger.besu.ethereum.trie.verkle.util.Parameters.BASIC_DATA_LEAF_KEY;
import static org.hyperledger.besu.ethereum.trie.verkle.util.Parameters.CODE_HASH_LEAF_KEY;
import static org.hyperledger.besu.evm.internal.Words.clampedAdd;
import org.hyperledger.besu.datatypes.AccessWitness;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Transaction;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.trie.verkle.util.Parameters;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.stateless.Eip4762AccessWitness;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
import org.apache.tuweni.units.bigints.UInt256;
public class Eip4762GasCalculator extends PragueGasCalculator {
public static final Address HISTORY_STORAGE_ADDRESS =
Address.fromHexString("0xfffffffffffffffffffffffffffffffffffffffe");
private static final long CREATE_OPERATION_GAS_COST = 1_000L;
/** Instantiates a new Prague Gas Calculator. */
@ -52,14 +51,8 @@ public class Eip4762GasCalculator extends PragueGasCalculator {
}
@Override
public long initcodeCost(final int initCodeLength) {
return super.initcodeCost(initCodeLength);
}
@Override
public long initcodeStatelessCost(
final MessageFrame frame, final Address address, final Wei value) {
return frame.getAccessWitness().touchAndChargeContractCreateInit(address, !value.isZero());
public long proofOfAbsenceCost(final MessageFrame frame, final Address address) {
return frame.getAccessWitness().touchAndChargeProofOfAbsence(address);
}
@Override
@ -75,7 +68,7 @@ public class Eip4762GasCalculator extends PragueGasCalculator {
final Address to,
final boolean accountIsWarm) {
long gasCost =
long gas =
super.callOperationGasCost(
frame,
stipend,
@ -86,27 +79,35 @@ public class Eip4762GasCalculator extends PragueGasCalculator {
transferValue,
recipient,
to,
true);
if (super.isPrecompile(to)) {
return gasCost;
} else {
long statelessGas;
if (frame.getWorldUpdater().get(to) == null) {
statelessGas = frame.getAccessWitness().touchAndChargeProofOfAbsence(to);
} else {
statelessGas = frame.getAccessWitness().touchAndChargeMessageCall(to);
}
if (!transferValue.isZero()) {
statelessGas =
clampedAdd(
statelessGas,
frame.getAccessWitness().touchAndChargeValueTransfer(recipient.getAddress(), to));
}
if (statelessGas == 0) {
return getWarmStorageReadCost();
}
return clampedAdd(gasCost, statelessGas);
false);
if (!transferValue.isZero()) {
gas =
clampedAdd(
gas,
frame
.getAccessWitness()
.touchAndChargeValueTransfer(
frame.getContractAddress(), to, recipient == null, getWarmStorageReadCost()));
return gas;
}
if (isPrecompile(to) || isSystemContract(to)) {
return clampedAdd(gas, getWarmStorageReadCost());
}
long messageCallGas =
frame.getAccessWitness().touchAddressAndChargeRead(to, BASIC_DATA_LEAF_KEY);
if (messageCallGas == 0) {
messageCallGas = getWarmStorageReadCost();
}
return clampedAdd(gas, messageCallGas);
}
@Override
public long callValueTransferGasCost() {
return 0L;
}
@Override
@ -121,38 +122,6 @@ public class Eip4762GasCalculator extends PragueGasCalculator {
.touchCodeChunksUponContractCreation(frame.getContractAddress(), codeSize);
}
@Override
public long calculateStorageCost(
final MessageFrame frame,
final UInt256 key,
final UInt256 newValue,
final Supplier<UInt256> currentValue,
final Supplier<UInt256> originalValue) {
long gasCost = 0;
// TODO VEKLE: right now we're not computing what is the tree index and subindex we're just
// charging the cost of writing to the storage
AccessWitness accessWitness = frame.getAccessWitness();
List<UInt256> treeIndexes = accessWitness.getStorageSlotTreeIndexes(key);
gasCost +=
frame
.getAccessWitness()
.touchAddressOnReadAndComputeGas(
frame.getRecipientAddress(), treeIndexes.get(0), treeIndexes.get(1));
gasCost +=
frame
.getAccessWitness()
.touchAddressOnWriteAndComputeGas(
frame.getRecipientAddress(), treeIndexes.get(0), treeIndexes.get(1));
if (gasCost == 0) {
return getWarmStorageReadCost();
}
return gasCost;
}
@Override
public long calculateStorageRefundAmount(
final UInt256 newValue,
@ -170,24 +139,26 @@ public class Eip4762GasCalculator extends PragueGasCalculator {
final long codeOffset,
final long readSize,
final long codeSize) {
long gasCost = extCodeCopyOperationGasCost(frame, memOffset, readSize);
long gas = extCodeCopyOperationGasCost(frame, memOffset, readSize);
long statelessGas =
frame
.getAccessWitness()
.touchAddressOnReadAndComputeGas(address, UInt256.ZERO, Parameters.BASIC_DATA_LEAF_KEY);
if (statelessGas == 0) {
statelessGas = getWarmStorageReadCost();
gas =
clampedAdd(
gas,
frame
.getAccessWitness()
.touchCodeChunks(address, false, codeOffset, readSize, codeSize));
if (isPrecompile(address) || isSystemContract(address)) {
return clampedAdd(gas, getWarmStorageReadCost());
}
if (!frame.wasCreatedInTransaction(frame.getContractAddress())) {
statelessGas =
clampedAdd(
statelessGas,
frame.getAccessWitness().touchCodeChunks(address, codeOffset, readSize, codeSize));
long readTargetGas =
frame.getAccessWitness().touchAddressAndChargeRead(address, BASIC_DATA_LEAF_KEY);
if (readTargetGas == 0) {
readTargetGas = getWarmStorageReadCost();
}
return clampedAdd(gasCost, statelessGas);
return clampedAdd(gas, readTargetGas);
}
@Override
@ -197,109 +168,88 @@ public class Eip4762GasCalculator extends PragueGasCalculator {
final long codeOffset,
final long readSize,
final long codeSize) {
long gasCost = super.dataCopyOperationGasCost(frame, memOffset, readSize);
if (!frame.wasCreatedInTransaction(frame.getContractAddress())) {
gasCost =
clampedAdd(
gasCost,
frame
.getAccessWitness()
.touchCodeChunks(frame.getContractAddress(), codeOffset, readSize, codeSize));
}
return gasCost;
long gas = super.dataCopyOperationGasCost(frame, memOffset, readSize);
final Address contractAddress = frame.getContractAddress();
gas =
clampedAdd(
gas,
frame
.getAccessWitness()
.touchCodeChunks(
contractAddress,
frame.wasCreatedInTransaction(contractAddress),
codeOffset,
readSize,
codeSize));
return gas;
}
@Override
public long pushOperationGasCost(
final MessageFrame frame, final long codeOffset, final long readSize, final long codeSize) {
long gasCost = super.pushOperationGasCost(frame, codeOffset, readSize, codeSize);
if (!frame.wasCreatedInTransaction(frame.getContractAddress())) {
if (readSize == 1) {
if ((codeOffset % 31 == 0)) {
gasCost =
clampedAdd(
gasCost,
frame
.getAccessWitness()
.touchCodeChunks(
frame.getContractAddress(), codeOffset + 1, readSize, codeSize));
}
} else {
gasCost =
clampedAdd(
gasCost,
frame
.getAccessWitness()
.touchCodeChunks(frame.getContractAddress(), codeOffset, readSize, codeSize));
}
long gas = super.pushOperationGasCost(frame, codeOffset, readSize, codeSize);
if (frame.wasCreatedInTransaction(frame.getContractAddress())
// PUSH1 only touches code chunks if borderline
|| (readSize == 1 && codeOffset % 31 != 0)) {
return gas;
}
return gasCost;
final Address contractAddress = frame.getContractAddress();
gas =
clampedAdd(
gas,
frame
.getAccessWitness()
.touchCodeChunks(
contractAddress,
frame.wasCreatedInTransaction(contractAddress),
codeOffset,
readSize,
codeSize));
return gas;
}
@Override
public long getBalanceOperationGasCost(
final MessageFrame frame, final boolean accountIsWarm, final Optional<Address> maybeAddress) {
if (maybeAddress.isPresent()) {
final Address address = maybeAddress.get();
final long statelessGas =
frame
.getAccessWitness()
.touchAddressOnReadAndComputeGas(
address, UInt256.ZERO, Parameters.BASIC_DATA_LEAF_KEY);
if (statelessGas == 0) {
return getWarmStorageReadCost();
} else {
return statelessGas;
}
public long balanceOperationGasCost(
final MessageFrame frame, final boolean accountIsWarm, final Address address) {
final long gas =
frame.getAccessWitness().touchAddressAndChargeRead(address, BASIC_DATA_LEAF_KEY);
if (gas == 0) {
return getWarmStorageReadCost();
}
return 0L;
return gas;
}
@Override
public long extCodeHashOperationGasCost(
final MessageFrame frame, final boolean accountIsWarm, final Optional<Address> maybeAddress) {
if (maybeAddress.isPresent()) {
final Address address = maybeAddress.get();
if (isPrecompile(address)) {
return 0L;
} else {
final long statelessGas =
frame
.getAccessWitness()
.touchAddressOnReadAndComputeGas(
address, UInt256.ZERO, Parameters.CODE_HASH_LEAF_KEY);
if (statelessGas == 0) {
return getWarmStorageReadCost();
} else {
return statelessGas;
}
}
final MessageFrame frame, final boolean accountIsWarm, final Address address) {
if (isPrecompile(address) || isSystemContract(address)) {
return getWarmStorageReadCost();
}
return 0L;
long gas = frame.getAccessWitness().touchAddressAndChargeRead(address, CODE_HASH_LEAF_KEY);
if (gas == 0) {
return getWarmStorageReadCost();
}
return gas;
}
@Override
public long getExtCodeSizeOperationGasCost(
final MessageFrame frame, final boolean accountIsWarm, final Optional<Address> maybeAddress) {
if (maybeAddress.isPresent()) {
final Address address = maybeAddress.get();
if (isPrecompile(address)) {
return 0L;
} else {
long statelessGas =
frame
.getAccessWitness()
.touchAddressOnReadAndComputeGas(
address, UInt256.ZERO, Parameters.BASIC_DATA_LEAF_KEY);
if (statelessGas == 0) {
return getWarmStorageReadCost();
} else {
return statelessGas;
}
}
public long extCodeSizeOperationGasCost(
final MessageFrame frame, final boolean accountIsWarm, final Address address) {
if (isPrecompile(address) || isSystemContract(address)) {
return getWarmStorageReadCost();
}
return 0L;
long gas = frame.getAccessWitness().touchAddressAndChargeRead(address, BASIC_DATA_LEAF_KEY);
if (gas == 0) {
return getWarmStorageReadCost();
}
return gas;
}
@Override
@ -307,94 +257,85 @@ public class Eip4762GasCalculator extends PragueGasCalculator {
final MessageFrame frame,
final Account recipient,
final Address recipientAddress,
final Wei inheritance,
final Wei value,
final Address originatorAddress) {
final long gasCost =
super.selfDestructOperationGasCost(
frame, recipient, recipientAddress, inheritance, originatorAddress);
if (isPrecompile(recipientAddress)) {
return gasCost;
} else {
long statelessGas =
frame
.getAccessWitness()
.touchAddressOnReadAndComputeGas(
originatorAddress, UInt256.ZERO, Parameters.BASIC_DATA_LEAF_KEY);
if (!originatorAddress.equals(recipientAddress)) {
statelessGas =
clampedAdd(
statelessGas,
frame
.getAccessWitness()
.touchAddressOnReadAndComputeGas(
recipientAddress, UInt256.ZERO, Parameters.BASIC_DATA_LEAF_KEY));
}
if (!inheritance.isZero()) {
statelessGas =
clampedAdd(
statelessGas,
frame
.getAccessWitness()
.touchAddressOnWriteAndComputeGas(
originatorAddress, UInt256.ZERO, Parameters.BASIC_DATA_LEAF_KEY));
if (!originatorAddress.equals(recipientAddress)) {
statelessGas =
clampedAdd(
statelessGas,
frame
.getAccessWitness()
.touchAddressOnWriteAndComputeGas(
recipientAddress, UInt256.ZERO, Parameters.BASIC_DATA_LEAF_KEY));
}
if (recipient == null) {
statelessGas =
clampedAdd(
statelessGas,
frame
.getAccessWitness()
.touchAddressOnWriteAndComputeGas(
recipientAddress, UInt256.ZERO, Parameters.BASIC_DATA_LEAF_KEY));
}
}
return clampedAdd(gasCost, statelessGas);
// TODO: only charge non-account creation for devnet-7. Geth has removed the 25k gas cost
// from EIP-150 for account
// creation by mistake and will change in the next devnet
// long gas = super.selfDestructOperationGasCost(
// frame, recipient, recipientAddress, value, originatorAddress);
long gas = 5000L;
if (!value.isZero()) {
gas =
clampedAdd(
gas,
frame
.getAccessWitness()
.touchAndChargeValueTransferSelfDestruct(
originatorAddress,
recipientAddress,
recipient == null,
getWarmStorageReadCost()));
}
// check if there's any balance in the originating account in search of value
gas =
clampedAdd(
gas,
frame
.getAccessWitness()
.touchAddressAndChargeRead(originatorAddress, BASIC_DATA_LEAF_KEY));
// TODO: REMOVE - if code removed below there's no point to check for this
if (isPrecompile(recipientAddress) || isSystemContract(recipientAddress)) {
return gas;
}
// TODO: Recheck only here to pass tests - after talking with Geth team this will be dropped in
// future devnets. If there's no value transferred, there's no point in touching the beneficiary
if (!recipientAddress.equals(originatorAddress)) {
gas =
clampedAdd(
gas,
frame
.getAccessWitness()
.touchAddressAndChargeRead(recipientAddress, BASIC_DATA_LEAF_KEY));
}
return gas;
}
@Override
public long getSloadOperationGasCost(
public long sloadOperationGasCost(
final MessageFrame frame, final UInt256 key, final boolean slotIsWarm) {
AccessWitness accessWitness = frame.getAccessWitness();
List<UInt256> treeIndexes = accessWitness.getStorageSlotTreeIndexes(key);
long gasCost =
frame
.getAccessWitness()
.touchAddressOnReadAndComputeGas(
frame.getContractAddress(), treeIndexes.get(0), treeIndexes.get(1));
if (gasCost == 0) {
long gas = frame.getAccessWitness().touchAndChargeStorageLoad(frame.getContractAddress(), key);
if (gas == 0) {
return getWarmStorageReadCost();
}
return gasCost;
return gas;
}
@Override
public long computeBaseAccessEventsCost(
final AccessWitness accessWitness,
final Transaction transaction,
final MutableAccount sender) {
final boolean sendsValue = !transaction.getValue().equals(Wei.ZERO);
long cost = 0;
cost += accessWitness.touchTxOriginAndComputeGas(transaction.getSender());
if (transaction.getTo().isPresent()) {
final Address to = transaction.getTo().get();
cost += accessWitness.touchTxExistingAndComputeGas(to, sendsValue);
} else {
cost +=
accessWitness.touchAndChargeContractCreateInit(
Address.contractAddress(transaction.getSender(), sender.getNonce() - 1L), sendsValue);
public long calculateStorageCost(
final MessageFrame frame,
final UInt256 key,
final UInt256 newValue,
final Supplier<UInt256> currentValue,
final Supplier<UInt256> originalValue) {
long gas =
frame
.getAccessWitness()
.touchAndChargeStorageStore(frame.getRecipientAddress(), key, originalValue != null);
if (gas == 0) {
return SLOAD_GAS;
}
return cost;
return gas;
}
@Override
@ -404,6 +345,10 @@ public class Eip4762GasCalculator extends PragueGasCalculator {
.touchAndChargeContractCreateCompleted(frame.getContractAddress());
}
private static boolean isSystemContract(final Address address) {
return HISTORY_STORAGE_ADDRESS.equals(address);
}
@Override
public AccessWitness newAccessWitness() {
return new Eip4762AccessWitness();

@ -26,7 +26,6 @@ import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.operation.ExpOperation;
import java.util.Optional;
import java.util.function.Supplier;
import org.apache.tuweni.bytes.Bytes;
@ -414,8 +413,8 @@ public class FrontierGasCalculator implements GasCalculator {
}
@Override
public long getBalanceOperationGasCost(
final MessageFrame frame, final boolean accountIsWarm, final Optional<Address> maybeAddress) {
public long balanceOperationGasCost(
final MessageFrame frame, final boolean accountIsWarm, final Address address) {
return BALANCE_OPERATION_GAS_COST;
}
@ -468,14 +467,14 @@ public class FrontierGasCalculator implements GasCalculator {
@Override
public long extCodeHashOperationGasCost(
final MessageFrame frame, final boolean accountIsWarm, final Optional<Address> address) {
final MessageFrame frame, final boolean accountIsWarm, final Address address) {
throw new UnsupportedOperationException(
"EXTCODEHASH not supported by " + getClass().getSimpleName());
}
@Override
public long getExtCodeSizeOperationGasCost(
final MessageFrame frame, final boolean accountIsWarm, final Optional<Address> maybeAddress) {
public long extCodeSizeOperationGasCost(
final MessageFrame frame, final boolean accountIsWarm, final Address address) {
return extCodeBaseGasCost();
}
@ -533,7 +532,7 @@ public class FrontierGasCalculator implements GasCalculator {
}
@Override
public long getSloadOperationGasCost(
public long sloadOperationGasCost(
final MessageFrame frame, final UInt256 key, final boolean slotIsWarm) {
return SLOAD_OPERATION_GAS_COST;
}

@ -17,10 +17,8 @@ package org.hyperledger.besu.evm.gascalculator;
import org.hyperledger.besu.datatypes.AccessListEntry;
import org.hyperledger.besu.datatypes.AccessWitness;
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.account.MutableAccount;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.stateless.NoopAccessWitness;
import org.hyperledger.besu.evm.operation.BalanceOperation;
@ -44,7 +42,6 @@ import org.hyperledger.besu.evm.precompile.SHA256PrecompiledContract;
import org.hyperledger.besu.evm.processor.AbstractMessageProcessor;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;
import org.apache.tuweni.bytes.Bytes;
@ -309,8 +306,7 @@ public interface GasCalculator {
*/
long initcodeCost(final int initCodeLength);
default long initcodeStatelessCost(
final MessageFrame frame, final Address address, final Wei value) {
default long proofOfAbsenceCost(final MessageFrame frame, final Address address) {
return 0;
}
@ -366,11 +362,11 @@ public interface GasCalculator {
*
* @param frame The current frame
* @param accountIsWarm true to add warm storage read cost, false to add cold account access cost
* @param maybeAddress targeted address
* @param address targeted address
* @return the cost for executing the balance operation
*/
long getBalanceOperationGasCost(
MessageFrame frame, final boolean accountIsWarm, final Optional<Address> maybeAddress);
long balanceOperationGasCost(
MessageFrame frame, final boolean accountIsWarm, final Address address);
/**
* Returns the cost for executing a {@link BlockHashOperation}.
@ -423,22 +419,22 @@ public interface GasCalculator {
*
* @param frame The current frame
* @param accountIsWarm true to add warm storage read cost, false to add cold account access cost
* @param maybeAddress targeted address
* @param address targeted address
* @return the cost for executing the external code hash operation
*/
long extCodeHashOperationGasCost(
final MessageFrame frame, final boolean accountIsWarm, Optional<Address> maybeAddress);
final MessageFrame frame, final boolean accountIsWarm, Address address);
/**
* Returns the cost for executing a {@link ExtCodeSizeOperation}.
*
* @param frame The current frame
* @param accountIsWarm true to add warm storage read cost, false to add cold account access cost
* @param maybeAddress targeted address
* @param address targeted address
* @return the cost for executing the external code size operation
*/
long getExtCodeSizeOperationGasCost(
MessageFrame frame, final boolean accountIsWarm, Optional<Address> maybeAddress);
long extCodeSizeOperationGasCost(
MessageFrame frame, final boolean accountIsWarm, Address address);
/**
* Returns the cost for executing a {@link JumpDestOperation}.
@ -517,7 +513,7 @@ public interface GasCalculator {
*
* @return the cost for executing the storage load operation
*/
long getSloadOperationGasCost(MessageFrame frame, UInt256 key, final boolean slotIsWarm);
long sloadOperationGasCost(MessageFrame frame, UInt256 key, final boolean slotIsWarm);
/**
* Returns the cost for an SSTORE operation.
@ -752,20 +748,7 @@ public interface GasCalculator {
return 0L;
}
/**
* Compute access events cost of a transaction
*
* @param transaction transaction
* @return gas cost after computing all access events
*/
default long computeBaseAccessEventsCost(
final AccessWitness accessWitness,
final Transaction transaction,
final MutableAccount sender) {
return 0L;
}
default AccessWitness newAccessWitness() {
return new NoopAccessWitness();
return NoopAccessWitness.get();
}
}

@ -17,7 +17,6 @@ package org.hyperledger.besu.evm.gascalculator;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.evm.frame.MessageFrame;
import java.util.Optional;
import java.util.function.Supplier;
import org.apache.tuweni.bytes.Bytes;
@ -127,22 +126,22 @@ public class IstanbulGasCalculator extends PetersburgGasCalculator {
@Override
// As per https://eips.ethereum.org/EIPS/eip-1884
public long getSloadOperationGasCost(
public long sloadOperationGasCost(
final MessageFrame frame, final UInt256 key, final boolean slotIsWarm) {
return SLOAD_GAS;
}
@Override
// As per https://eips.ethereum.org/EIPS/eip-1884
public long getBalanceOperationGasCost(
final MessageFrame frame, final boolean accountIsWarm, final Optional<Address> maybeAddress) {
public long balanceOperationGasCost(
final MessageFrame frame, final boolean accountIsWarm, final Address address) {
return BALANCE_OPERATION_GAS_COST;
}
@Override
// As per https://eips.ethereum.org/EIPS/eip-1884
public long extCodeHashOperationGasCost(
final MessageFrame frame, final boolean accountIsWarm, final Optional<Address> address) {
final MessageFrame frame, final boolean accountIsWarm, final Address address) {
return EXTCODE_HASH_COST;
}

@ -22,8 +22,6 @@ import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.internal.Words;
import java.util.Optional;
import org.apache.tuweni.units.bigints.UInt256;
/** The Tangerine whistle gas calculator. */
@ -45,8 +43,8 @@ public class TangerineWhistleGasCalculator extends HomesteadGasCalculator {
public TangerineWhistleGasCalculator() {}
@Override
public long getBalanceOperationGasCost(
final MessageFrame frame, final boolean accountIsWarm, final Optional<Address> maybeAddress) {
public long balanceOperationGasCost(
final MessageFrame frame, final boolean accountIsWarm, final Address address) {
return BALANCE_OPERATION_GAS_COST;
}
@ -137,7 +135,7 @@ public class TangerineWhistleGasCalculator extends HomesteadGasCalculator {
}
@Override
public long getSloadOperationGasCost(
public long sloadOperationGasCost(
final MessageFrame frame, final UInt256 key, final boolean slotIsWarm) {
return SLOAD_OPERATION_GAS_COST;
}

@ -14,67 +14,34 @@
*/
package org.hyperledger.besu.evm.gascalculator.stateless;
public class AccessEvents {
private boolean branchRead;
private boolean chunkRead;
private boolean branchWrite;
private boolean chunkWrite;
private boolean chunkFill;
public final class AccessEvents {
public AccessEvents() {
this(false, false, false, false, false);
}
public AccessEvents(
final boolean branchRead,
final boolean chunkRead,
final boolean branchWrite,
final boolean chunkWrite,
final boolean chunkFill) {
this.branchRead = branchRead;
this.chunkRead = chunkRead;
this.branchWrite = branchWrite;
this.chunkWrite = chunkWrite;
this.chunkFill = chunkFill;
}
public boolean isBranchRead() {
return branchRead;
}
public boolean isChunkRead() {
return chunkRead;
}
public boolean isBranchWrite() {
return branchWrite;
}
public static final short NONE = 0;
public static final short BRANCH_READ = 1;
public static final short BRANCH_WRITE = 2;
public static final short LEAF_READ = 4;
public static final short LEAF_RESET = 8;
public static final short LEAF_SET = 16;
public boolean isChunkWrite() {
return chunkWrite;
}
public boolean isChunkFill() {
return chunkFill;
}
private AccessEvents() {}
public void setBranchRead(final boolean branchRead) {
this.branchRead = branchRead;
public static boolean isBranchRead(final short accessEvents) {
return (accessEvents & BRANCH_READ) != 0;
}
public void setChunkRead(final boolean chunkRead) {
this.chunkRead = chunkRead;
public static boolean isBranchWrite(final short accessEvents) {
return (accessEvents & BRANCH_WRITE) != 0;
}
public void setBranchWrite(final boolean branchWrite) {
this.branchWrite = branchWrite;
public static boolean isLeafRead(final short accessEvents) {
return (accessEvents & LEAF_READ) != 0;
}
public void setChunkWrite(final boolean chunkWrite) {
this.chunkWrite = chunkWrite;
public static boolean isLeafReset(final short accessEvents) {
return (accessEvents & LEAF_RESET) != 0;
}
public void setChunkFill(final boolean chunkFill) {
this.chunkFill = chunkFill;
public static boolean isLeafSet(final short accessEvents) {
return (accessEvents & LEAF_SET) != 0;
}
}

@ -0,0 +1,49 @@
/*
* 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.stateless;
public final class AccessMode {
public static int NONE = 0;
public static int READ = 1;
public static int WRITE_SET = 2;
public static int WRITE_RESET = 4;
private static boolean isRead(final int accessMode) {
return (accessMode & READ) != 0;
}
public static boolean isWrite(final int accessMode) {
return isWriteSet(accessMode) || isWriteReset(accessMode);
}
public static boolean isWriteSet(final int accessMode) {
return (accessMode & WRITE_SET) != 0;
}
private static boolean isWriteReset(final int accessMode) {
return (accessMode & WRITE_RESET) != 0;
}
public static String toString(final int accessMode) {
if (isRead(accessMode)) {
return "READ";
} else if (isWriteSet(accessMode)) {
return "WRITE_SET";
} else if (isWriteReset(accessMode)) {
return "WRITE_RESET";
}
return "UNKNOWN_ACCESS_MODE";
}
}

@ -20,7 +20,9 @@ import static org.hyperledger.besu.ethereum.trie.verkle.util.Parameters.CODE_OFF
import static org.hyperledger.besu.ethereum.trie.verkle.util.Parameters.VERKLE_NODE_WIDTH;
import static org.hyperledger.besu.evm.internal.Words.clampedAdd;
import org.hyperledger.besu.datatypes.AccessWitness;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.trie.verkle.adapter.TrieKeyAdapter;
import org.hyperledger.besu.ethereum.trie.verkle.hasher.PedersenHasher;
@ -28,146 +30,174 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import org.apache.tuweni.units.bigints.UInt256;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Eip4762AccessWitness implements org.hyperledger.besu.datatypes.AccessWitness {
public class Eip4762AccessWitness implements AccessWitness {
private static final Logger LOG = LoggerFactory.getLogger(Eip4762AccessWitness.class);
private static final TrieKeyAdapter TRIE_KEY_ADAPTER = new TrieKeyAdapter(new PedersenHasher());
private static final long WITNESS_BRANCH_READ_COST = 1900;
private static final long WITNESS_CHUNK_READ_COST = 200;
private static final long WITNESS_BRANCH_WRITE_COST = 3000;
private static final long WITNESS_CHUNK_WRITE_COST = 500;
private static final long WITNESS_CHUNK_FILL_COST = 6200;
private static final long WITNESS_BRANCH_COST = 1900;
private static final long WITNESS_CHUNK_COST = 200;
private static final long SUBTREE_EDIT_COST = 3000;
private static final long CHUNK_EDIT_COST = 500;
private static final long CHUNK_FILL_COST = 6200;
private static final UInt256 zeroTreeIndex = UInt256.ZERO;
private static final byte AccessWitnessReadFlag = 1;
private static final byte AccessWitnessWriteFlag = 2;
private final Map<BranchAccessKey, Byte> branches;
private final Map<ChunkAccessKey, Byte> chunks;
private final Map<LeafAccessKey, Integer> leaves;
private final Map<BranchAccessKey, Integer> branches;
public Eip4762AccessWitness() {
this(new HashMap<>(), new HashMap<>());
}
public Eip4762AccessWitness(
final Map<BranchAccessKey, Byte> branches, final Map<ChunkAccessKey, Byte> chunks) {
final Map<LeafAccessKey, Integer> leaves, final Map<BranchAccessKey, Integer> branches) {
this.branches = branches;
this.chunks = chunks;
this.leaves = leaves;
}
@Override
public List<Address> keys() {
return this.chunks.keySet().stream()
.map(chunkAccessKey -> chunkAccessKey.branchAccessKey().address())
.toList();
public long touchAddressAndChargeRead(final Address address, final UInt256 leafKey) {
return touchAddressOnReadAndComputeGas(address, zeroTreeIndex, leafKey);
}
@Override
public long touchAndChargeProofOfAbsence(final Address address) {
public long touchAndChargeValueTransfer(
final Address caller,
final Address target,
final boolean isAccountCreation,
final long warmReadCost) {
long gas = 0;
gas =
clampedAdd(
gas, touchAddressOnReadAndComputeGas(address, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
gas =
clampedAdd(
gas, touchAddressOnReadAndComputeGas(address, zeroTreeIndex, CODE_HASH_LEAF_KEY));
return gas;
}
gas, touchAddressOnWriteResetAndComputeGas(caller, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
@Override
public long touchAndChargeMessageCall(final Address address) {
return touchAddressOnReadAndComputeGas(address, zeroTreeIndex, BASIC_DATA_LEAF_KEY);
}
@Override
public long touchAndChargeValueTransfer(final Address caller, final Address target) {
if (isAccountCreation) {
gas =
clampedAdd(
gas, touchAddressOnWriteSetAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
gas =
clampedAdd(
gas, touchAddressOnWriteSetAndComputeGas(target, zeroTreeIndex, CODE_HASH_LEAF_KEY));
return gas;
}
long gas = 0;
long readTargetStatelessGas =
touchAddressOnReadAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY);
if (readTargetStatelessGas == 0) {
readTargetStatelessGas = clampedAdd(readTargetStatelessGas, warmReadCost);
}
gas =
clampedAdd(
gas, touchAddressOnWriteAndComputeGas(caller, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
gas =
clampedAdd(
gas, touchAddressOnWriteAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
gas, touchAddressOnWriteResetAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
return gas;
return clampedAdd(gas, readTargetStatelessGas);
}
@Override
public long touchAndChargeContractCreateInit(
final Address address, final boolean createSendsValue) {
public long touchAndChargeValueTransferSelfDestruct(
final Address caller,
final Address target,
final boolean isAccountCreation,
final long warmReadCost) {
long gas = 0;
gas =
clampedAdd(
gas, touchAddressOnWriteAndComputeGas(address, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
gas, touchAddressOnWriteResetAndComputeGas(caller, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
if (caller.equals(target)) {
return gas;
}
if (createSendsValue) {
if (isAccountCreation) {
gas =
clampedAdd(
gas, touchAddressOnWriteSetAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
gas =
clampedAdd(
gas, touchAddressOnWriteAndComputeGas(address, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
gas, touchAddressOnWriteSetAndComputeGas(target, zeroTreeIndex, CODE_HASH_LEAF_KEY));
return gas;
}
return gas;
long readTargetStatelessGas =
touchAddressOnReadAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY);
if (readTargetStatelessGas == 0) {
readTargetStatelessGas = clampedAdd(readTargetStatelessGas, warmReadCost);
}
gas =
clampedAdd(
gas, touchAddressOnWriteResetAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
return clampedAdd(gas, readTargetStatelessGas);
}
@Override
public long touchAndChargeContractCreateCompleted(final Address address) {
public long touchAndChargeProofOfAbsence(final Address address) {
long gas = 0;
gas =
clampedAdd(
gas, touchAddressOnWriteAndComputeGas(address, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
gas, touchAddressOnReadAndComputeGas(address, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
gas =
clampedAdd(
gas, touchAddressOnWriteAndComputeGas(address, zeroTreeIndex, CODE_HASH_LEAF_KEY));
gas, touchAddressOnReadAndComputeGas(address, zeroTreeIndex, CODE_HASH_LEAF_KEY));
return gas;
}
@SuppressWarnings("unused")
@Override
public long touchTxOriginAndComputeGas(final Address origin) {
public long touchAndChargeContractCreateCompleted(final Address address) {
long gas = 0;
gas =
clampedAdd(
gas, touchAddressOnReadAndComputeGas(origin, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
gas,
touchAddressOnWriteResetAndComputeGas(address, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
gas =
clampedAdd(
gas, touchAddressOnWriteAndComputeGas(origin, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
gas =
clampedAdd(gas, touchAddressOnReadAndComputeGas(origin, zeroTreeIndex, CODE_HASH_LEAF_KEY));
// modifying this after update on EIP-4762 to not charge simple transfers
gas, touchAddressOnWriteResetAndComputeGas(address, zeroTreeIndex, CODE_HASH_LEAF_KEY));
return 0;
return gas;
}
@SuppressWarnings("unused")
@Override
public long touchTxExistingAndComputeGas(final Address target, final boolean sendsValue) {
long gas = 0;
public void touchBaseTx(final Address origin, final Optional<Address> target, final Wei value) {
LOG.atDebug().log("START OF UNCHARGED COSTS");
touchTxOrigin(origin);
if (target.isPresent()) { // is not contract creation
final Address to = target.get();
final boolean sendsValue = !Wei.ZERO.equals(value);
touchTxExisting(to, sendsValue);
}
LOG.atDebug().log("END OF UNCHARGED COSTS");
}
gas =
clampedAdd(
gas, touchAddressOnReadAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
gas =
clampedAdd(gas, touchAddressOnReadAndComputeGas(target, zeroTreeIndex, CODE_HASH_LEAF_KEY));
private void touchTxOrigin(final Address origin) {
touchAddressOnWriteResetAndComputeGas(origin, zeroTreeIndex, BASIC_DATA_LEAF_KEY);
touchAddressOnReadAndComputeGas(origin, zeroTreeIndex, CODE_HASH_LEAF_KEY);
}
if (sendsValue) {
gas =
clampedAdd(
gas, touchAddressOnWriteAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
private void touchTxExisting(final Address target, final boolean sendsValue) {
touchAddressOnReadAndComputeGas(target, zeroTreeIndex, CODE_HASH_LEAF_KEY);
if (!sendsValue) {
touchAddressOnReadAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY);
return;
}
// modifying this after update on EIP-4762 to not charge simple transfers
return 0;
// TODO: not done right now on Geth either - implement case if target does not exist yet - a
// CODEHASH_LEAF will be touched too and WriteSet will be called
touchAddressOnWriteResetAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY);
}
@Override
@ -177,7 +207,7 @@ public class Eip4762AccessWitness implements org.hyperledger.besu.datatypes.Acce
gas =
clampedAdd(
gas,
touchAddressOnWriteAndComputeGas(
touchAddressOnWriteSetAndComputeGas(
address,
CODE_OFFSET.add(i).divide(VERKLE_NODE_WIDTH),
CODE_OFFSET.add(i).mod(VERKLE_NODE_WIDTH)));
@ -187,173 +217,217 @@ public class Eip4762AccessWitness implements org.hyperledger.besu.datatypes.Acce
@Override
public long touchCodeChunks(
final Address address, final long startPc, final long readSize, final long codeLength) {
final Address contractAddress,
final boolean isContractInDeployment,
final long startPc,
final long readSize,
final long codeLength) {
long gas = 0;
if ((readSize == 0 && codeLength == 0) || startPc > codeLength) {
if (isContractInDeployment || readSize == 0 || startPc >= codeLength) {
return 0;
}
long endPc = startPc + readSize;
if (endPc > codeLength) {
endPc = codeLength;
}
if (endPc > 0) {
endPc -= 1;
}
for (long i = startPc / 31; i <= endPc / 31; i++) {
// last byte read is limited by code length, and it is an index, hence the decrement
long endPc = Math.min(startPc + readSize, codeLength) - 1L;
for (long i = startPc / 31L; i <= endPc / 31L; i++) {
gas =
clampedAdd(
gas,
touchAddressOnReadAndComputeGas(
address,
contractAddress,
CODE_OFFSET.add(i).divide(VERKLE_NODE_WIDTH),
CODE_OFFSET.add(i).mod(VERKLE_NODE_WIDTH)));
}
return gas;
}
@Override
public long touchAddressOnWriteAndComputeGas(
private long touchAddressOnWriteResetAndComputeGas(
final Address address, final UInt256 treeIndex, final UInt256 subIndex) {
return touchAddressAndChargeGas(address, treeIndex, subIndex, AccessMode.WRITE_RESET);
}
return touchAddressAndChargeGas(address, treeIndex, subIndex, true);
private long touchAddressOnWriteSetAndComputeGas(
final Address address, final UInt256 treeIndex, final UInt256 subIndex) {
// TODO: change to WRITE_SET when CHUNK_FILL is implemented. Still not implemented in devnet-7
return touchAddressAndChargeGas(address, treeIndex, subIndex, AccessMode.WRITE_RESET);
}
@Override
public long touchAddressOnReadAndComputeGas(
private long touchAddressOnReadAndComputeGas(
final Address address, final UInt256 treeIndex, final UInt256 subIndex) {
return touchAddressAndChargeGas(address, treeIndex, subIndex, false);
return touchAddressAndChargeGas(address, treeIndex, subIndex, AccessMode.READ);
}
private List<UInt256> getStorageSlotTreeIndexes(final UInt256 storageKey) {
return List.of(
TRIE_KEY_ADAPTER.locateStorageKeyOffset(storageKey),
TRIE_KEY_ADAPTER.locateStorageKeySuffix(storageKey));
}
@Override
public long touchAndChargeStorageLoad(final Address address, final UInt256 storageKey) {
List<UInt256> treeIndexes = getStorageSlotTreeIndexes(storageKey);
return touchAddressOnReadAndComputeGas(address, treeIndexes.get(0), treeIndexes.get(1));
}
@Override
public long touchAndChargeStorageStore(
final Address address, final UInt256 storageKey, final boolean hasPreviousValue) {
List<UInt256> treeIndexes = getStorageSlotTreeIndexes(storageKey);
if (!hasPreviousValue) {
return touchAddressOnWriteSetAndComputeGas(address, treeIndexes.get(0), treeIndexes.get(1));
}
return touchAddressOnWriteResetAndComputeGas(address, treeIndexes.get(0), treeIndexes.get(1));
}
public long touchAddressAndChargeGas(
final Address address,
final UInt256 treeIndex,
final UInt256 subIndex,
final boolean isWrite) {
AccessEvents accessEvent = touchAddress(address, treeIndex, subIndex, isWrite);
boolean logEnabled = false;
final int accessMode) {
final short accessEvents = touchAddress(address, treeIndex, subIndex, accessMode);
long gas = 0;
if (accessEvent.isBranchRead()) {
gas = clampedAdd(gas, WITNESS_BRANCH_READ_COST);
if (logEnabled) {
System.out.println(
"touchAddressAndChargeGas WitnessBranchReadCost "
+ address
+ " "
+ treeIndex
+ " "
+ subIndex
+ " "
+ isWrite
+ " "
+ gas);
}
if (AccessEvents.isBranchRead(accessEvents)) {
gas = clampedAdd(gas, WITNESS_BRANCH_COST);
final long gasView = gas;
LOG.atDebug().log(
() ->
"touchAddressAndChargeGas WITNESS_BRANCH_COST "
+ address
+ " "
+ treeIndex
+ " "
+ subIndex
+ " "
+ AccessMode.toString(accessMode)
+ " "
+ gasView);
}
if (accessEvent.isChunkRead()) {
gas = clampedAdd(gas, WITNESS_CHUNK_READ_COST);
if (logEnabled) {
System.out.println(
"touchAddressAndChargeGas WitnessChunkReadCost "
+ address
+ " "
+ treeIndex
+ " "
+ subIndex
+ " "
+ isWrite
+ " "
+ gas);
}
if (AccessEvents.isLeafRead(accessEvents)) {
gas = clampedAdd(gas, WITNESS_CHUNK_COST);
final long gasView = gas;
LOG.atDebug().log(
() ->
"touchAddressAndChargeGas WITNESS_CHUNK_COST "
+ address
+ " "
+ treeIndex
+ " "
+ subIndex
+ " "
+ AccessMode.toString(accessMode)
+ " "
+ gasView);
}
if (accessEvent.isBranchWrite()) {
gas = clampedAdd(gas, WITNESS_BRANCH_WRITE_COST);
if (logEnabled) {
System.out.println(
"touchAddressAndChargeGas WitnessBranchWriteCost "
+ address
+ " "
+ treeIndex
+ " "
+ subIndex
+ " "
+ isWrite
+ " "
+ gas);
}
if (AccessEvents.isBranchWrite(accessEvents)) {
gas = clampedAdd(gas, SUBTREE_EDIT_COST);
final long gasView = gas;
LOG.atDebug().log(
() ->
"touchAddressAndChargeGas SUBTREE_EDIT_COST "
+ address
+ " "
+ treeIndex
+ " "
+ subIndex
+ " "
+ AccessMode.toString(accessMode)
+ " "
+ gasView);
}
if (accessEvent.isChunkWrite()) {
gas = clampedAdd(gas, WITNESS_CHUNK_WRITE_COST);
if (logEnabled) {
System.out.println(
"touchAddressAndChargeGas WitnessChunkWriteCost "
+ address
+ " "
+ treeIndex
+ " "
+ subIndex
+ " "
+ isWrite
+ " "
+ gas);
}
if (AccessEvents.isLeafReset(accessEvents)) {
gas = clampedAdd(gas, CHUNK_EDIT_COST);
final long gasView = gas;
LOG.atDebug().log(
() ->
"touchAddressAndChargeGas CHUNK_EDIT_COST "
+ address
+ " "
+ treeIndex
+ " "
+ subIndex
+ " "
+ AccessMode.toString(accessMode)
+ " "
+ gasView);
}
if (accessEvent.isChunkFill()) {
gas = clampedAdd(gas, WITNESS_CHUNK_FILL_COST);
if (logEnabled) {
System.out.println(
"touchAddressAndChargeGas WitnessChunkFillCost "
+ address
+ " "
+ treeIndex
+ " "
+ subIndex
+ " "
+ isWrite
+ " "
+ gas);
}
if (AccessEvents.isLeafSet(accessEvents)) {
gas = clampedAdd(gas, CHUNK_FILL_COST);
final long gasView = gas;
LOG.atDebug().log(
() ->
"touchAddressAndChargeGas CHUNK_FILL_COST "
+ address
+ " "
+ treeIndex
+ " "
+ subIndex
+ " "
+ AccessMode.toString(accessMode)
+ " "
+ gasView);
}
return gas;
}
public AccessEvents touchAddress(
final Address addr, final UInt256 treeIndex, final UInt256 subIndex, final boolean isWrite) {
AccessEvents accessEvents = new AccessEvents();
public short touchAddress(
final Address addr, final UInt256 treeIndex, final UInt256 leafIndex, final int accessMode) {
short accessEvents = AccessEvents.NONE;
BranchAccessKey branchKey = new BranchAccessKey(addr, treeIndex);
ChunkAccessKey chunkKey = new ChunkAccessKey(addr, treeIndex, subIndex);
accessEvents |= touchAddressForBranch(branchKey, accessMode);
if (leafIndex != null) {
accessEvents |= touchAddressForLeaf(branchKey, leafIndex, accessMode);
}
return accessEvents;
}
private short touchAddressForBranch(final BranchAccessKey branchKey, final int accessMode) {
short accessEvents = AccessEvents.NONE;
// Read access.
if (!this.branches.containsKey(branchKey)) {
accessEvents.setBranchRead(true);
this.branches.put(branchKey, AccessWitnessReadFlag);
}
if (!this.chunks.containsKey(chunkKey)) {
accessEvents.setChunkRead(true);
this.chunks.put(chunkKey, AccessWitnessReadFlag);
accessEvents |= AccessEvents.BRANCH_READ;
this.branches.put(branchKey, AccessMode.READ);
}
// TODO VERKLE: for now testnet doesn't charge
// chunk filling costs if the leaf was previously empty in the state
// boolean chunkFill = false;
// A write is always a read
if (AccessMode.isWrite(accessMode)) {
int previousAccessMode =
!this.branches.containsKey(branchKey) ? AccessMode.NONE : this.branches.get(branchKey);
if (!AccessMode.isWrite(previousAccessMode)) {
accessEvents |= AccessEvents.BRANCH_WRITE;
this.branches.put(branchKey, (previousAccessMode | accessMode));
}
}
if (isWrite) {
return accessEvents;
}
if ((this.branches.get(branchKey) & AccessWitnessWriteFlag) == 0) {
accessEvents.setBranchWrite(true);
this.branches.put(
branchKey, (byte) (this.branches.get(branchKey) | AccessWitnessWriteFlag));
}
private short touchAddressForLeaf(
final BranchAccessKey branchKey, final UInt256 subIndex, final int accessMode) {
LeafAccessKey leafKey = new LeafAccessKey(branchKey, subIndex);
short accessEvents = AccessEvents.NONE;
byte chunkValue = this.chunks.get(chunkKey);
if (!this.leaves.containsKey(leafKey)) {
accessEvents |= AccessEvents.LEAF_READ;
this.leaves.put(leafKey, AccessMode.READ);
}
if ((chunkValue & AccessWitnessWriteFlag) == 0) {
accessEvents.setChunkWrite(true);
this.chunks.put(chunkKey, (byte) (this.chunks.get(chunkKey) | AccessWitnessWriteFlag));
// A write is always a read
if (AccessMode.isWrite(accessMode)) {
int previousAccessMode =
!this.leaves.containsKey(leafKey) ? AccessMode.NONE : this.leaves.get(leafKey);
if (!AccessMode.isWrite(previousAccessMode)) {
accessEvents |= AccessEvents.LEAF_RESET;
if (AccessMode.isWriteSet(accessMode)) {
accessEvents |= AccessEvents.LEAF_SET;
}
this.leaves.put(leafKey, (previousAccessMode | accessMode));
}
}
return accessEvents;
}
@ -362,41 +436,20 @@ public class Eip4762AccessWitness implements org.hyperledger.besu.datatypes.Acce
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Eip4762AccessWitness that = (Eip4762AccessWitness) o;
return Objects.equals(branches, that.branches) && Objects.equals(chunks, that.chunks);
return Objects.equals(branches, that.branches) && Objects.equals(leaves, that.leaves);
}
@Override
public int hashCode() {
return Objects.hash(branches, chunks);
return Objects.hash(branches, leaves);
}
@Override
public String toString() {
return "AccessWitness{" + "branches=" + branches + ", chunks=" + chunks + '}';
}
public Map<BranchAccessKey, Byte> getBranches() {
return branches;
}
public Map<ChunkAccessKey, Byte> getChunks() {
return chunks;
return "AccessWitness{" + "leaves=" + leaves + ", branches=" + branches + '}';
}
public record BranchAccessKey(Address address, UInt256 treeIndex) {}
;
public record ChunkAccessKey(BranchAccessKey branchAccessKey, UInt256 chunkIndex) {
public ChunkAccessKey(
final Address address, final UInt256 treeIndex, final UInt256 chunkIndex) {
this(new BranchAccessKey(address, treeIndex), chunkIndex);
}
}
@Override
public List<UInt256> getStorageSlotTreeIndexes(final UInt256 storageKey) {
return List.of(
TRIE_KEY_ADAPTER.locateStorageKeyOffset(storageKey),
TRIE_KEY_ADAPTER.locateStorageKeySuffix(storageKey));
}
public record LeafAccessKey(BranchAccessKey branchAccessKey, UInt256 leafIndex) {}
}

@ -14,40 +14,55 @@
*/
package org.hyperledger.besu.evm.gascalculator.stateless;
import org.hyperledger.besu.datatypes.AccessWitness;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import java.util.Optional;
import org.apache.tuweni.units.bigints.UInt256;
public class NoopAccessWitness extends Eip4762AccessWitness {
public class NoopAccessWitness implements AccessWitness {
@Override
public long touchAndChargeProofOfAbsence(final Address address) {
return 0;
private static NoopAccessWitness instance;
private NoopAccessWitness() {}
public static NoopAccessWitness get() {
if (instance == null) {
instance = new NoopAccessWitness();
}
return instance;
}
@Override
public long touchAndChargeValueTransfer(final Address caller, final Address target) {
public long touchAndChargeValueTransfer(
final Address caller,
final Address target,
final boolean isAccountCreation,
final long warmReadCost) {
return 0;
}
@Override
public long touchAndChargeMessageCall(final Address address) {
public long touchAndChargeValueTransferSelfDestruct(
final Address caller,
final Address target,
final boolean isAccountCreation,
final long warmReadCost) {
return 0;
}
@Override
public long touchTxOriginAndComputeGas(final Address origin) {
public long touchAddressAndChargeRead(final Address address, final UInt256 leafKey) {
return 0;
}
@Override
public long touchTxExistingAndComputeGas(final Address target, final boolean sendsValue) {
return 0;
}
public void touchBaseTx(final Address origin, final Optional<Address> target, final Wei value) {}
@Override
public long touchAndChargeContractCreateInit(
final Address address, final boolean createSendsValue) {
public long touchAndChargeProofOfAbsence(final Address address) {
return 0;
}
@ -57,14 +72,13 @@ public class NoopAccessWitness extends Eip4762AccessWitness {
}
@Override
public long touchAddressOnWriteAndComputeGas(
final Address address, final UInt256 treeIndex, final UInt256 subIndex) {
public long touchAndChargeStorageLoad(final Address address, final UInt256 storageKey) {
return 0;
}
@Override
public long touchAddressOnReadAndComputeGas(
final Address address, final UInt256 treeIndex, final UInt256 subIndex) {
public long touchAndChargeStorageStore(
final Address address, final UInt256 storageKey, final boolean hasPreviousValue) {
return 0;
}
@ -75,13 +89,11 @@ public class NoopAccessWitness extends Eip4762AccessWitness {
@Override
public long touchCodeChunks(
final Address address, final long offset, final long readSize, final long codeLength) {
return 0;
}
@Override
public long touchCodeChunksWithoutAccessCost(
final Address address, final long offset, final long readSize, final long codeLength) {
final Address address,
final boolean isContractInDeployment,
final long offset,
final long readSize,
final long codeLength) {
return 0;
}
}

@ -141,9 +141,6 @@ public abstract class AbstractCreateOperation extends AbstractOperation {
*/
protected abstract long cost(final MessageFrame frame, Supplier<Code> codeSupplier);
protected abstract long statelessCost(
final MessageFrame frame, final Address contractAddress, final Wei value);
/**
* Target contract address.
*

@ -20,12 +20,8 @@ import org.hyperledger.besu.evm.account.Account;
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.OverflowException;
import org.hyperledger.besu.evm.internal.UnderflowException;
import org.hyperledger.besu.evm.internal.Words;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
/** The Balance operation. */
@ -44,38 +40,27 @@ public class BalanceOperation extends AbstractOperation {
* Gets Balance operation Gas Cost plus warm storage read cost or cold account access cost.
*
* @param frame current frame
* @param maybeAddress to use for get balance
* @param address to use for get balance
* @param accountIsWarm true to add warm storage read cost, false to add cold account access cost
* @return the long
*/
protected long cost(
final MessageFrame frame, final Optional<Address> maybeAddress, final boolean accountIsWarm) {
return gasCalculator().getBalanceOperationGasCost(frame, accountIsWarm, maybeAddress);
final MessageFrame frame, final Address address, final boolean accountIsWarm) {
return gasCalculator().balanceOperationGasCost(frame, accountIsWarm, address);
}
@Override
public OperationResult execute(final MessageFrame frame, final EVM evm) {
final Address address;
try {
address = Words.toAddress(frame.popStackItem());
} catch (final UnderflowException ufe) {
return new OperationResult(
cost(frame, Optional.empty(), true), ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS);
}
try {
final boolean accountIsWarm =
frame.warmUpAddress(address) || gasCalculator().isPrecompile(address);
final long cost = cost(frame, Optional.of(address), accountIsWarm);
if (frame.getRemainingGas() < cost) {
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
} else {
final Account account = frame.getWorldUpdater().get(address);
frame.pushStackItem(account == null ? Bytes.EMPTY : account.getBalance());
return new OperationResult(cost, null);
}
} catch (final OverflowException ofe) {
return new OperationResult(
cost(frame, Optional.of(address), true), ExceptionalHaltReason.TOO_MANY_STACK_ITEMS);
final Address address = Words.toAddress(frame.popStackItem());
final boolean accountIsWarm =
frame.warmUpAddress(address) || gasCalculator().isPrecompile(address);
final long cost = cost(frame, address, accountIsWarm);
if (frame.getRemainingGas() < cost) {
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
} else {
final Account account = frame.getWorldUpdater().get(address);
frame.pushStackItem(account == null ? Bytes.EMPTY : account.getBalance());
return new OperationResult(cost, null);
}
}
}

@ -18,6 +18,7 @@ import static org.hyperledger.besu.evm.internal.Words.clampedToLong;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.internal.Words;
@ -74,6 +75,31 @@ public class CallCodeOperation extends AbstractCallOperation {
return frame.getRecipientAddress();
}
@Override
public long cost(final MessageFrame frame, final boolean accountIsWarm) {
final long stipend = gas(frame);
final long inputDataOffset = inputDataOffset(frame);
final long inputDataLength = inputDataLength(frame);
final long outputDataOffset = outputDataOffset(frame);
final long outputDataLength = outputDataLength(frame);
final Account recipient = frame.getWorldUpdater().get(address(frame));
final Address to = to(frame);
GasCalculator gasCalculator = gasCalculator();
return gasCalculator.callOperationGasCost(
frame,
stipend,
inputDataOffset,
inputDataLength,
outputDataOffset,
outputDataLength,
// As far as CALLCODE is concerned, there isn't a value transfer
Wei.ZERO,
recipient,
to,
accountIsWarm);
}
@Override
protected Address sender(final MessageFrame frame) {
return frame.getRecipientAddress();

@ -20,7 +20,6 @@ import static org.hyperledger.besu.evm.internal.Words.clampedToInt;
import static org.hyperledger.besu.evm.internal.Words.clampedToLong;
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.frame.MessageFrame;
@ -57,12 +56,6 @@ public class Create2Operation extends AbstractCreateOperation {
gasCalculator().createKeccakCost(inputSize), gasCalculator().initcodeCost(inputSize)));
}
@Override
protected long statelessCost(
final MessageFrame frame, final Address contractAddress, final Wei value) {
return gasCalculator().initcodeStatelessCost(frame, contractAddress, value);
}
@Override
public Address generateTargetContractAddress(final MessageFrame frame, final Code initcode) {
final Address sender = frame.getRecipientAddress();

@ -19,7 +19,6 @@ import static org.hyperledger.besu.evm.internal.Words.clampedToInt;
import static org.hyperledger.besu.evm.internal.Words.clampedToLong;
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;
@ -53,12 +52,6 @@ public class CreateOperation extends AbstractCreateOperation {
gasCalculator().initcodeCost(inputSize));
}
@Override
protected long statelessCost(
final MessageFrame frame, final Address contractAddress, final Wei value) {
return gasCalculator().initcodeStatelessCost(frame, contractAddress, value);
}
@Override
protected Address generateTargetContractAddress(final MessageFrame frame, final Code initcode) {
final Account sender = frame.getWorldUpdater().get(frame.getRecipientAddress());

@ -20,7 +20,6 @@ import static org.hyperledger.besu.evm.internal.Words.clampedToInt;
import static org.hyperledger.besu.evm.internal.Words.clampedToLong;
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.frame.MessageFrame;
@ -59,12 +58,6 @@ public class EOFCreateOperation extends AbstractCreateOperation {
gasCalculator().createKeccakCost(codeSupplier.get().getSize())));
}
@Override
protected long statelessCost(
final MessageFrame frame, final Address contractAddress, final Wei value) {
return gasCalculator().initcodeStatelessCost(frame, contractAddress, value);
}
@Override
public Address generateTargetContractAddress(final MessageFrame frame, final Code initcode) {
final Address sender = frame.getRecipientAddress();

@ -103,7 +103,6 @@ public class ExtCodeCopyOperation extends AbstractOperation {
}
}
// TODO get account and get code before cost checking only for verkle ? maybe move this call
final Bytes code = account != null ? account.getCode() : Bytes.EMPTY;
final long cost =

@ -24,13 +24,9 @@ import org.hyperledger.besu.evm.code.EOFLayout;
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.OverflowException;
import org.hyperledger.besu.evm.internal.UnderflowException;
import org.hyperledger.besu.evm.internal.Words;
import org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
/** The Ext code hash operation. */
@ -65,64 +61,49 @@ public class ExtCodeHashOperation extends AbstractOperation {
* Cost of Ext code hash operation.
*
* @param frame the current frame
* @param maybeAddress the address to use
* @param address the address to use
* @param accountIsWarm the account is warm
* @return the long
*/
protected long cost(
final MessageFrame frame, final Optional<Address> maybeAddress, final boolean accountIsWarm) {
return gasCalculator().extCodeHashOperationGasCost(frame, accountIsWarm, maybeAddress);
final MessageFrame frame, final Address address, final boolean accountIsWarm) {
return gasCalculator().extCodeHashOperationGasCost(frame, accountIsWarm, address);
}
@Override
public OperationResult execute(final MessageFrame frame, final EVM evm) {
final Address address;
try {
address = Words.toAddress(frame.popStackItem());
} catch (final UnderflowException ufe) {
// TODO not sure about this case, we need to check what is the gas cost in case of underflow
// exception
return new OperationResult(
cost(frame, Optional.empty(), true), ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS);
final Address address = Words.toAddress(frame.popStackItem());
final boolean accountIsWarm =
frame.warmUpAddress(address) || gasCalculator().isPrecompile(address);
final long cost = cost(frame, address, accountIsWarm);
if (frame.getRemainingGas() < cost) {
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
}
try {
final boolean accountIsWarm =
frame.warmUpAddress(address) || gasCalculator().isPrecompile(address);
final long cost = cost(frame, Optional.of(address), accountIsWarm);
if (frame.getRemainingGas() < cost) {
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
}
final Account account = frame.getWorldUpdater().get(address);
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) {
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);
if (account == null || account.isEmpty()) {
frame.pushStackItem(Bytes.EMPTY);
} else {
final Bytes code = account.getCode();
if (enableEIP3540
&& code.size() >= 2
&& code.get(0) == EOFLayout.EOF_PREFIX_BYTE
&& code.get(1) == 0) {
frame.pushStackItem(EOF_REPLACEMENT_HASH);
} else {
final Bytes code = account.getCode();
if (enableEIP3540
&& code.size() >= 2
&& code.get(0) == EOFLayout.EOF_PREFIX_BYTE
&& code.get(1) == 0) {
frame.pushStackItem(EOF_REPLACEMENT_HASH);
} else {
frame.pushStackItem(account.getCodeHash());
}
frame.pushStackItem(account.getCodeHash());
}
return new OperationResult(cost, null);
} catch (final OverflowException ofe) {
// TODO not sure about this case, we need to check what is the gas cost in case of overflow
// exception
return new OperationResult(
cost(frame, Optional.of(address), true), ExceptionalHaltReason.TOO_MANY_STACK_ITEMS);
}
return new OperationResult(cost, null);
}
}

@ -23,13 +23,9 @@ import org.hyperledger.besu.evm.code.EOFLayout;
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.OverflowException;
import org.hyperledger.besu.evm.internal.UnderflowException;
import org.hyperledger.besu.evm.internal.Words;
import org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
/** The Ext code size operation. */
@ -66,57 +62,46 @@ public class ExtCodeSizeOperation extends AbstractOperation {
* @return the long
*/
protected long cost(
final boolean accountIsWarm, final MessageFrame frame, final Optional<Address> maybeAddress) {
return gasCalculator().getExtCodeSizeOperationGasCost(frame, accountIsWarm, maybeAddress);
final boolean accountIsWarm, final MessageFrame frame, final Address address) {
return gasCalculator().extCodeSizeOperationGasCost(frame, accountIsWarm, address);
}
@Override
public OperationResult execute(final MessageFrame frame, final EVM evm) {
final Address address;
try {
address = Words.toAddress(frame.popStackItem());
} catch (final UnderflowException ufe) {
return new OperationResult(
cost(true, frame, Optional.empty()), ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS);
}
try {
final boolean accountIsWarm =
frame.warmUpAddress(address) || gasCalculator().isPrecompile(address);
final long cost = cost(accountIsWarm, frame, Optional.of(address));
if (frame.getRemainingGas() < cost) {
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
} else {
final Account account = frame.getWorldUpdater().get(address);
final Address address = Words.toAddress(frame.popStackItem());
final boolean accountIsWarm =
frame.warmUpAddress(address) || gasCalculator().isPrecompile(address);
final long cost = cost(accountIsWarm, frame, address);
if (frame.getRemainingGas() < cost) {
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
} 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);
}
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;
Bytes codeSize;
if (account == null) {
codeSize = Bytes.EMPTY;
} else {
final Bytes code = account.getCode();
if (enableEIP3540
&& code.size() >= 2
&& code.get(0) == EOFLayout.EOF_PREFIX_BYTE
&& code.get(1) == 0) {
codeSize = EOF_SIZE;
} else {
final Bytes code = account.getCode();
if (enableEIP3540
&& code.size() >= 2
&& code.get(0) == EOFLayout.EOF_PREFIX_BYTE
&& code.get(1) == 0) {
codeSize = EOF_SIZE;
} else {
codeSize = Words.intBytes(code.size());
}
codeSize = Words.intBytes(code.size());
}
frame.pushStackItem(codeSize);
return new OperationResult(cost, null);
}
} catch (final OverflowException ofe) {
return new OperationResult(
cost(true, frame, Optional.of(address)), ExceptionalHaltReason.TOO_MANY_STACK_ITEMS);
frame.pushStackItem(codeSize);
return new OperationResult(cost, null);
}
}
}

@ -54,7 +54,7 @@ public class PushOperation extends AbstractOperation {
if (code.length <= copyStart) {
push = Bytes.EMPTY;
} else {
final int copyLength = Math.min(length, code.length - pc - 1);
final int copyLength = Math.min(length, code.length - copyStart);
push = Bytes.wrap(code, copyStart, copyLength);
}
frame.pushStackItem(push);

@ -39,7 +39,7 @@ public class SLoadOperation extends AbstractOperation {
}
protected long cost(final MessageFrame frame, final Bytes32 key, final boolean slotIsWarm) {
return gasCalculator().getSloadOperationGasCost(frame, UInt256.fromBytes(key), slotIsWarm);
return gasCalculator().sloadOperationGasCost(frame, UInt256.fromBytes(key), slotIsWarm);
}
@Override

@ -99,6 +99,9 @@ public class ContractCreationProcessor extends AbstractMessageProcessor {
Address contractAddress = frame.getContractAddress();
final MutableAccount contract = frame.getWorldUpdater().getOrCreate(contractAddress);
long statelessGasCost =
evm.getGasCalculator().proofOfAbsenceCost(frame, contract.getAddress());
frame.decrementRemainingGas(statelessGasCost);
if (accountExists(contract)) {
LOG.trace(
"Contract creation error: account has already been created for address {}",

@ -116,12 +116,6 @@ class AbstractCreateOperationTest {
gasCalculator().initcodeCost(inputSize));
}
@Override
protected long statelessCost(
final MessageFrame frame, final Address contractAddress, final Wei value) {
return gasCalculator().initcodeStatelessCost(frame, contractAddress, value);
}
@Override
protected Address generateTargetContractAddress(final MessageFrame frame, final Code initcode) {
final Account sender = frame.getWorldUpdater().get(frame.getRecipientAddress());

Loading…
Cancel
Save