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. 124
      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. 14
      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. 363
      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. 417
      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. 25
      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. 29
      evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeHashOperation.java
  28. 23
      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; package org.hyperledger.besu.datatypes;
import java.util.List; import java.util.Optional;
import org.apache.tuweni.units.bigints.UInt256; 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 { public interface AccessWitness {
List<Address> keys(); /**
* Touch witness for all required accesses during a CALL when value is transferred.
long touchAndChargeProofOfAbsence(Address address); *
* @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);
long touchAndChargeValueTransfer(Address caller, Address target); /**
* 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);
long touchAndChargeMessageCall(Address address); /**
* 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);
long touchTxOriginAndComputeGas(Address origin); /**
* 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);
long touchTxExistingAndComputeGas(Address target, boolean sendsValue); /**
* 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 touchAndChargeContractCreateInit(Address address, boolean createSendsValue); /**
* 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);
/**
* 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); long touchAndChargeContractCreateCompleted(final Address address);
long touchAddressOnWriteAndComputeGas(Address address, UInt256 treeIndex, UInt256 subIndex); /**
* Touch witness for read access to a given account's storage location during an SLOAD operation.
long touchAddressOnReadAndComputeGas(Address address, UInt256 treeIndex, UInt256 subIndex); *
* @param address account address to read the storage for
List<UInt256> getStorageSlotTreeIndexes(UInt256 storageKey); * @param storageKey key to the account storage location in the trie
* @return stateless costs associated with storage access for the given key
long touchCodeChunksUponContractCreation(Address address, long codeLength); */
long touchAndChargeStorageLoad(Address address, UInt256 storageKey);
long touchCodeChunks(Address address, long offset, long readSize, long codeLength); /**
* 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);
default long touchCodeChunksWithoutAccessCost( /**
final Address address, final long offset, final long readSize, final long codeLength) { * Touch witness for reading code chunks of an account.
return touchCodeChunks(address, offset, readSize, codeLength); *
} * @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) { if (warmCoinbase) {
warmAddressList.add(miningBeneficiary); warmAddressList.add(miningBeneficiary);
} }
final AccessWitness accessWitness = gasCalculator.newAccessWitness();
final long intrinsicGas = final long intrinsicGas =
gasCalculator.transactionIntrinsicGasCost( gasCalculator.transactionIntrinsicGasCost(
transaction.getPayload(), transaction.isContractCreation()); transaction.getPayload(), transaction.isContractCreation());
final long accessListGas = final long accessListGas =
gasCalculator.accessListGasCost(accessListEntries.size(), accessListStorageCount); gasCalculator.accessListGasCost(accessListEntries.size(), accessListStorageCount);
final long accessEventCost =
gasCalculator.computeBaseAccessEventsCost(accessWitness, transaction, sender);
final long codeDelegationGas = final long codeDelegationGas =
gasCalculator.delegateCodeGasCost(transaction.codeDelegationListSize()); gasCalculator.delegateCodeGasCost(transaction.codeDelegationListSize());
final long gasAvailable = final long gasAvailable =
transaction.getGasLimit() transaction.getGasLimit() - intrinsicGas - accessListGas - codeDelegationGas;
- intrinsicGas
- accessListGas
- codeDelegationGas
- accessEventCost;
LOG.trace( LOG.trace(
"Gas available for execution {} = {} - {} - {} - {} (limit - intrinsic - accessList - codeDelegation)", "Gas available for execution {} = {} - {} - {} - {} (limit - intrinsic - accessList - codeDelegation)",
gasAvailable, gasAvailable,
@ -403,6 +396,10 @@ public class MainnetTransactionProcessor {
accessListGas, accessListGas,
codeDelegationGas); codeDelegationGas);
final AccessWitness accessWitness = gasCalculator.newAccessWitness();
accessWitness.touchBaseTx(
transaction.getSender(), transaction.getTo(), transaction.getValue());
final WorldUpdater worldUpdater = evmWorldUpdater.updater(); final WorldUpdater worldUpdater = evmWorldUpdater.updater();
final ImmutableMap.Builder<String, Object> contextVariablesBuilder = final ImmutableMap.Builder<String, Object> contextVariablesBuilder =
ImmutableMap.<String, Object>builder() ImmutableMap.<String, Object>builder()
@ -526,8 +523,6 @@ public class MainnetTransactionProcessor {
.log(); .log();
final long gasUsedByTransaction = transaction.getGasLimit() - initialFrame.getRemainingGas(); final long gasUsedByTransaction = transaction.getGasLimit() - initialFrame.getRemainingGas();
// checkTransactionGas(transaction.getHash(), gasUsedByTransaction);
// update the coinbase // update the coinbase
final long usedGas = transaction.getGasLimit() - refundedGas; final long usedGas = transaction.getGasLimit() - refundedGas;
final CoinbaseFeePriceCalculator coinbaseCalculator; final CoinbaseFeePriceCalculator coinbaseCalculator;

@ -44,6 +44,7 @@ import org.hyperledger.besu.plugin.services.storage.SegmentIdentifier;
import org.hyperledger.besu.plugin.services.storage.SegmentedKeyValueStorageTransaction; import org.hyperledger.besu.plugin.services.storage.SegmentedKeyValueStorageTransaction;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -435,7 +436,7 @@ public class VerkleWorldState extends DiffBasedWorldState {
@Override @Override
public Map<Bytes32, Bytes> getAllAccountStorage(final Address address, final Hash rootHash) { 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) { private VerkleTrie createTrie(final NodeLoader nodeLoader, final Bytes32 rootHash) {

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

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

@ -1526,6 +1526,8 @@ public class MainnetEVMs {
final BigInteger chainID) { final BigInteger chainID) {
// basing off of shanghai for devnet-6 // basing off of shanghai for devnet-6
registerShanghaiOperations(registry, gasCalculator, chainID); 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 // mimic a weird behavior by geth that ignores eip-1706
registry.put(new SStoreOperation(gasCalculator, 272L)); registry.put(new SStoreOperation(gasCalculator, 272L));
} }

@ -90,7 +90,7 @@ public class EVMExecutor {
private ContractCreationProcessor contractCreationProcessor = null; private ContractCreationProcessor contractCreationProcessor = null;
private MessageFrame.Type messageFrameType = MessageFrame.Type.MESSAGE_CALL; 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) { private EVMExecutor(final EVM evm) {
checkNotNull(evm, "evm must not be null"); 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.VersionedHash;
import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.Code; 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.MemoryEntry;
import org.hyperledger.besu.evm.internal.OperandStack; import org.hyperledger.besu.evm.internal.OperandStack;
import org.hyperledger.besu.evm.internal.ReturnStack; import org.hyperledger.besu.evm.internal.ReturnStack;
@ -1364,7 +1365,7 @@ public class MessageFrame {
private Optional<List<VersionedHash>> versionedHashes = Optional.empty(); private Optional<List<VersionedHash>> versionedHashes = Optional.empty();
private AccessWitness accessWitness = null; private AccessWitness accessWitness = NoopAccessWitness.get();
/** Instantiates a new Builder. */ /** Instantiates a new Builder. */
public Builder() { public Builder() {

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

@ -15,25 +15,24 @@
package org.hyperledger.besu.evm.gascalculator; package org.hyperledger.besu.evm.gascalculator;
import static org.hyperledger.besu.datatypes.Address.KZG_POINT_EVAL; 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 static org.hyperledger.besu.evm.internal.Words.clampedAdd;
import org.hyperledger.besu.datatypes.AccessWitness; import org.hyperledger.besu.datatypes.AccessWitness;
import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Transaction;
import org.hyperledger.besu.datatypes.Wei; 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.Account;
import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.stateless.Eip4762AccessWitness; import org.hyperledger.besu.evm.gascalculator.stateless.Eip4762AccessWitness;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.apache.tuweni.units.bigints.UInt256; import org.apache.tuweni.units.bigints.UInt256;
public class Eip4762GasCalculator extends PragueGasCalculator { 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; private static final long CREATE_OPERATION_GAS_COST = 1_000L;
/** Instantiates a new Prague Gas Calculator. */ /** Instantiates a new Prague Gas Calculator. */
@ -52,14 +51,8 @@ public class Eip4762GasCalculator extends PragueGasCalculator {
} }
@Override @Override
public long initcodeCost(final int initCodeLength) { public long proofOfAbsenceCost(final MessageFrame frame, final Address address) {
return super.initcodeCost(initCodeLength); return frame.getAccessWitness().touchAndChargeProofOfAbsence(address);
}
@Override
public long initcodeStatelessCost(
final MessageFrame frame, final Address address, final Wei value) {
return frame.getAccessWitness().touchAndChargeContractCreateInit(address, !value.isZero());
} }
@Override @Override
@ -75,7 +68,7 @@ public class Eip4762GasCalculator extends PragueGasCalculator {
final Address to, final Address to,
final boolean accountIsWarm) { final boolean accountIsWarm) {
long gasCost = long gas =
super.callOperationGasCost( super.callOperationGasCost(
frame, frame,
stipend, stipend,
@ -86,27 +79,35 @@ public class Eip4762GasCalculator extends PragueGasCalculator {
transferValue, transferValue,
recipient, recipient,
to, to,
true); false);
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()) { if (!transferValue.isZero()) {
statelessGas = gas =
clampedAdd( clampedAdd(
statelessGas, gas,
frame.getAccessWitness().touchAndChargeValueTransfer(recipient.getAddress(), to)); frame
.getAccessWitness()
.touchAndChargeValueTransfer(
frame.getContractAddress(), to, recipient == null, getWarmStorageReadCost()));
return gas;
} }
if (statelessGas == 0) {
return getWarmStorageReadCost(); if (isPrecompile(to) || isSystemContract(to)) {
return clampedAdd(gas, getWarmStorageReadCost());
} }
return clampedAdd(gasCost, statelessGas);
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 @Override
@ -121,38 +122,6 @@ public class Eip4762GasCalculator extends PragueGasCalculator {
.touchCodeChunksUponContractCreation(frame.getContractAddress(), codeSize); .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 @Override
public long calculateStorageRefundAmount( public long calculateStorageRefundAmount(
final UInt256 newValue, final UInt256 newValue,
@ -170,24 +139,26 @@ public class Eip4762GasCalculator extends PragueGasCalculator {
final long codeOffset, final long codeOffset,
final long readSize, final long readSize,
final long codeSize) { final long codeSize) {
long gasCost = extCodeCopyOperationGasCost(frame, memOffset, readSize); long gas = extCodeCopyOperationGasCost(frame, memOffset, readSize);
long statelessGas = gas =
clampedAdd(
gas,
frame frame
.getAccessWitness() .getAccessWitness()
.touchAddressOnReadAndComputeGas(address, UInt256.ZERO, Parameters.BASIC_DATA_LEAF_KEY); .touchCodeChunks(address, false, codeOffset, readSize, codeSize));
if (statelessGas == 0) {
statelessGas = getWarmStorageReadCost(); if (isPrecompile(address) || isSystemContract(address)) {
return clampedAdd(gas, getWarmStorageReadCost());
} }
if (!frame.wasCreatedInTransaction(frame.getContractAddress())) { long readTargetGas =
statelessGas = frame.getAccessWitness().touchAddressAndChargeRead(address, BASIC_DATA_LEAF_KEY);
clampedAdd( if (readTargetGas == 0) {
statelessGas, readTargetGas = getWarmStorageReadCost();
frame.getAccessWitness().touchCodeChunks(address, codeOffset, readSize, codeSize));
} }
return clampedAdd(gasCost, statelessGas); return clampedAdd(gas, readTargetGas);
} }
@Override @Override
@ -197,109 +168,88 @@ public class Eip4762GasCalculator extends PragueGasCalculator {
final long codeOffset, final long codeOffset,
final long readSize, final long readSize,
final long codeSize) { final long codeSize) {
long gasCost = super.dataCopyOperationGasCost(frame, memOffset, readSize); long gas = super.dataCopyOperationGasCost(frame, memOffset, readSize);
if (!frame.wasCreatedInTransaction(frame.getContractAddress())) {
gasCost = final Address contractAddress = frame.getContractAddress();
gas =
clampedAdd( clampedAdd(
gasCost, gas,
frame frame
.getAccessWitness() .getAccessWitness()
.touchCodeChunks(frame.getContractAddress(), codeOffset, readSize, codeSize)); .touchCodeChunks(
} contractAddress,
return gasCost; frame.wasCreatedInTransaction(contractAddress),
codeOffset,
readSize,
codeSize));
return gas;
} }
@Override @Override
public long pushOperationGasCost( public long pushOperationGasCost(
final MessageFrame frame, final long codeOffset, final long readSize, final long codeSize) { final MessageFrame frame, final long codeOffset, final long readSize, final long codeSize) {
long gasCost = super.pushOperationGasCost(frame, codeOffset, readSize, codeSize); long gas = super.pushOperationGasCost(frame, codeOffset, readSize, codeSize);
if (!frame.wasCreatedInTransaction(frame.getContractAddress())) {
if (readSize == 1) { if (frame.wasCreatedInTransaction(frame.getContractAddress())
if ((codeOffset % 31 == 0)) { // PUSH1 only touches code chunks if borderline
gasCost = || (readSize == 1 && codeOffset % 31 != 0)) {
clampedAdd( return gas;
gasCost,
frame
.getAccessWitness()
.touchCodeChunks(
frame.getContractAddress(), codeOffset + 1, readSize, codeSize));
} }
} else {
gasCost = final Address contractAddress = frame.getContractAddress();
gas =
clampedAdd( clampedAdd(
gasCost, gas,
frame frame
.getAccessWitness() .getAccessWitness()
.touchCodeChunks(frame.getContractAddress(), codeOffset, readSize, codeSize)); .touchCodeChunks(
} contractAddress,
} frame.wasCreatedInTransaction(contractAddress),
return gasCost; codeOffset,
readSize,
codeSize));
return gas;
} }
@Override @Override
public long getBalanceOperationGasCost( public long balanceOperationGasCost(
final MessageFrame frame, final boolean accountIsWarm, final Optional<Address> maybeAddress) { final MessageFrame frame, final boolean accountIsWarm, final Address address) {
if (maybeAddress.isPresent()) { final long gas =
final Address address = maybeAddress.get(); frame.getAccessWitness().touchAddressAndChargeRead(address, BASIC_DATA_LEAF_KEY);
final long statelessGas = if (gas == 0) {
frame
.getAccessWitness()
.touchAddressOnReadAndComputeGas(
address, UInt256.ZERO, Parameters.BASIC_DATA_LEAF_KEY);
if (statelessGas == 0) {
return getWarmStorageReadCost(); return getWarmStorageReadCost();
} else {
return statelessGas;
}
} }
return 0L; return gas;
} }
@Override @Override
public long extCodeHashOperationGasCost( public long extCodeHashOperationGasCost(
final MessageFrame frame, final boolean accountIsWarm, final Optional<Address> maybeAddress) { final MessageFrame frame, final boolean accountIsWarm, final Address address) {
if (maybeAddress.isPresent()) { if (isPrecompile(address) || isSystemContract(address)) {
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(); return getWarmStorageReadCost();
} else {
return statelessGas;
}
} }
long gas = frame.getAccessWitness().touchAddressAndChargeRead(address, CODE_HASH_LEAF_KEY);
if (gas == 0) {
return getWarmStorageReadCost();
} }
return 0L; return gas;
} }
@Override @Override
public long getExtCodeSizeOperationGasCost( public long extCodeSizeOperationGasCost(
final MessageFrame frame, final boolean accountIsWarm, final Optional<Address> maybeAddress) { final MessageFrame frame, final boolean accountIsWarm, final Address address) {
if (isPrecompile(address) || isSystemContract(address)) {
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(); return getWarmStorageReadCost();
} else {
return statelessGas;
}
} }
long gas = frame.getAccessWitness().touchAddressAndChargeRead(address, BASIC_DATA_LEAF_KEY);
if (gas == 0) {
return getWarmStorageReadCost();
} }
return 0L; return gas;
} }
@Override @Override
@ -307,94 +257,85 @@ public class Eip4762GasCalculator extends PragueGasCalculator {
final MessageFrame frame, final MessageFrame frame,
final Account recipient, final Account recipient,
final Address recipientAddress, final Address recipientAddress,
final Wei inheritance, final Wei value,
final Address originatorAddress) { final Address originatorAddress) {
final long gasCost = // TODO: only charge non-account creation for devnet-7. Geth has removed the 25k gas cost
super.selfDestructOperationGasCost( // from EIP-150 for account
frame, recipient, recipientAddress, inheritance, originatorAddress); // creation by mistake and will change in the next devnet
if (isPrecompile(recipientAddress)) { // long gas = super.selfDestructOperationGasCost(
return gasCost; // frame, recipient, recipientAddress, value, originatorAddress);
} else { long gas = 5000L;
long statelessGas =
frame if (!value.isZero()) {
.getAccessWitness() gas =
.touchAddressOnReadAndComputeGas(
originatorAddress, UInt256.ZERO, Parameters.BASIC_DATA_LEAF_KEY);
if (!originatorAddress.equals(recipientAddress)) {
statelessGas =
clampedAdd( clampedAdd(
statelessGas, gas,
frame frame
.getAccessWitness() .getAccessWitness()
.touchAddressOnReadAndComputeGas( .touchAndChargeValueTransferSelfDestruct(
recipientAddress, UInt256.ZERO, Parameters.BASIC_DATA_LEAF_KEY)); originatorAddress,
recipientAddress,
recipient == null,
getWarmStorageReadCost()));
} }
if (!inheritance.isZero()) {
statelessGas = // check if there's any balance in the originating account in search of value
clampedAdd( gas =
statelessGas,
frame
.getAccessWitness()
.touchAddressOnWriteAndComputeGas(
originatorAddress, UInt256.ZERO, Parameters.BASIC_DATA_LEAF_KEY));
if (!originatorAddress.equals(recipientAddress)) {
statelessGas =
clampedAdd( clampedAdd(
statelessGas, gas,
frame frame
.getAccessWitness() .getAccessWitness()
.touchAddressOnWriteAndComputeGas( .touchAddressAndChargeRead(originatorAddress, BASIC_DATA_LEAF_KEY));
recipientAddress, UInt256.ZERO, Parameters.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;
} }
if (recipient == null) {
statelessGas = // 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( clampedAdd(
statelessGas, gas,
frame frame
.getAccessWitness() .getAccessWitness()
.touchAddressOnWriteAndComputeGas( .touchAddressAndChargeRead(recipientAddress, BASIC_DATA_LEAF_KEY));
recipientAddress, UInt256.ZERO, Parameters.BASIC_DATA_LEAF_KEY));
}
}
return clampedAdd(gasCost, statelessGas);
} }
return gas;
} }
@Override @Override
public long getSloadOperationGasCost( public long sloadOperationGasCost(
final MessageFrame frame, final UInt256 key, final boolean slotIsWarm) { final MessageFrame frame, final UInt256 key, final boolean slotIsWarm) {
AccessWitness accessWitness = frame.getAccessWitness(); long gas = frame.getAccessWitness().touchAndChargeStorageLoad(frame.getContractAddress(), key);
List<UInt256> treeIndexes = accessWitness.getStorageSlotTreeIndexes(key);
long gasCost = if (gas == 0) {
frame
.getAccessWitness()
.touchAddressOnReadAndComputeGas(
frame.getContractAddress(), treeIndexes.get(0), treeIndexes.get(1));
if (gasCost == 0) {
return getWarmStorageReadCost(); return getWarmStorageReadCost();
} }
return gasCost;
return gas;
} }
@Override @Override
public long computeBaseAccessEventsCost( public long calculateStorageCost(
final AccessWitness accessWitness, final MessageFrame frame,
final Transaction transaction, final UInt256 key,
final MutableAccount sender) { final UInt256 newValue,
final boolean sendsValue = !transaction.getValue().equals(Wei.ZERO); final Supplier<UInt256> currentValue,
long cost = 0; final Supplier<UInt256> originalValue) {
cost += accessWitness.touchTxOriginAndComputeGas(transaction.getSender());
long gas =
frame
.getAccessWitness()
.touchAndChargeStorageStore(frame.getRecipientAddress(), key, originalValue != null);
if (transaction.getTo().isPresent()) { if (gas == 0) {
final Address to = transaction.getTo().get(); return SLOAD_GAS;
cost += accessWitness.touchTxExistingAndComputeGas(to, sendsValue);
} else {
cost +=
accessWitness.touchAndChargeContractCreateInit(
Address.contractAddress(transaction.getSender(), sender.getNonce() - 1L), sendsValue);
} }
return cost; return gas;
} }
@Override @Override
@ -404,6 +345,10 @@ public class Eip4762GasCalculator extends PragueGasCalculator {
.touchAndChargeContractCreateCompleted(frame.getContractAddress()); .touchAndChargeContractCreateCompleted(frame.getContractAddress());
} }
private static boolean isSystemContract(final Address address) {
return HISTORY_STORAGE_ADDRESS.equals(address);
}
@Override @Override
public AccessWitness newAccessWitness() { public AccessWitness newAccessWitness() {
return new Eip4762AccessWitness(); 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.frame.MessageFrame;
import org.hyperledger.besu.evm.operation.ExpOperation; import org.hyperledger.besu.evm.operation.ExpOperation;
import java.util.Optional;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
@ -414,8 +413,8 @@ public class FrontierGasCalculator implements GasCalculator {
} }
@Override @Override
public long getBalanceOperationGasCost( public long balanceOperationGasCost(
final MessageFrame frame, final boolean accountIsWarm, final Optional<Address> maybeAddress) { final MessageFrame frame, final boolean accountIsWarm, final Address address) {
return BALANCE_OPERATION_GAS_COST; return BALANCE_OPERATION_GAS_COST;
} }
@ -468,14 +467,14 @@ public class FrontierGasCalculator implements GasCalculator {
@Override @Override
public long extCodeHashOperationGasCost( 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( throw new UnsupportedOperationException(
"EXTCODEHASH not supported by " + getClass().getSimpleName()); "EXTCODEHASH not supported by " + getClass().getSimpleName());
} }
@Override @Override
public long getExtCodeSizeOperationGasCost( public long extCodeSizeOperationGasCost(
final MessageFrame frame, final boolean accountIsWarm, final Optional<Address> maybeAddress) { final MessageFrame frame, final boolean accountIsWarm, final Address address) {
return extCodeBaseGasCost(); return extCodeBaseGasCost();
} }
@ -533,7 +532,7 @@ public class FrontierGasCalculator implements GasCalculator {
} }
@Override @Override
public long getSloadOperationGasCost( public long sloadOperationGasCost(
final MessageFrame frame, final UInt256 key, final boolean slotIsWarm) { final MessageFrame frame, final UInt256 key, final boolean slotIsWarm) {
return SLOAD_OPERATION_GAS_COST; 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.AccessListEntry;
import org.hyperledger.besu.datatypes.AccessWitness; import org.hyperledger.besu.datatypes.AccessWitness;
import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Transaction;
import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.account.Account; 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.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.stateless.NoopAccessWitness; import org.hyperledger.besu.evm.gascalculator.stateless.NoopAccessWitness;
import org.hyperledger.besu.evm.operation.BalanceOperation; 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 org.hyperledger.besu.evm.processor.AbstractMessageProcessor;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
@ -309,8 +306,7 @@ public interface GasCalculator {
*/ */
long initcodeCost(final int initCodeLength); long initcodeCost(final int initCodeLength);
default long initcodeStatelessCost( default long proofOfAbsenceCost(final MessageFrame frame, final Address address) {
final MessageFrame frame, final Address address, final Wei value) {
return 0; return 0;
} }
@ -366,11 +362,11 @@ public interface GasCalculator {
* *
* @param frame The current frame * @param frame The current frame
* @param accountIsWarm true to add warm storage read cost, false to add cold account access cost * @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 * @return the cost for executing the balance operation
*/ */
long getBalanceOperationGasCost( long balanceOperationGasCost(
MessageFrame frame, final boolean accountIsWarm, final Optional<Address> maybeAddress); MessageFrame frame, final boolean accountIsWarm, final Address address);
/** /**
* Returns the cost for executing a {@link BlockHashOperation}. * Returns the cost for executing a {@link BlockHashOperation}.
@ -423,22 +419,22 @@ public interface GasCalculator {
* *
* @param frame The current frame * @param frame The current frame
* @param accountIsWarm true to add warm storage read cost, false to add cold account access cost * @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 * @return the cost for executing the external code hash operation
*/ */
long extCodeHashOperationGasCost( 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}. * Returns the cost for executing a {@link ExtCodeSizeOperation}.
* *
* @param frame The current frame * @param frame The current frame
* @param accountIsWarm true to add warm storage read cost, false to add cold account access cost * @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 * @return the cost for executing the external code size operation
*/ */
long getExtCodeSizeOperationGasCost( long extCodeSizeOperationGasCost(
MessageFrame frame, final boolean accountIsWarm, Optional<Address> maybeAddress); MessageFrame frame, final boolean accountIsWarm, Address address);
/** /**
* Returns the cost for executing a {@link JumpDestOperation}. * Returns the cost for executing a {@link JumpDestOperation}.
@ -517,7 +513,7 @@ public interface GasCalculator {
* *
* @return the cost for executing the storage load operation * @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. * Returns the cost for an SSTORE operation.
@ -752,20 +748,7 @@ public interface GasCalculator {
return 0L; 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() { 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.datatypes.Address;
import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.frame.MessageFrame;
import java.util.Optional;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
@ -127,22 +126,22 @@ public class IstanbulGasCalculator extends PetersburgGasCalculator {
@Override @Override
// As per https://eips.ethereum.org/EIPS/eip-1884 // As per https://eips.ethereum.org/EIPS/eip-1884
public long getSloadOperationGasCost( public long sloadOperationGasCost(
final MessageFrame frame, final UInt256 key, final boolean slotIsWarm) { final MessageFrame frame, final UInt256 key, final boolean slotIsWarm) {
return SLOAD_GAS; return SLOAD_GAS;
} }
@Override @Override
// As per https://eips.ethereum.org/EIPS/eip-1884 // As per https://eips.ethereum.org/EIPS/eip-1884
public long getBalanceOperationGasCost( public long balanceOperationGasCost(
final MessageFrame frame, final boolean accountIsWarm, final Optional<Address> maybeAddress) { final MessageFrame frame, final boolean accountIsWarm, final Address address) {
return BALANCE_OPERATION_GAS_COST; return BALANCE_OPERATION_GAS_COST;
} }
@Override @Override
// As per https://eips.ethereum.org/EIPS/eip-1884 // As per https://eips.ethereum.org/EIPS/eip-1884
public long extCodeHashOperationGasCost( 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; 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.frame.MessageFrame;
import org.hyperledger.besu.evm.internal.Words; import org.hyperledger.besu.evm.internal.Words;
import java.util.Optional;
import org.apache.tuweni.units.bigints.UInt256; import org.apache.tuweni.units.bigints.UInt256;
/** The Tangerine whistle gas calculator. */ /** The Tangerine whistle gas calculator. */
@ -45,8 +43,8 @@ public class TangerineWhistleGasCalculator extends HomesteadGasCalculator {
public TangerineWhistleGasCalculator() {} public TangerineWhistleGasCalculator() {}
@Override @Override
public long getBalanceOperationGasCost( public long balanceOperationGasCost(
final MessageFrame frame, final boolean accountIsWarm, final Optional<Address> maybeAddress) { final MessageFrame frame, final boolean accountIsWarm, final Address address) {
return BALANCE_OPERATION_GAS_COST; return BALANCE_OPERATION_GAS_COST;
} }
@ -137,7 +135,7 @@ public class TangerineWhistleGasCalculator extends HomesteadGasCalculator {
} }
@Override @Override
public long getSloadOperationGasCost( public long sloadOperationGasCost(
final MessageFrame frame, final UInt256 key, final boolean slotIsWarm) { final MessageFrame frame, final UInt256 key, final boolean slotIsWarm) {
return SLOAD_OPERATION_GAS_COST; return SLOAD_OPERATION_GAS_COST;
} }

@ -14,67 +14,34 @@
*/ */
package org.hyperledger.besu.evm.gascalculator.stateless; package org.hyperledger.besu.evm.gascalculator.stateless;
public class AccessEvents { public final class AccessEvents {
private boolean branchRead;
private boolean chunkRead;
private boolean branchWrite;
private boolean chunkWrite;
private boolean chunkFill;
public AccessEvents() { public static final short NONE = 0;
this(false, false, false, false, false); public static final short BRANCH_READ = 1;
} public static final short BRANCH_WRITE = 2;
public static final short LEAF_READ = 4;
public AccessEvents( public static final short LEAF_RESET = 8;
final boolean branchRead, public static final short LEAF_SET = 16;
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 boolean isChunkWrite() { private AccessEvents() {}
return chunkWrite;
}
public boolean isChunkFill() {
return chunkFill;
}
public void setBranchRead(final boolean branchRead) { public static boolean isBranchRead(final short accessEvents) {
this.branchRead = branchRead; return (accessEvents & BRANCH_READ) != 0;
} }
public void setChunkRead(final boolean chunkRead) { public static boolean isBranchWrite(final short accessEvents) {
this.chunkRead = chunkRead; return (accessEvents & BRANCH_WRITE) != 0;
} }
public void setBranchWrite(final boolean branchWrite) { public static boolean isLeafRead(final short accessEvents) {
this.branchWrite = branchWrite; return (accessEvents & LEAF_READ) != 0;
} }
public void setChunkWrite(final boolean chunkWrite) { public static boolean isLeafReset(final short accessEvents) {
this.chunkWrite = chunkWrite; return (accessEvents & LEAF_RESET) != 0;
} }
public void setChunkFill(final boolean chunkFill) { public static boolean isLeafSet(final short accessEvents) {
this.chunkFill = chunkFill; 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.ethereum.trie.verkle.util.Parameters.VERKLE_NODE_WIDTH;
import static org.hyperledger.besu.evm.internal.Words.clampedAdd; 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.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.trie.verkle.adapter.TrieKeyAdapter; import org.hyperledger.besu.ethereum.trie.verkle.adapter.TrieKeyAdapter;
import org.hyperledger.besu.ethereum.trie.verkle.hasher.PedersenHasher; import org.hyperledger.besu.ethereum.trie.verkle.hasher.PedersenHasher;
@ -28,146 +30,174 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional;
import org.apache.tuweni.units.bigints.UInt256; 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 TrieKeyAdapter TRIE_KEY_ADAPTER = new TrieKeyAdapter(new PedersenHasher());
private static final long WITNESS_BRANCH_READ_COST = 1900; private static final long WITNESS_BRANCH_COST = 1900;
private static final long WITNESS_CHUNK_READ_COST = 200; private static final long WITNESS_CHUNK_COST = 200;
private static final long WITNESS_BRANCH_WRITE_COST = 3000; private static final long SUBTREE_EDIT_COST = 3000;
private static final long WITNESS_CHUNK_WRITE_COST = 500; private static final long CHUNK_EDIT_COST = 500;
private static final long WITNESS_CHUNK_FILL_COST = 6200; private static final long CHUNK_FILL_COST = 6200;
private static final UInt256 zeroTreeIndex = UInt256.ZERO; private static final UInt256 zeroTreeIndex = UInt256.ZERO;
private static final byte AccessWitnessReadFlag = 1; private final Map<LeafAccessKey, Integer> leaves;
private static final byte AccessWitnessWriteFlag = 2; private final Map<BranchAccessKey, Integer> branches;
private final Map<BranchAccessKey, Byte> branches;
private final Map<ChunkAccessKey, Byte> chunks;
public Eip4762AccessWitness() { public Eip4762AccessWitness() {
this(new HashMap<>(), new HashMap<>()); this(new HashMap<>(), new HashMap<>());
} }
public Eip4762AccessWitness( 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.branches = branches;
this.chunks = chunks; this.leaves = leaves;
} }
@Override @Override
public List<Address> keys() { public long touchAddressAndChargeRead(final Address address, final UInt256 leafKey) {
return this.chunks.keySet().stream() return touchAddressOnReadAndComputeGas(address, zeroTreeIndex, leafKey);
.map(chunkAccessKey -> chunkAccessKey.branchAccessKey().address())
.toList();
} }
@Override @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; long gas = 0;
gas = gas =
clampedAdd( clampedAdd(
gas, touchAddressOnReadAndComputeGas(address, zeroTreeIndex, BASIC_DATA_LEAF_KEY)); gas, touchAddressOnWriteResetAndComputeGas(caller, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
if (isAccountCreation) {
gas = gas =
clampedAdd( clampedAdd(
gas, touchAddressOnReadAndComputeGas(address, zeroTreeIndex, CODE_HASH_LEAF_KEY)); gas, touchAddressOnWriteSetAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
gas =
clampedAdd(
gas, touchAddressOnWriteSetAndComputeGas(target, zeroTreeIndex, CODE_HASH_LEAF_KEY));
return gas; return gas;
} }
@Override long readTargetStatelessGas =
public long touchAndChargeMessageCall(final Address address) { touchAddressOnReadAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY);
return touchAddressOnReadAndComputeGas(address, zeroTreeIndex, BASIC_DATA_LEAF_KEY); if (readTargetStatelessGas == 0) {
readTargetStatelessGas = clampedAdd(readTargetStatelessGas, warmReadCost);
} }
@Override
public long touchAndChargeValueTransfer(final Address caller, final Address target) {
long gas = 0;
gas =
clampedAdd(
gas, touchAddressOnWriteAndComputeGas(caller, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
gas = gas =
clampedAdd( clampedAdd(
gas, touchAddressOnWriteAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY)); gas, touchAddressOnWriteResetAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
return gas; return clampedAdd(gas, readTargetStatelessGas);
} }
@Override @Override
public long touchAndChargeContractCreateInit( public long touchAndChargeValueTransferSelfDestruct(
final Address address, final boolean createSendsValue) { final Address caller,
final Address target,
final boolean isAccountCreation,
final long warmReadCost) {
long gas = 0; long gas = 0;
gas = gas =
clampedAdd( clampedAdd(
gas, touchAddressOnWriteAndComputeGas(address, zeroTreeIndex, BASIC_DATA_LEAF_KEY)); gas, touchAddressOnWriteResetAndComputeGas(caller, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
if (createSendsValue) { if (caller.equals(target)) {
return gas;
}
if (isAccountCreation) {
gas = gas =
clampedAdd( clampedAdd(
gas, touchAddressOnWriteAndComputeGas(address, zeroTreeIndex, BASIC_DATA_LEAF_KEY)); gas, touchAddressOnWriteSetAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
} gas =
clampedAdd(
gas, touchAddressOnWriteSetAndComputeGas(target, zeroTreeIndex, CODE_HASH_LEAF_KEY));
return gas; return gas;
} }
@Override long readTargetStatelessGas =
public long touchAndChargeContractCreateCompleted(final Address address) { touchAddressOnReadAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY);
if (readTargetStatelessGas == 0) {
long gas = 0; readTargetStatelessGas = clampedAdd(readTargetStatelessGas, warmReadCost);
}
gas = gas =
clampedAdd( clampedAdd(
gas, touchAddressOnWriteAndComputeGas(address, zeroTreeIndex, BASIC_DATA_LEAF_KEY)); gas, touchAddressOnWriteResetAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
gas =
clampedAdd(
gas, touchAddressOnWriteAndComputeGas(address, zeroTreeIndex, CODE_HASH_LEAF_KEY));
return gas; return clampedAdd(gas, readTargetStatelessGas);
} }
@SuppressWarnings("unused")
@Override @Override
public long touchTxOriginAndComputeGas(final Address origin) { public long touchAndChargeProofOfAbsence(final Address address) {
long gas = 0; long gas = 0;
gas = gas =
clampedAdd( clampedAdd(
gas, touchAddressOnReadAndComputeGas(origin, zeroTreeIndex, BASIC_DATA_LEAF_KEY)); gas, touchAddressOnReadAndComputeGas(address, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
gas = gas =
clampedAdd( clampedAdd(
gas, touchAddressOnWriteAndComputeGas(origin, zeroTreeIndex, BASIC_DATA_LEAF_KEY)); gas, touchAddressOnReadAndComputeGas(address, zeroTreeIndex, CODE_HASH_LEAF_KEY));
gas =
clampedAdd(gas, touchAddressOnReadAndComputeGas(origin, zeroTreeIndex, CODE_HASH_LEAF_KEY));
// modifying this after update on EIP-4762 to not charge simple transfers
return 0; return gas;
} }
@SuppressWarnings("unused")
@Override @Override
public long touchTxExistingAndComputeGas(final Address target, final boolean sendsValue) { public long touchAndChargeContractCreateCompleted(final Address address) {
long gas = 0; long gas = 0;
gas = gas =
clampedAdd( clampedAdd(
gas, touchAddressOnReadAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY)); gas,
gas = touchAddressOnWriteResetAndComputeGas(address, zeroTreeIndex, BASIC_DATA_LEAF_KEY));
clampedAdd(gas, touchAddressOnReadAndComputeGas(target, zeroTreeIndex, CODE_HASH_LEAF_KEY));
if (sendsValue) {
gas = gas =
clampedAdd( clampedAdd(
gas, touchAddressOnWriteAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY)); gas, touchAddressOnWriteResetAndComputeGas(address, zeroTreeIndex, CODE_HASH_LEAF_KEY));
return gas;
} }
// modifying this after update on EIP-4762 to not charge simple transfers
return 0; @Override
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");
}
private void touchTxOrigin(final Address origin) {
touchAddressOnWriteResetAndComputeGas(origin, zeroTreeIndex, BASIC_DATA_LEAF_KEY);
touchAddressOnReadAndComputeGas(origin, zeroTreeIndex, CODE_HASH_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;
}
// 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 @Override
@ -177,7 +207,7 @@ public class Eip4762AccessWitness implements org.hyperledger.besu.datatypes.Acce
gas = gas =
clampedAdd( clampedAdd(
gas, gas,
touchAddressOnWriteAndComputeGas( touchAddressOnWriteSetAndComputeGas(
address, address,
CODE_OFFSET.add(i).divide(VERKLE_NODE_WIDTH), CODE_OFFSET.add(i).divide(VERKLE_NODE_WIDTH),
CODE_OFFSET.add(i).mod(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 @Override
public long touchCodeChunks( 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; long gas = 0;
if ((readSize == 0 && codeLength == 0) || startPc > codeLength) {
if (isContractInDeployment || readSize == 0 || startPc >= codeLength) {
return 0; return 0;
} }
long endPc = startPc + readSize;
if (endPc > codeLength) { // last byte read is limited by code length, and it is an index, hence the decrement
endPc = codeLength; long endPc = Math.min(startPc + readSize, codeLength) - 1L;
} for (long i = startPc / 31L; i <= endPc / 31L; i++) {
if (endPc > 0) {
endPc -= 1;
}
for (long i = startPc / 31; i <= endPc / 31; i++) {
gas = gas =
clampedAdd( clampedAdd(
gas, gas,
touchAddressOnReadAndComputeGas( touchAddressOnReadAndComputeGas(
address, contractAddress,
CODE_OFFSET.add(i).divide(VERKLE_NODE_WIDTH), CODE_OFFSET.add(i).divide(VERKLE_NODE_WIDTH),
CODE_OFFSET.add(i).mod(VERKLE_NODE_WIDTH))); CODE_OFFSET.add(i).mod(VERKLE_NODE_WIDTH)));
} }
return gas; return gas;
} }
@Override private long touchAddressOnWriteResetAndComputeGas(
public long touchAddressOnWriteAndComputeGas(
final Address address, final UInt256 treeIndex, final UInt256 subIndex) { 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 private long touchAddressOnReadAndComputeGas(
public long touchAddressOnReadAndComputeGas(
final Address address, final UInt256 treeIndex, final UInt256 subIndex) { 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( public long touchAddressAndChargeGas(
final Address address, final Address address,
final UInt256 treeIndex, final UInt256 treeIndex,
final UInt256 subIndex, final UInt256 subIndex,
final boolean isWrite) { final int accessMode) {
final short accessEvents = touchAddress(address, treeIndex, subIndex, accessMode);
AccessEvents accessEvent = touchAddress(address, treeIndex, subIndex, isWrite);
boolean logEnabled = false;
long gas = 0; long gas = 0;
if (accessEvent.isBranchRead()) { if (AccessEvents.isBranchRead(accessEvents)) {
gas = clampedAdd(gas, WITNESS_BRANCH_READ_COST); gas = clampedAdd(gas, WITNESS_BRANCH_COST);
if (logEnabled) { final long gasView = gas;
System.out.println( LOG.atDebug().log(
"touchAddressAndChargeGas WitnessBranchReadCost " () ->
"touchAddressAndChargeGas WITNESS_BRANCH_COST "
+ address + address
+ " " + " "
+ treeIndex + treeIndex
+ " " + " "
+ subIndex + subIndex
+ " " + " "
+ isWrite + AccessMode.toString(accessMode)
+ " " + " "
+ gas); + gasView);
} }
} if (AccessEvents.isLeafRead(accessEvents)) {
if (accessEvent.isChunkRead()) { gas = clampedAdd(gas, WITNESS_CHUNK_COST);
gas = clampedAdd(gas, WITNESS_CHUNK_READ_COST); final long gasView = gas;
if (logEnabled) { LOG.atDebug().log(
System.out.println( () ->
"touchAddressAndChargeGas WitnessChunkReadCost " "touchAddressAndChargeGas WITNESS_CHUNK_COST "
+ address + address
+ " " + " "
+ treeIndex + treeIndex
+ " " + " "
+ subIndex + subIndex
+ " " + " "
+ isWrite + AccessMode.toString(accessMode)
+ " " + " "
+ gas); + gasView);
} }
} if (AccessEvents.isBranchWrite(accessEvents)) {
if (accessEvent.isBranchWrite()) { gas = clampedAdd(gas, SUBTREE_EDIT_COST);
gas = clampedAdd(gas, WITNESS_BRANCH_WRITE_COST); final long gasView = gas;
if (logEnabled) { LOG.atDebug().log(
System.out.println( () ->
"touchAddressAndChargeGas WitnessBranchWriteCost " "touchAddressAndChargeGas SUBTREE_EDIT_COST "
+ address + address
+ " " + " "
+ treeIndex + treeIndex
+ " " + " "
+ subIndex + subIndex
+ " " + " "
+ isWrite + AccessMode.toString(accessMode)
+ " " + " "
+ gas); + gasView);
} }
} if (AccessEvents.isLeafReset(accessEvents)) {
if (accessEvent.isChunkWrite()) { gas = clampedAdd(gas, CHUNK_EDIT_COST);
gas = clampedAdd(gas, WITNESS_CHUNK_WRITE_COST); final long gasView = gas;
if (logEnabled) { LOG.atDebug().log(
System.out.println( () ->
"touchAddressAndChargeGas WitnessChunkWriteCost " "touchAddressAndChargeGas CHUNK_EDIT_COST "
+ address + address
+ " " + " "
+ treeIndex + treeIndex
+ " " + " "
+ subIndex + subIndex
+ " " + " "
+ isWrite + AccessMode.toString(accessMode)
+ " " + " "
+ gas); + gasView);
} }
} if (AccessEvents.isLeafSet(accessEvents)) {
if (accessEvent.isChunkFill()) { gas = clampedAdd(gas, CHUNK_FILL_COST);
gas = clampedAdd(gas, WITNESS_CHUNK_FILL_COST); final long gasView = gas;
if (logEnabled) { LOG.atDebug().log(
System.out.println( () ->
"touchAddressAndChargeGas WitnessChunkFillCost " "touchAddressAndChargeGas CHUNK_FILL_COST "
+ address + address
+ " " + " "
+ treeIndex + treeIndex
+ " " + " "
+ subIndex + subIndex
+ " " + " "
+ isWrite + AccessMode.toString(accessMode)
+ " " + " "
+ gas); + gasView);
}
} }
return gas; return gas;
} }
public AccessEvents touchAddress( public short touchAddress(
final Address addr, final UInt256 treeIndex, final UInt256 subIndex, final boolean isWrite) { final Address addr, final UInt256 treeIndex, final UInt256 leafIndex, final int accessMode) {
AccessEvents accessEvents = new AccessEvents(); short accessEvents = AccessEvents.NONE;
BranchAccessKey branchKey = new BranchAccessKey(addr, treeIndex); 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)) { if (!this.branches.containsKey(branchKey)) {
accessEvents.setBranchRead(true); accessEvents |= AccessEvents.BRANCH_READ;
this.branches.put(branchKey, AccessWitnessReadFlag); this.branches.put(branchKey, AccessMode.READ);
}
// 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 (!this.chunks.containsKey(chunkKey)) {
accessEvents.setChunkRead(true);
this.chunks.put(chunkKey, AccessWitnessReadFlag);
} }
// TODO VERKLE: for now testnet doesn't charge return accessEvents;
// chunk filling costs if the leaf was previously empty in the state }
// boolean chunkFill = false;
if (isWrite) { private short touchAddressForLeaf(
final BranchAccessKey branchKey, final UInt256 subIndex, final int accessMode) {
LeafAccessKey leafKey = new LeafAccessKey(branchKey, subIndex);
short accessEvents = AccessEvents.NONE;
if ((this.branches.get(branchKey) & AccessWitnessWriteFlag) == 0) { if (!this.leaves.containsKey(leafKey)) {
accessEvents.setBranchWrite(true); accessEvents |= AccessEvents.LEAF_READ;
this.branches.put( this.leaves.put(leafKey, AccessMode.READ);
branchKey, (byte) (this.branches.get(branchKey) | AccessWitnessWriteFlag));
} }
byte chunkValue = this.chunks.get(chunkKey); // A write is always a read
if (AccessMode.isWrite(accessMode)) {
if ((chunkValue & AccessWitnessWriteFlag) == 0) { int previousAccessMode =
accessEvents.setChunkWrite(true); !this.leaves.containsKey(leafKey) ? AccessMode.NONE : this.leaves.get(leafKey);
this.chunks.put(chunkKey, (byte) (this.chunks.get(chunkKey) | AccessWitnessWriteFlag)); if (!AccessMode.isWrite(previousAccessMode)) {
accessEvents |= AccessEvents.LEAF_RESET;
if (AccessMode.isWriteSet(accessMode)) {
accessEvents |= AccessEvents.LEAF_SET;
}
this.leaves.put(leafKey, (previousAccessMode | accessMode));
} }
} }
return accessEvents; return accessEvents;
} }
@ -362,41 +436,20 @@ public class Eip4762AccessWitness implements org.hyperledger.besu.datatypes.Acce
if (this == o) return true; if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; if (o == null || getClass() != o.getClass()) return false;
Eip4762AccessWitness that = (Eip4762AccessWitness) o; 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 @Override
public int hashCode() { public int hashCode() {
return Objects.hash(branches, chunks); return Objects.hash(branches, leaves);
} }
@Override @Override
public String toString() { public String toString() {
return "AccessWitness{" + "branches=" + branches + ", chunks=" + chunks + '}'; return "AccessWitness{" + "leaves=" + leaves + ", branches=" + branches + '}';
}
public Map<BranchAccessKey, Byte> getBranches() {
return branches;
}
public Map<ChunkAccessKey, Byte> getChunks() {
return chunks;
} }
public record BranchAccessKey(Address address, UInt256 treeIndex) {} public record BranchAccessKey(Address address, UInt256 treeIndex) {}
;
public record ChunkAccessKey(BranchAccessKey branchAccessKey, UInt256 chunkIndex) { public record LeafAccessKey(BranchAccessKey branchAccessKey, UInt256 leafIndex) {}
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));
}
} }

@ -14,40 +14,55 @@
*/ */
package org.hyperledger.besu.evm.gascalculator.stateless; package org.hyperledger.besu.evm.gascalculator.stateless;
import org.hyperledger.besu.datatypes.AccessWitness;
import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import java.util.Optional;
import org.apache.tuweni.units.bigints.UInt256; import org.apache.tuweni.units.bigints.UInt256;
public class NoopAccessWitness extends Eip4762AccessWitness { public class NoopAccessWitness implements AccessWitness {
@Override private static NoopAccessWitness instance;
public long touchAndChargeProofOfAbsence(final Address address) {
return 0; private NoopAccessWitness() {}
public static NoopAccessWitness get() {
if (instance == null) {
instance = new NoopAccessWitness();
}
return instance;
} }
@Override @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; return 0;
} }
@Override @Override
public long touchAndChargeMessageCall(final Address address) { public long touchAndChargeValueTransferSelfDestruct(
final Address caller,
final Address target,
final boolean isAccountCreation,
final long warmReadCost) {
return 0; return 0;
} }
@Override @Override
public long touchTxOriginAndComputeGas(final Address origin) { public long touchAddressAndChargeRead(final Address address, final UInt256 leafKey) {
return 0; return 0;
} }
@Override @Override
public long touchTxExistingAndComputeGas(final Address target, final boolean sendsValue) { public void touchBaseTx(final Address origin, final Optional<Address> target, final Wei value) {}
return 0;
}
@Override @Override
public long touchAndChargeContractCreateInit( public long touchAndChargeProofOfAbsence(final Address address) {
final Address address, final boolean createSendsValue) {
return 0; return 0;
} }
@ -57,14 +72,13 @@ public class NoopAccessWitness extends Eip4762AccessWitness {
} }
@Override @Override
public long touchAddressOnWriteAndComputeGas( public long touchAndChargeStorageLoad(final Address address, final UInt256 storageKey) {
final Address address, final UInt256 treeIndex, final UInt256 subIndex) {
return 0; return 0;
} }
@Override @Override
public long touchAddressOnReadAndComputeGas( public long touchAndChargeStorageStore(
final Address address, final UInt256 treeIndex, final UInt256 subIndex) { final Address address, final UInt256 storageKey, final boolean hasPreviousValue) {
return 0; return 0;
} }
@ -75,13 +89,11 @@ public class NoopAccessWitness extends Eip4762AccessWitness {
@Override @Override
public long touchCodeChunks( public long touchCodeChunks(
final Address address, final long offset, final long readSize, final long codeLength) { final Address address,
return 0; final boolean isContractInDeployment,
} final long offset,
final long readSize,
@Override final long codeLength) {
public long touchCodeChunksWithoutAccessCost(
final Address address, final long offset, final long readSize, final long codeLength) {
return 0; 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 cost(final MessageFrame frame, Supplier<Code> codeSupplier);
protected abstract long statelessCost(
final MessageFrame frame, final Address contractAddress, final Wei value);
/** /**
* Target contract address. * 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.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator; 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.internal.Words;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
/** The Balance operation. */ /** The Balance operation. */
@ -44,28 +40,21 @@ public class BalanceOperation extends AbstractOperation {
* Gets Balance operation Gas Cost plus warm storage read cost or cold account access cost. * Gets Balance operation Gas Cost plus warm storage read cost or cold account access cost.
* *
* @param frame current frame * @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 * @param accountIsWarm true to add warm storage read cost, false to add cold account access cost
* @return the long * @return the long
*/ */
protected long cost( protected long cost(
final MessageFrame frame, final Optional<Address> maybeAddress, final boolean accountIsWarm) { final MessageFrame frame, final Address address, final boolean accountIsWarm) {
return gasCalculator().getBalanceOperationGasCost(frame, accountIsWarm, maybeAddress); return gasCalculator().balanceOperationGasCost(frame, accountIsWarm, address);
} }
@Override @Override
public OperationResult execute(final MessageFrame frame, final EVM evm) { public OperationResult execute(final MessageFrame frame, final EVM evm) {
final Address address; final Address address = Words.toAddress(frame.popStackItem());
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 = final boolean accountIsWarm =
frame.warmUpAddress(address) || gasCalculator().isPrecompile(address); frame.warmUpAddress(address) || gasCalculator().isPrecompile(address);
final long cost = cost(frame, Optional.of(address), accountIsWarm); final long cost = cost(frame, address, accountIsWarm);
if (frame.getRemainingGas() < cost) { if (frame.getRemainingGas() < cost) {
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS); return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
} else { } else {
@ -73,9 +62,5 @@ public class BalanceOperation extends AbstractOperation {
frame.pushStackItem(account == null ? Bytes.EMPTY : account.getBalance()); frame.pushStackItem(account == null ? Bytes.EMPTY : account.getBalance());
return new OperationResult(cost, null); return new OperationResult(cost, null);
} }
} catch (final OverflowException ofe) {
return new OperationResult(
cost(frame, Optional.of(address), true), ExceptionalHaltReason.TOO_MANY_STACK_ITEMS);
}
} }
} }

@ -18,6 +18,7 @@ import static org.hyperledger.besu.evm.internal.Words.clampedToLong;
import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei; 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.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.internal.Words; import org.hyperledger.besu.evm.internal.Words;
@ -74,6 +75,31 @@ public class CallCodeOperation extends AbstractCallOperation {
return frame.getRecipientAddress(); 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 @Override
protected Address sender(final MessageFrame frame) { protected Address sender(final MessageFrame frame) {
return frame.getRecipientAddress(); 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 static org.hyperledger.besu.evm.internal.Words.clampedToLong;
import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.frame.MessageFrame;
@ -57,12 +56,6 @@ public class Create2Operation extends AbstractCreateOperation {
gasCalculator().createKeccakCost(inputSize), gasCalculator().initcodeCost(inputSize))); 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 @Override
public Address generateTargetContractAddress(final MessageFrame frame, final Code initcode) { public Address generateTargetContractAddress(final MessageFrame frame, final Code initcode) {
final Address sender = frame.getRecipientAddress(); 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 static org.hyperledger.besu.evm.internal.Words.clampedToLong;
import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.account.Account;
@ -53,12 +52,6 @@ public class CreateOperation extends AbstractCreateOperation {
gasCalculator().initcodeCost(inputSize)); gasCalculator().initcodeCost(inputSize));
} }
@Override
protected long statelessCost(
final MessageFrame frame, final Address contractAddress, final Wei value) {
return gasCalculator().initcodeStatelessCost(frame, contractAddress, value);
}
@Override @Override
protected Address generateTargetContractAddress(final MessageFrame frame, final Code initcode) { protected Address generateTargetContractAddress(final MessageFrame frame, final Code initcode) {
final Account sender = frame.getWorldUpdater().get(frame.getRecipientAddress()); 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 static org.hyperledger.besu.evm.internal.Words.clampedToLong;
import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.Code;
import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.frame.MessageFrame;
@ -59,12 +58,6 @@ public class EOFCreateOperation extends AbstractCreateOperation {
gasCalculator().createKeccakCost(codeSupplier.get().getSize()))); 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 @Override
public Address generateTargetContractAddress(final MessageFrame frame, final Code initcode) { public Address generateTargetContractAddress(final MessageFrame frame, final Code initcode) {
final Address sender = frame.getRecipientAddress(); 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 Bytes code = account != null ? account.getCode() : Bytes.EMPTY;
final long cost = 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.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator; 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.internal.Words;
import org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper; import org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
/** The Ext code hash operation. */ /** The Ext code hash operation. */
@ -65,30 +61,21 @@ public class ExtCodeHashOperation extends AbstractOperation {
* Cost of Ext code hash operation. * Cost of Ext code hash operation.
* *
* @param frame the current frame * @param frame the current frame
* @param maybeAddress the address to use * @param address the address to use
* @param accountIsWarm the account is warm * @param accountIsWarm the account is warm
* @return the long * @return the long
*/ */
protected long cost( protected long cost(
final MessageFrame frame, final Optional<Address> maybeAddress, final boolean accountIsWarm) { final MessageFrame frame, final Address address, final boolean accountIsWarm) {
return gasCalculator().extCodeHashOperationGasCost(frame, accountIsWarm, maybeAddress); return gasCalculator().extCodeHashOperationGasCost(frame, accountIsWarm, address);
} }
@Override @Override
public OperationResult execute(final MessageFrame frame, final EVM evm) { public OperationResult execute(final MessageFrame frame, final EVM evm) {
final Address address; final Address address = Words.toAddress(frame.popStackItem());
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);
}
try {
final boolean accountIsWarm = final boolean accountIsWarm =
frame.warmUpAddress(address) || gasCalculator().isPrecompile(address); frame.warmUpAddress(address) || gasCalculator().isPrecompile(address);
final long cost = cost(frame, Optional.of(address), accountIsWarm); final long cost = cost(frame, address, accountIsWarm);
if (frame.getRemainingGas() < cost) { if (frame.getRemainingGas() < cost) {
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS); return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
} }
@ -118,11 +105,5 @@ public class ExtCodeHashOperation extends AbstractOperation {
} }
} }
return new OperationResult(cost, null); 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);
}
} }
} }

@ -23,13 +23,9 @@ import org.hyperledger.besu.evm.code.EOFLayout;
import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.ExceptionalHaltReason;
import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator; 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.internal.Words;
import org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper; import org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
/** The Ext code size operation. */ /** The Ext code size operation. */
@ -66,23 +62,16 @@ public class ExtCodeSizeOperation extends AbstractOperation {
* @return the long * @return the long
*/ */
protected long cost( protected long cost(
final boolean accountIsWarm, final MessageFrame frame, final Optional<Address> maybeAddress) { final boolean accountIsWarm, final MessageFrame frame, final Address address) {
return gasCalculator().getExtCodeSizeOperationGasCost(frame, accountIsWarm, maybeAddress); return gasCalculator().extCodeSizeOperationGasCost(frame, accountIsWarm, address);
} }
@Override @Override
public OperationResult execute(final MessageFrame frame, final EVM evm) { public OperationResult execute(final MessageFrame frame, final EVM evm) {
final Address address; final Address address = Words.toAddress(frame.popStackItem());
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 = final boolean accountIsWarm =
frame.warmUpAddress(address) || gasCalculator().isPrecompile(address); frame.warmUpAddress(address) || gasCalculator().isPrecompile(address);
final long cost = cost(accountIsWarm, frame, Optional.of(address)); final long cost = cost(accountIsWarm, frame, address);
if (frame.getRemainingGas() < cost) { if (frame.getRemainingGas() < cost) {
return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS); return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS);
} else { } else {
@ -114,9 +103,5 @@ public class ExtCodeSizeOperation extends AbstractOperation {
frame.pushStackItem(codeSize); frame.pushStackItem(codeSize);
return new OperationResult(cost, null); return new OperationResult(cost, null);
} }
} catch (final OverflowException ofe) {
return new OperationResult(
cost(true, frame, Optional.of(address)), ExceptionalHaltReason.TOO_MANY_STACK_ITEMS);
}
} }
} }

@ -54,7 +54,7 @@ public class PushOperation extends AbstractOperation {
if (code.length <= copyStart) { if (code.length <= copyStart) {
push = Bytes.EMPTY; push = Bytes.EMPTY;
} else { } 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); push = Bytes.wrap(code, copyStart, copyLength);
} }
frame.pushStackItem(push); frame.pushStackItem(push);

@ -39,7 +39,7 @@ public class SLoadOperation extends AbstractOperation {
} }
protected long cost(final MessageFrame frame, final Bytes32 key, final boolean slotIsWarm) { 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 @Override

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

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

Loading…
Cancel
Save