From d000c7a8800152c32e17c53b467c8c568d97ecbb Mon Sep 17 00:00:00 2001 From: Luis Pinto Date: Thu, 24 Oct 2024 08:15:13 +0100 Subject: [PATCH] Eip4762 fix gas costs (#7802) Signed-off-by: Luis Pinto --- .../besu/datatypes/AccessWitness.java | 134 ++++- .../mainnet/MainnetTransactionProcessor.java | 15 +- .../verkle/worldview/VerkleWorldState.java | 3 +- ethereum/referencetests/build.gradle | 2 +- .../java/org/hyperledger/besu/evm/EVM.java | 20 +- .../org/hyperledger/besu/evm/MainnetEVMs.java | 2 + .../besu/evm/fluent/EVMExecutor.java | 2 +- .../besu/evm/frame/MessageFrame.java | 3 +- .../gascalculator/BerlinGasCalculator.java | 15 +- .../ConstantinopleGasCalculator.java | 3 +- .../gascalculator/Eip4762GasCalculator.java | 421 +++++++-------- .../gascalculator/FrontierGasCalculator.java | 13 +- .../besu/evm/gascalculator/GasCalculator.java | 39 +- .../gascalculator/IstanbulGasCalculator.java | 9 +- .../TangerineWhistleGasCalculator.java | 8 +- .../gascalculator/stateless/AccessEvents.java | 69 +-- .../gascalculator/stateless/AccessMode.java | 49 ++ .../stateless/Eip4762AccessWitness.java | 489 ++++++++++-------- .../stateless/NoopAccessWitness.java | 58 ++- .../operation/AbstractCreateOperation.java | 3 - .../besu/evm/operation/BalanceOperation.java | 41 +- .../besu/evm/operation/CallCodeOperation.java | 26 + .../besu/evm/operation/Create2Operation.java | 7 - .../besu/evm/operation/CreateOperation.java | 7 - .../evm/operation/EOFCreateOperation.java | 7 - .../evm/operation/ExtCodeCopyOperation.java | 1 - .../evm/operation/ExtCodeHashOperation.java | 75 +-- .../evm/operation/ExtCodeSizeOperation.java | 75 ++- .../besu/evm/operation/PushOperation.java | 2 +- .../besu/evm/operation/SLoadOperation.java | 2 +- .../processor/ContractCreationProcessor.java | 3 + .../AbstractCreateOperationTest.java | 6 - 32 files changed, 823 insertions(+), 786 deletions(-) create mode 100644 evm/src/main/java/org/hyperledger/besu/evm/gascalculator/stateless/AccessMode.java diff --git a/datatypes/src/main/java/org/hyperledger/besu/datatypes/AccessWitness.java b/datatypes/src/main/java/org/hyperledger/besu/datatypes/AccessWitness.java index 90fdce8291..c8f8fca491 100644 --- a/datatypes/src/main/java/org/hyperledger/besu/datatypes/AccessWitness.java +++ b/datatypes/src/main/java/org/hyperledger/besu/datatypes/AccessWitness.java @@ -14,40 +14,122 @@ */ package org.hyperledger.besu.datatypes; -import java.util.List; +import java.util.Optional; import org.apache.tuweni.units.bigints.UInt256; +/** + * Manages and records all accesses to the witness for stateless access to the storage and it might + * return back the corresponding gas charges for such access. + */ public interface AccessWitness { - List
keys(); - + /** + * Touch witness for all required accesses during a CALL when value is transferred. + * + * @param caller address of whom is doing the CALL operation + * @param target address of the account being called + * @param isAccountCreation will there be any account creation as a result of the operation + * @param warmReadCost cost to deduct in case the witness was touched before with {@code target} + * address + * @return stateless costs associated with accesses + */ + long touchAndChargeValueTransfer( + Address caller, Address target, boolean isAccountCreation, long warmReadCost); + + /** + * Touch witness for all required accesses during a SELFDESTRUCT when value is transferred. + * + * @param caller address of whom is doing the SELFDESTRUCT operation + * @param target address of the account beneficiary + * @param isAccountCreation will there be any account creation as a result of the operation + * @param warmReadCost cost to deduct in case the witness was touched before with {@code target} + * address + * @return stateless costs associated with accesses + */ + long touchAndChargeValueTransferSelfDestruct( + Address caller, Address target, boolean isAccountCreation, long warmReadCost); + + /** + * Touch witness as if accessing the {@code leafKey} index on the 0x0 branch index in the trie of + * an account. + * + * @param address address of the account to record the read access + * @param leafKey leaf index of the tree to record the read access + * @return stateless costs associated with accesses + */ + long touchAddressAndChargeRead(Address address, UInt256 leafKey); + + /** + * Touch witness for all required accesses during a base transaction from an EOA. This does not + * return any cost because there are no charges. + * + * @param origin EOA from which transaction originates + * @param target address of the account being called + * @param value value being transferred as part of the transaction + */ + void touchBaseTx(Address origin, Optional
target, Wei value); + + /** + * Touch witness for checking if a contract address already exists upon contract creation. + * + * @param address contract address being created + * @return stateless costs associated with access + */ long touchAndChargeProofOfAbsence(Address address); - long touchAndChargeValueTransfer(Address caller, Address target); - - long touchAndChargeMessageCall(Address address); - - long touchTxOriginAndComputeGas(Address origin); - - long touchTxExistingAndComputeGas(Address target, boolean sendsValue); - - long touchAndChargeContractCreateInit(Address address, boolean createSendsValue); - - long touchAndChargeContractCreateCompleted(final Address address); - - long touchAddressOnWriteAndComputeGas(Address address, UInt256 treeIndex, UInt256 subIndex); - - long touchAddressOnReadAndComputeGas(Address address, UInt256 treeIndex, UInt256 subIndex); - - List getStorageSlotTreeIndexes(UInt256 storageKey); - + /** + * Touch witness for all required accesses after a contract has been deposited in the account. + * This implies all accesses required to finish contract creation after code has been added stored + * in the account. + * + * @param address where the contract is being deployed + * @return stateless costs associated with accesses + */ long touchCodeChunksUponContractCreation(Address address, long codeLength); - long touchCodeChunks(Address address, long offset, long readSize, long codeLength); + /** + * Touch witness for all required accesses for completing contract creation. This does not include + * accesses related to the contract's code being deposited in the account. + * + * @param address where the contract is deployed + * @return stateless costs associated with accesses + */ + long touchAndChargeContractCreateCompleted(final Address address); - default long touchCodeChunksWithoutAccessCost( - final Address address, final long offset, final long readSize, final long codeLength) { - return touchCodeChunks(address, offset, readSize, codeLength); - } + /** + * Touch witness for read access to a given account's storage location during an SLOAD operation. + * + * @param address account address to read the storage for + * @param storageKey key to the account storage location in the trie + * @return stateless costs associated with storage access for the given key + */ + long touchAndChargeStorageLoad(Address address, UInt256 storageKey); + + /** + * Touch witness for write access to a given account's storage location during an SSTORE + * operation. Depending on whether a storage location already holds a value, write access will be + * different for either setting or resetting the trie leaf holding the value. + * + * @param address account address to write the storage for + * @param storageKey key to the account storage location in the trie + * @param hasPreviousValue true if account previously stored a value at the given location, false + * otherwise + * @return stateless costs associated with storage access for the given key + */ + long touchAndChargeStorageStore(Address address, UInt256 storageKey, boolean hasPreviousValue); + + /** + * Touch witness for reading code chunks of an account. + * + * @param address contract address to read the code for + * @param isContractInDeployment whether contract, which code is being read, is being deployed in + * the current transaction + * @param offset index of the code that the read event starting from + * @param readSize how many bytes are being read from the contract's code + * @param codeLength total size of the contract's code + * @return stateless costs associated with accesses + */ + long touchCodeChunks( + Address address, boolean isContractInDeployment, long offset, long readSize, long codeLength); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java index ae9ba692c1..2960ec7d3f 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java @@ -379,22 +379,15 @@ public class MainnetTransactionProcessor { if (warmCoinbase) { warmAddressList.add(miningBeneficiary); } - final AccessWitness accessWitness = gasCalculator.newAccessWitness(); final long intrinsicGas = gasCalculator.transactionIntrinsicGasCost( transaction.getPayload(), transaction.isContractCreation()); final long accessListGas = gasCalculator.accessListGasCost(accessListEntries.size(), accessListStorageCount); - final long accessEventCost = - gasCalculator.computeBaseAccessEventsCost(accessWitness, transaction, sender); final long codeDelegationGas = gasCalculator.delegateCodeGasCost(transaction.codeDelegationListSize()); final long gasAvailable = - transaction.getGasLimit() - - intrinsicGas - - accessListGas - - codeDelegationGas - - accessEventCost; + transaction.getGasLimit() - intrinsicGas - accessListGas - codeDelegationGas; LOG.trace( "Gas available for execution {} = {} - {} - {} - {} (limit - intrinsic - accessList - codeDelegation)", gasAvailable, @@ -403,6 +396,10 @@ public class MainnetTransactionProcessor { accessListGas, codeDelegationGas); + final AccessWitness accessWitness = gasCalculator.newAccessWitness(); + accessWitness.touchBaseTx( + transaction.getSender(), transaction.getTo(), transaction.getValue()); + final WorldUpdater worldUpdater = evmWorldUpdater.updater(); final ImmutableMap.Builder contextVariablesBuilder = ImmutableMap.builder() @@ -526,8 +523,6 @@ public class MainnetTransactionProcessor { .log(); final long gasUsedByTransaction = transaction.getGasLimit() - initialFrame.getRemainingGas(); - // checkTransactionGas(transaction.getHash(), gasUsedByTransaction); - // update the coinbase final long usedGas = transaction.getGasLimit() - refundedGas; final CoinbaseFeePriceCalculator coinbaseCalculator; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/verkle/worldview/VerkleWorldState.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/verkle/worldview/VerkleWorldState.java index 2ca0cae09c..92d325811b 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/verkle/worldview/VerkleWorldState.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/trie/diffbased/verkle/worldview/VerkleWorldState.java @@ -44,6 +44,7 @@ import org.hyperledger.besu.plugin.services.storage.SegmentIdentifier; import org.hyperledger.besu.plugin.services.storage.SegmentedKeyValueStorageTransaction; import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -435,7 +436,7 @@ public class VerkleWorldState extends DiffBasedWorldState { @Override public Map 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) { diff --git a/ethereum/referencetests/build.gradle b/ethereum/referencetests/build.gradle index d39c0a6a28..b941194c8c 100644 --- a/ethereum/referencetests/build.gradle +++ b/ethereum/referencetests/build.gradle @@ -255,7 +255,7 @@ dependencies { } verkleRefTestImplemention dependencies.create('ethereum:execution-spec-tests') { version { - strictly 'verkle@v0.0.4' + strictly 'verkle@v0.0.5' } artifact { name = 'fixtures' diff --git a/evm/src/main/java/org/hyperledger/besu/evm/EVM.java b/evm/src/main/java/org/hyperledger/besu/evm/EVM.java index 25a1a8d45b..9bf098fd8a 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/EVM.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/EVM.java @@ -17,6 +17,7 @@ package org.hyperledger.besu.evm; import static com.google.common.base.Preconditions.checkNotNull; import static org.hyperledger.besu.evm.operation.SwapOperation.SWAP_BASE; +import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.evm.code.CodeFactory; import org.hyperledger.besu.evm.code.EOFLayout; @@ -213,18 +214,21 @@ public class EVM { int opcode; int pc = frame.getPC(); - long statelessGas; try { opcode = code[pc] & 0xff; currentOperation = operationArray[opcode]; - if (!frame.wasCreatedInTransaction(frame.getContractAddress())) { - statelessGas = - frame - .getAccessWitness() - .touchCodeChunks(frame.getContractAddress(), pc, 1, code.length); - frame.decrementRemainingGas(statelessGas); - } + final Address contractAddress = frame.getContractAddress(); + long statelessGas = + frame + .getAccessWitness() + .touchCodeChunks( + contractAddress, + frame.wasCreatedInTransaction(contractAddress), + frame.getPC(), + 1, + code.length); + frame.decrementRemainingGas(statelessGas); } catch (ArrayIndexOutOfBoundsException aiiobe) { opcode = 0; diff --git a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java index 710f053f02..0bf21315db 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/MainnetEVMs.java @@ -1526,6 +1526,8 @@ public class MainnetEVMs { final BigInteger chainID) { // basing off of shanghai for devnet-6 registerShanghaiOperations(registry, gasCalculator, chainID); + // EIP-6780 nerf self destruct + registry.put(new SelfDestructOperation(gasCalculator, true)); // mimic a weird behavior by geth that ignores eip-1706 registry.put(new SStoreOperation(gasCalculator, 272L)); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/fluent/EVMExecutor.java b/evm/src/main/java/org/hyperledger/besu/evm/fluent/EVMExecutor.java index f1d8de8cae..5f3ca91fb4 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/fluent/EVMExecutor.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/fluent/EVMExecutor.java @@ -90,7 +90,7 @@ public class EVMExecutor { private ContractCreationProcessor contractCreationProcessor = null; private MessageFrame.Type messageFrameType = MessageFrame.Type.MESSAGE_CALL; - private final AccessWitness accessWitness = new NoopAccessWitness(); + private final AccessWitness accessWitness = NoopAccessWitness.get(); private EVMExecutor(final EVM evm) { checkNotNull(evm, "evm must not be null"); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java b/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java index 73a1593536..c16e9aa932 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/frame/MessageFrame.java @@ -26,6 +26,7 @@ import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.VersionedHash; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.evm.Code; +import org.hyperledger.besu.evm.gascalculator.stateless.NoopAccessWitness; import org.hyperledger.besu.evm.internal.MemoryEntry; import org.hyperledger.besu.evm.internal.OperandStack; import org.hyperledger.besu.evm.internal.ReturnStack; @@ -1364,7 +1365,7 @@ public class MessageFrame { private Optional> versionedHashes = Optional.empty(); - private AccessWitness accessWitness = null; + private AccessWitness accessWitness = NoopAccessWitness.get(); /** Instantiates a new Builder. */ public Builder() { diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/BerlinGasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/BerlinGasCalculator.java index 92f62a4604..daf82e356b 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/BerlinGasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/BerlinGasCalculator.java @@ -27,7 +27,6 @@ import org.hyperledger.besu.evm.internal.Words; import org.hyperledger.besu.evm.precompile.BigIntegerModularExponentiationPrecompiledContract; import java.math.BigInteger; -import java.util.Optional; import java.util.function.Supplier; import org.apache.tuweni.bytes.Bytes; @@ -49,7 +48,7 @@ public class BerlinGasCalculator extends IstanbulGasCalculator { protected static final long ACCESS_LIST_STORAGE_COST = 1900L; // redefinitions for EIP-2929 - private static final long SLOAD_GAS = WARM_STORAGE_READ_COST; + protected static final long SLOAD_GAS = WARM_STORAGE_READ_COST; /** The constant SSTORE_RESET_GAS. */ protected static final long SSTORE_RESET_GAS = 5000L - COLD_SLOAD_COST; @@ -120,8 +119,8 @@ public class BerlinGasCalculator extends IstanbulGasCalculator { // Zeroed out old costs @Override - public long getBalanceOperationGasCost( - final MessageFrame frame, final boolean accountIsWarm, final Optional
maybeAddress) { + public long balanceOperationGasCost( + final MessageFrame frame, final boolean accountIsWarm, final Address address) { return accountIsWarm ? getWarmStorageReadCost() : getColdAccountAccessCost(); } @@ -132,18 +131,18 @@ public class BerlinGasCalculator extends IstanbulGasCalculator { @Override public long extCodeHashOperationGasCost( - final MessageFrame frame, final boolean accountIsWarm, final Optional
address) { + final MessageFrame frame, final boolean accountIsWarm, final Address address) { return (accountIsWarm ? getWarmStorageReadCost() : getColdAccountAccessCost()); } @Override - public long getExtCodeSizeOperationGasCost( - final MessageFrame frame, final boolean accountIsWarm, final Optional
maybeAddress) { + public long extCodeSizeOperationGasCost( + final MessageFrame frame, final boolean accountIsWarm, final Address address) { return (accountIsWarm ? getWarmStorageReadCost() : getColdAccountAccessCost()); } @Override - public long getSloadOperationGasCost( + public long sloadOperationGasCost( final MessageFrame frame, final UInt256 key, final boolean slotIsWarm) { return (slotIsWarm ? getWarmStorageReadCost() : getColdSloadCost()); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/ConstantinopleGasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/ConstantinopleGasCalculator.java index 43358b19b6..cbdd4f370c 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/ConstantinopleGasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/ConstantinopleGasCalculator.java @@ -20,7 +20,6 @@ import static org.hyperledger.besu.evm.internal.Words.clampedToInt; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.evm.frame.MessageFrame; -import java.util.Optional; import java.util.function.Supplier; import org.apache.tuweni.units.bigints.UInt256; @@ -131,7 +130,7 @@ public class ConstantinopleGasCalculator extends ByzantiumGasCalculator { @Override public long extCodeHashOperationGasCost( - final MessageFrame frame, final boolean accountIsWarm, final Optional
address) { + final MessageFrame frame, final boolean accountIsWarm, final Address address) { return EXTCODE_HASH_COST; } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/Eip4762GasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/Eip4762GasCalculator.java index 5378f1623f..a7b919ef53 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/Eip4762GasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/Eip4762GasCalculator.java @@ -15,25 +15,24 @@ package org.hyperledger.besu.evm.gascalculator; import static org.hyperledger.besu.datatypes.Address.KZG_POINT_EVAL; +import static org.hyperledger.besu.ethereum.trie.verkle.util.Parameters.BASIC_DATA_LEAF_KEY; +import static org.hyperledger.besu.ethereum.trie.verkle.util.Parameters.CODE_HASH_LEAF_KEY; import static org.hyperledger.besu.evm.internal.Words.clampedAdd; import org.hyperledger.besu.datatypes.AccessWitness; import org.hyperledger.besu.datatypes.Address; -import org.hyperledger.besu.datatypes.Transaction; import org.hyperledger.besu.datatypes.Wei; -import org.hyperledger.besu.ethereum.trie.verkle.util.Parameters; import org.hyperledger.besu.evm.account.Account; -import org.hyperledger.besu.evm.account.MutableAccount; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.stateless.Eip4762AccessWitness; -import java.util.List; -import java.util.Optional; import java.util.function.Supplier; import org.apache.tuweni.units.bigints.UInt256; public class Eip4762GasCalculator extends PragueGasCalculator { + public static final Address HISTORY_STORAGE_ADDRESS = + Address.fromHexString("0xfffffffffffffffffffffffffffffffffffffffe"); private static final long CREATE_OPERATION_GAS_COST = 1_000L; /** Instantiates a new Prague Gas Calculator. */ @@ -52,14 +51,8 @@ public class Eip4762GasCalculator extends PragueGasCalculator { } @Override - public long initcodeCost(final int initCodeLength) { - return super.initcodeCost(initCodeLength); - } - - @Override - public long initcodeStatelessCost( - final MessageFrame frame, final Address address, final Wei value) { - return frame.getAccessWitness().touchAndChargeContractCreateInit(address, !value.isZero()); + public long proofOfAbsenceCost(final MessageFrame frame, final Address address) { + return frame.getAccessWitness().touchAndChargeProofOfAbsence(address); } @Override @@ -75,7 +68,7 @@ public class Eip4762GasCalculator extends PragueGasCalculator { final Address to, final boolean accountIsWarm) { - long gasCost = + long gas = super.callOperationGasCost( frame, stipend, @@ -86,27 +79,35 @@ public class Eip4762GasCalculator extends PragueGasCalculator { transferValue, recipient, to, - true); - if (super.isPrecompile(to)) { - return gasCost; - } else { - long statelessGas; - if (frame.getWorldUpdater().get(to) == null) { - statelessGas = frame.getAccessWitness().touchAndChargeProofOfAbsence(to); - } else { - statelessGas = frame.getAccessWitness().touchAndChargeMessageCall(to); - } - if (!transferValue.isZero()) { - statelessGas = - clampedAdd( - statelessGas, - frame.getAccessWitness().touchAndChargeValueTransfer(recipient.getAddress(), to)); - } - if (statelessGas == 0) { - return getWarmStorageReadCost(); - } - return clampedAdd(gasCost, statelessGas); + false); + + if (!transferValue.isZero()) { + gas = + clampedAdd( + gas, + frame + .getAccessWitness() + .touchAndChargeValueTransfer( + frame.getContractAddress(), to, recipient == null, getWarmStorageReadCost())); + return gas; } + + if (isPrecompile(to) || isSystemContract(to)) { + return clampedAdd(gas, getWarmStorageReadCost()); + } + + long messageCallGas = + frame.getAccessWitness().touchAddressAndChargeRead(to, BASIC_DATA_LEAF_KEY); + if (messageCallGas == 0) { + messageCallGas = getWarmStorageReadCost(); + } + + return clampedAdd(gas, messageCallGas); + } + + @Override + public long callValueTransferGasCost() { + return 0L; } @Override @@ -121,38 +122,6 @@ public class Eip4762GasCalculator extends PragueGasCalculator { .touchCodeChunksUponContractCreation(frame.getContractAddress(), codeSize); } - @Override - public long calculateStorageCost( - final MessageFrame frame, - final UInt256 key, - final UInt256 newValue, - final Supplier currentValue, - final Supplier 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 treeIndexes = accessWitness.getStorageSlotTreeIndexes(key); - gasCost += - frame - .getAccessWitness() - .touchAddressOnReadAndComputeGas( - frame.getRecipientAddress(), treeIndexes.get(0), treeIndexes.get(1)); - gasCost += - frame - .getAccessWitness() - .touchAddressOnWriteAndComputeGas( - frame.getRecipientAddress(), treeIndexes.get(0), treeIndexes.get(1)); - - if (gasCost == 0) { - return getWarmStorageReadCost(); - } - - return gasCost; - } - @Override public long calculateStorageRefundAmount( final UInt256 newValue, @@ -170,24 +139,26 @@ public class Eip4762GasCalculator extends PragueGasCalculator { final long codeOffset, final long readSize, final long codeSize) { - long gasCost = extCodeCopyOperationGasCost(frame, memOffset, readSize); + long gas = extCodeCopyOperationGasCost(frame, memOffset, readSize); - long statelessGas = - frame - .getAccessWitness() - .touchAddressOnReadAndComputeGas(address, UInt256.ZERO, Parameters.BASIC_DATA_LEAF_KEY); - if (statelessGas == 0) { - statelessGas = getWarmStorageReadCost(); + gas = + clampedAdd( + gas, + frame + .getAccessWitness() + .touchCodeChunks(address, false, codeOffset, readSize, codeSize)); + + if (isPrecompile(address) || isSystemContract(address)) { + return clampedAdd(gas, getWarmStorageReadCost()); } - if (!frame.wasCreatedInTransaction(frame.getContractAddress())) { - statelessGas = - clampedAdd( - statelessGas, - frame.getAccessWitness().touchCodeChunks(address, codeOffset, readSize, codeSize)); + long readTargetGas = + frame.getAccessWitness().touchAddressAndChargeRead(address, BASIC_DATA_LEAF_KEY); + if (readTargetGas == 0) { + readTargetGas = getWarmStorageReadCost(); } - return clampedAdd(gasCost, statelessGas); + return clampedAdd(gas, readTargetGas); } @Override @@ -197,109 +168,88 @@ public class Eip4762GasCalculator extends PragueGasCalculator { final long codeOffset, final long readSize, final long codeSize) { - long gasCost = super.dataCopyOperationGasCost(frame, memOffset, readSize); - if (!frame.wasCreatedInTransaction(frame.getContractAddress())) { - gasCost = - clampedAdd( - gasCost, - frame - .getAccessWitness() - .touchCodeChunks(frame.getContractAddress(), codeOffset, readSize, codeSize)); - } - return gasCost; + long gas = super.dataCopyOperationGasCost(frame, memOffset, readSize); + + final Address contractAddress = frame.getContractAddress(); + gas = + clampedAdd( + gas, + frame + .getAccessWitness() + .touchCodeChunks( + contractAddress, + frame.wasCreatedInTransaction(contractAddress), + codeOffset, + readSize, + codeSize)); + + return gas; } @Override public long pushOperationGasCost( final MessageFrame frame, final long codeOffset, final long readSize, final long codeSize) { - long gasCost = super.pushOperationGasCost(frame, codeOffset, readSize, codeSize); - if (!frame.wasCreatedInTransaction(frame.getContractAddress())) { - if (readSize == 1) { - if ((codeOffset % 31 == 0)) { - gasCost = - clampedAdd( - gasCost, - frame - .getAccessWitness() - .touchCodeChunks( - frame.getContractAddress(), codeOffset + 1, readSize, codeSize)); - } - } else { - gasCost = - clampedAdd( - gasCost, - frame - .getAccessWitness() - .touchCodeChunks(frame.getContractAddress(), codeOffset, readSize, codeSize)); - } + long gas = super.pushOperationGasCost(frame, codeOffset, readSize, codeSize); + + if (frame.wasCreatedInTransaction(frame.getContractAddress()) + // PUSH1 only touches code chunks if borderline + || (readSize == 1 && codeOffset % 31 != 0)) { + return gas; } - return gasCost; + + final Address contractAddress = frame.getContractAddress(); + gas = + clampedAdd( + gas, + frame + .getAccessWitness() + .touchCodeChunks( + contractAddress, + frame.wasCreatedInTransaction(contractAddress), + codeOffset, + readSize, + codeSize)); + + return gas; } @Override - public long getBalanceOperationGasCost( - final MessageFrame frame, final boolean accountIsWarm, final Optional
maybeAddress) { - if (maybeAddress.isPresent()) { - final Address address = maybeAddress.get(); - final long statelessGas = - frame - .getAccessWitness() - .touchAddressOnReadAndComputeGas( - address, UInt256.ZERO, Parameters.BASIC_DATA_LEAF_KEY); - if (statelessGas == 0) { - return getWarmStorageReadCost(); - } else { - return statelessGas; - } + public long balanceOperationGasCost( + final MessageFrame frame, final boolean accountIsWarm, final Address address) { + final long gas = + frame.getAccessWitness().touchAddressAndChargeRead(address, BASIC_DATA_LEAF_KEY); + if (gas == 0) { + return getWarmStorageReadCost(); } - return 0L; + return gas; } @Override public long extCodeHashOperationGasCost( - final MessageFrame frame, final boolean accountIsWarm, final Optional
maybeAddress) { - if (maybeAddress.isPresent()) { - final Address address = maybeAddress.get(); - if (isPrecompile(address)) { - return 0L; - } else { - final long statelessGas = - frame - .getAccessWitness() - .touchAddressOnReadAndComputeGas( - address, UInt256.ZERO, Parameters.CODE_HASH_LEAF_KEY); - if (statelessGas == 0) { - return getWarmStorageReadCost(); - } else { - return statelessGas; - } - } + final MessageFrame frame, final boolean accountIsWarm, final Address address) { + if (isPrecompile(address) || isSystemContract(address)) { + return getWarmStorageReadCost(); } - return 0L; + + long gas = frame.getAccessWitness().touchAddressAndChargeRead(address, CODE_HASH_LEAF_KEY); + if (gas == 0) { + return getWarmStorageReadCost(); + } + return gas; } @Override - public long getExtCodeSizeOperationGasCost( - final MessageFrame frame, final boolean accountIsWarm, final Optional
maybeAddress) { - - if (maybeAddress.isPresent()) { - final Address address = maybeAddress.get(); - if (isPrecompile(address)) { - return 0L; - } else { - long statelessGas = - frame - .getAccessWitness() - .touchAddressOnReadAndComputeGas( - address, UInt256.ZERO, Parameters.BASIC_DATA_LEAF_KEY); - if (statelessGas == 0) { - return getWarmStorageReadCost(); - } else { - return statelessGas; - } - } + public long extCodeSizeOperationGasCost( + final MessageFrame frame, final boolean accountIsWarm, final Address address) { + if (isPrecompile(address) || isSystemContract(address)) { + return getWarmStorageReadCost(); } - return 0L; + + long gas = frame.getAccessWitness().touchAddressAndChargeRead(address, BASIC_DATA_LEAF_KEY); + if (gas == 0) { + return getWarmStorageReadCost(); + } + return gas; } @Override @@ -307,94 +257,85 @@ public class Eip4762GasCalculator extends PragueGasCalculator { final MessageFrame frame, final Account recipient, final Address recipientAddress, - final Wei inheritance, + final Wei value, final Address originatorAddress) { - final long gasCost = - super.selfDestructOperationGasCost( - frame, recipient, recipientAddress, inheritance, originatorAddress); - if (isPrecompile(recipientAddress)) { - return gasCost; - } else { - long statelessGas = - frame - .getAccessWitness() - .touchAddressOnReadAndComputeGas( - originatorAddress, UInt256.ZERO, Parameters.BASIC_DATA_LEAF_KEY); - if (!originatorAddress.equals(recipientAddress)) { - statelessGas = - clampedAdd( - statelessGas, - frame - .getAccessWitness() - .touchAddressOnReadAndComputeGas( - recipientAddress, UInt256.ZERO, Parameters.BASIC_DATA_LEAF_KEY)); - } - if (!inheritance.isZero()) { - statelessGas = - clampedAdd( - statelessGas, - frame - .getAccessWitness() - .touchAddressOnWriteAndComputeGas( - originatorAddress, UInt256.ZERO, Parameters.BASIC_DATA_LEAF_KEY)); - if (!originatorAddress.equals(recipientAddress)) { - statelessGas = - clampedAdd( - statelessGas, - frame - .getAccessWitness() - .touchAddressOnWriteAndComputeGas( - recipientAddress, UInt256.ZERO, Parameters.BASIC_DATA_LEAF_KEY)); - } - if (recipient == null) { - statelessGas = - clampedAdd( - statelessGas, - frame - .getAccessWitness() - .touchAddressOnWriteAndComputeGas( - recipientAddress, UInt256.ZERO, Parameters.BASIC_DATA_LEAF_KEY)); - } - } - return clampedAdd(gasCost, statelessGas); + // TODO: only charge non-account creation for devnet-7. Geth has removed the 25k gas cost + // from EIP-150 for account + // creation by mistake and will change in the next devnet + // long gas = super.selfDestructOperationGasCost( + // frame, recipient, recipientAddress, value, originatorAddress); + long gas = 5000L; + + if (!value.isZero()) { + gas = + clampedAdd( + gas, + frame + .getAccessWitness() + .touchAndChargeValueTransferSelfDestruct( + originatorAddress, + recipientAddress, + recipient == null, + getWarmStorageReadCost())); } + + // check if there's any balance in the originating account in search of value + gas = + clampedAdd( + gas, + frame + .getAccessWitness() + .touchAddressAndChargeRead(originatorAddress, BASIC_DATA_LEAF_KEY)); + + // TODO: REMOVE - if code removed below there's no point to check for this + if (isPrecompile(recipientAddress) || isSystemContract(recipientAddress)) { + return gas; + } + + // TODO: Recheck only here to pass tests - after talking with Geth team this will be dropped in + // future devnets. If there's no value transferred, there's no point in touching the beneficiary + if (!recipientAddress.equals(originatorAddress)) { + gas = + clampedAdd( + gas, + frame + .getAccessWitness() + .touchAddressAndChargeRead(recipientAddress, BASIC_DATA_LEAF_KEY)); + } + + return gas; } @Override - public long getSloadOperationGasCost( + public long sloadOperationGasCost( final MessageFrame frame, final UInt256 key, final boolean slotIsWarm) { - AccessWitness accessWitness = frame.getAccessWitness(); - List treeIndexes = accessWitness.getStorageSlotTreeIndexes(key); - long gasCost = - frame - .getAccessWitness() - .touchAddressOnReadAndComputeGas( - frame.getContractAddress(), treeIndexes.get(0), treeIndexes.get(1)); - if (gasCost == 0) { + long gas = frame.getAccessWitness().touchAndChargeStorageLoad(frame.getContractAddress(), key); + + if (gas == 0) { return getWarmStorageReadCost(); } - return gasCost; + + return gas; } @Override - public long computeBaseAccessEventsCost( - final AccessWitness accessWitness, - final Transaction transaction, - final MutableAccount sender) { - final boolean sendsValue = !transaction.getValue().equals(Wei.ZERO); - long cost = 0; - cost += accessWitness.touchTxOriginAndComputeGas(transaction.getSender()); - - if (transaction.getTo().isPresent()) { - final Address to = transaction.getTo().get(); - cost += accessWitness.touchTxExistingAndComputeGas(to, sendsValue); - } else { - cost += - accessWitness.touchAndChargeContractCreateInit( - Address.contractAddress(transaction.getSender(), sender.getNonce() - 1L), sendsValue); + public long calculateStorageCost( + final MessageFrame frame, + final UInt256 key, + final UInt256 newValue, + final Supplier currentValue, + final Supplier originalValue) { + + long gas = + frame + .getAccessWitness() + .touchAndChargeStorageStore(frame.getRecipientAddress(), key, originalValue != null); + + if (gas == 0) { + return SLOAD_GAS; } - return cost; + return gas; } @Override @@ -404,6 +345,10 @@ public class Eip4762GasCalculator extends PragueGasCalculator { .touchAndChargeContractCreateCompleted(frame.getContractAddress()); } + private static boolean isSystemContract(final Address address) { + return HISTORY_STORAGE_ADDRESS.equals(address); + } + @Override public AccessWitness newAccessWitness() { return new Eip4762AccessWitness(); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/FrontierGasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/FrontierGasCalculator.java index d2c597b1c4..0047b924a5 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/FrontierGasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/FrontierGasCalculator.java @@ -26,7 +26,6 @@ import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.operation.ExpOperation; -import java.util.Optional; import java.util.function.Supplier; import org.apache.tuweni.bytes.Bytes; @@ -414,8 +413,8 @@ public class FrontierGasCalculator implements GasCalculator { } @Override - public long getBalanceOperationGasCost( - final MessageFrame frame, final boolean accountIsWarm, final Optional
maybeAddress) { + public long balanceOperationGasCost( + final MessageFrame frame, final boolean accountIsWarm, final Address address) { return BALANCE_OPERATION_GAS_COST; } @@ -468,14 +467,14 @@ public class FrontierGasCalculator implements GasCalculator { @Override public long extCodeHashOperationGasCost( - final MessageFrame frame, final boolean accountIsWarm, final Optional
address) { + final MessageFrame frame, final boolean accountIsWarm, final Address address) { throw new UnsupportedOperationException( "EXTCODEHASH not supported by " + getClass().getSimpleName()); } @Override - public long getExtCodeSizeOperationGasCost( - final MessageFrame frame, final boolean accountIsWarm, final Optional
maybeAddress) { + public long extCodeSizeOperationGasCost( + final MessageFrame frame, final boolean accountIsWarm, final Address address) { return extCodeBaseGasCost(); } @@ -533,7 +532,7 @@ public class FrontierGasCalculator implements GasCalculator { } @Override - public long getSloadOperationGasCost( + public long sloadOperationGasCost( final MessageFrame frame, final UInt256 key, final boolean slotIsWarm) { return SLOAD_OPERATION_GAS_COST; } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java index 172204a515..93c527a432 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java @@ -17,10 +17,8 @@ package org.hyperledger.besu.evm.gascalculator; import org.hyperledger.besu.datatypes.AccessListEntry; import org.hyperledger.besu.datatypes.AccessWitness; import org.hyperledger.besu.datatypes.Address; -import org.hyperledger.besu.datatypes.Transaction; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.evm.account.Account; -import org.hyperledger.besu.evm.account.MutableAccount; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.stateless.NoopAccessWitness; import org.hyperledger.besu.evm.operation.BalanceOperation; @@ -44,7 +42,6 @@ import org.hyperledger.besu.evm.precompile.SHA256PrecompiledContract; import org.hyperledger.besu.evm.processor.AbstractMessageProcessor; import java.util.List; -import java.util.Optional; import java.util.function.Supplier; import org.apache.tuweni.bytes.Bytes; @@ -309,8 +306,7 @@ public interface GasCalculator { */ long initcodeCost(final int initCodeLength); - default long initcodeStatelessCost( - final MessageFrame frame, final Address address, final Wei value) { + default long proofOfAbsenceCost(final MessageFrame frame, final Address address) { return 0; } @@ -366,11 +362,11 @@ public interface GasCalculator { * * @param frame The current frame * @param accountIsWarm true to add warm storage read cost, false to add cold account access cost - * @param maybeAddress targeted address + * @param address targeted address * @return the cost for executing the balance operation */ - long getBalanceOperationGasCost( - MessageFrame frame, final boolean accountIsWarm, final Optional
maybeAddress); + long balanceOperationGasCost( + MessageFrame frame, final boolean accountIsWarm, final Address address); /** * Returns the cost for executing a {@link BlockHashOperation}. @@ -423,22 +419,22 @@ public interface GasCalculator { * * @param frame The current frame * @param accountIsWarm true to add warm storage read cost, false to add cold account access cost - * @param maybeAddress targeted address + * @param address targeted address * @return the cost for executing the external code hash operation */ long extCodeHashOperationGasCost( - final MessageFrame frame, final boolean accountIsWarm, Optional
maybeAddress); + final MessageFrame frame, final boolean accountIsWarm, Address address); /** * Returns the cost for executing a {@link ExtCodeSizeOperation}. * * @param frame The current frame * @param accountIsWarm true to add warm storage read cost, false to add cold account access cost - * @param maybeAddress targeted address + * @param address targeted address * @return the cost for executing the external code size operation */ - long getExtCodeSizeOperationGasCost( - MessageFrame frame, final boolean accountIsWarm, Optional
maybeAddress); + long extCodeSizeOperationGasCost( + MessageFrame frame, final boolean accountIsWarm, Address address); /** * Returns the cost for executing a {@link JumpDestOperation}. @@ -517,7 +513,7 @@ public interface GasCalculator { * * @return the cost for executing the storage load operation */ - long getSloadOperationGasCost(MessageFrame frame, UInt256 key, final boolean slotIsWarm); + long sloadOperationGasCost(MessageFrame frame, UInt256 key, final boolean slotIsWarm); /** * Returns the cost for an SSTORE operation. @@ -752,20 +748,7 @@ public interface GasCalculator { return 0L; } - /** - * Compute access events cost of a transaction - * - * @param transaction transaction - * @return gas cost after computing all access events - */ - default long computeBaseAccessEventsCost( - final AccessWitness accessWitness, - final Transaction transaction, - final MutableAccount sender) { - return 0L; - } - default AccessWitness newAccessWitness() { - return new NoopAccessWitness(); + return NoopAccessWitness.get(); } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/IstanbulGasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/IstanbulGasCalculator.java index 710f89ed24..a13fa99a20 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/IstanbulGasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/IstanbulGasCalculator.java @@ -17,7 +17,6 @@ package org.hyperledger.besu.evm.gascalculator; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.evm.frame.MessageFrame; -import java.util.Optional; import java.util.function.Supplier; import org.apache.tuweni.bytes.Bytes; @@ -127,22 +126,22 @@ public class IstanbulGasCalculator extends PetersburgGasCalculator { @Override // As per https://eips.ethereum.org/EIPS/eip-1884 - public long getSloadOperationGasCost( + public long sloadOperationGasCost( final MessageFrame frame, final UInt256 key, final boolean slotIsWarm) { return SLOAD_GAS; } @Override // As per https://eips.ethereum.org/EIPS/eip-1884 - public long getBalanceOperationGasCost( - final MessageFrame frame, final boolean accountIsWarm, final Optional
maybeAddress) { + public long balanceOperationGasCost( + final MessageFrame frame, final boolean accountIsWarm, final Address address) { return BALANCE_OPERATION_GAS_COST; } @Override // As per https://eips.ethereum.org/EIPS/eip-1884 public long extCodeHashOperationGasCost( - final MessageFrame frame, final boolean accountIsWarm, final Optional
address) { + final MessageFrame frame, final boolean accountIsWarm, final Address address) { return EXTCODE_HASH_COST; } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/TangerineWhistleGasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/TangerineWhistleGasCalculator.java index 8ff65163dd..24856aaa92 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/TangerineWhistleGasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/TangerineWhistleGasCalculator.java @@ -22,8 +22,6 @@ import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.internal.Words; -import java.util.Optional; - import org.apache.tuweni.units.bigints.UInt256; /** The Tangerine whistle gas calculator. */ @@ -45,8 +43,8 @@ public class TangerineWhistleGasCalculator extends HomesteadGasCalculator { public TangerineWhistleGasCalculator() {} @Override - public long getBalanceOperationGasCost( - final MessageFrame frame, final boolean accountIsWarm, final Optional
maybeAddress) { + public long balanceOperationGasCost( + final MessageFrame frame, final boolean accountIsWarm, final Address address) { return BALANCE_OPERATION_GAS_COST; } @@ -137,7 +135,7 @@ public class TangerineWhistleGasCalculator extends HomesteadGasCalculator { } @Override - public long getSloadOperationGasCost( + public long sloadOperationGasCost( final MessageFrame frame, final UInt256 key, final boolean slotIsWarm) { return SLOAD_OPERATION_GAS_COST; } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/stateless/AccessEvents.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/stateless/AccessEvents.java index 532edb2b93..acfe2a557b 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/stateless/AccessEvents.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/stateless/AccessEvents.java @@ -14,67 +14,34 @@ */ package org.hyperledger.besu.evm.gascalculator.stateless; -public class AccessEvents { - private boolean branchRead; - private boolean chunkRead; - private boolean branchWrite; - private boolean chunkWrite; - private boolean chunkFill; +public final class AccessEvents { - public AccessEvents() { - this(false, false, false, false, false); - } - - public AccessEvents( - final boolean branchRead, - final boolean chunkRead, - final boolean branchWrite, - final boolean chunkWrite, - final boolean chunkFill) { - this.branchRead = branchRead; - this.chunkRead = chunkRead; - this.branchWrite = branchWrite; - this.chunkWrite = chunkWrite; - this.chunkFill = chunkFill; - } - - public boolean isBranchRead() { - return branchRead; - } - - public boolean isChunkRead() { - return chunkRead; - } - - public boolean isBranchWrite() { - return branchWrite; - } + public static final short NONE = 0; + public static final short BRANCH_READ = 1; + public static final short BRANCH_WRITE = 2; + public static final short LEAF_READ = 4; + public static final short LEAF_RESET = 8; + public static final short LEAF_SET = 16; - public boolean isChunkWrite() { - return chunkWrite; - } - - public boolean isChunkFill() { - return chunkFill; - } + private AccessEvents() {} - public void setBranchRead(final boolean branchRead) { - this.branchRead = branchRead; + public static boolean isBranchRead(final short accessEvents) { + return (accessEvents & BRANCH_READ) != 0; } - public void setChunkRead(final boolean chunkRead) { - this.chunkRead = chunkRead; + public static boolean isBranchWrite(final short accessEvents) { + return (accessEvents & BRANCH_WRITE) != 0; } - public void setBranchWrite(final boolean branchWrite) { - this.branchWrite = branchWrite; + public static boolean isLeafRead(final short accessEvents) { + return (accessEvents & LEAF_READ) != 0; } - public void setChunkWrite(final boolean chunkWrite) { - this.chunkWrite = chunkWrite; + public static boolean isLeafReset(final short accessEvents) { + return (accessEvents & LEAF_RESET) != 0; } - public void setChunkFill(final boolean chunkFill) { - this.chunkFill = chunkFill; + public static boolean isLeafSet(final short accessEvents) { + return (accessEvents & LEAF_SET) != 0; } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/stateless/AccessMode.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/stateless/AccessMode.java new file mode 100644 index 0000000000..91477ba3ce --- /dev/null +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/stateless/AccessMode.java @@ -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"; + } +} diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/stateless/Eip4762AccessWitness.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/stateless/Eip4762AccessWitness.java index 390c429b67..f72b3d4609 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/stateless/Eip4762AccessWitness.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/stateless/Eip4762AccessWitness.java @@ -20,7 +20,9 @@ import static org.hyperledger.besu.ethereum.trie.verkle.util.Parameters.CODE_OFF import static org.hyperledger.besu.ethereum.trie.verkle.util.Parameters.VERKLE_NODE_WIDTH; import static org.hyperledger.besu.evm.internal.Words.clampedAdd; +import org.hyperledger.besu.datatypes.AccessWitness; import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.trie.verkle.adapter.TrieKeyAdapter; import org.hyperledger.besu.ethereum.trie.verkle.hasher.PedersenHasher; @@ -28,146 +30,174 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import org.apache.tuweni.units.bigints.UInt256; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -public class Eip4762AccessWitness implements org.hyperledger.besu.datatypes.AccessWitness { +public class Eip4762AccessWitness implements AccessWitness { + private static final Logger LOG = LoggerFactory.getLogger(Eip4762AccessWitness.class); private static final TrieKeyAdapter TRIE_KEY_ADAPTER = new TrieKeyAdapter(new PedersenHasher()); - private static final long WITNESS_BRANCH_READ_COST = 1900; - private static final long WITNESS_CHUNK_READ_COST = 200; - private static final long WITNESS_BRANCH_WRITE_COST = 3000; - private static final long WITNESS_CHUNK_WRITE_COST = 500; - private static final long WITNESS_CHUNK_FILL_COST = 6200; + private static final long WITNESS_BRANCH_COST = 1900; + private static final long WITNESS_CHUNK_COST = 200; + private static final long SUBTREE_EDIT_COST = 3000; + private static final long CHUNK_EDIT_COST = 500; + private static final long CHUNK_FILL_COST = 6200; private static final UInt256 zeroTreeIndex = UInt256.ZERO; - private static final byte AccessWitnessReadFlag = 1; - private static final byte AccessWitnessWriteFlag = 2; - private final Map branches; - private final Map chunks; + private final Map leaves; + private final Map branches; public Eip4762AccessWitness() { this(new HashMap<>(), new HashMap<>()); } public Eip4762AccessWitness( - final Map branches, final Map chunks) { + final Map leaves, final Map branches) { this.branches = branches; - this.chunks = chunks; + this.leaves = leaves; } @Override - public List
keys() { - return this.chunks.keySet().stream() - .map(chunkAccessKey -> chunkAccessKey.branchAccessKey().address()) - .toList(); + public long touchAddressAndChargeRead(final Address address, final UInt256 leafKey) { + return touchAddressOnReadAndComputeGas(address, zeroTreeIndex, leafKey); } @Override - public long touchAndChargeProofOfAbsence(final Address address) { + public long touchAndChargeValueTransfer( + final Address caller, + final Address target, + final boolean isAccountCreation, + final long warmReadCost) { + long gas = 0; + gas = clampedAdd( - gas, touchAddressOnReadAndComputeGas(address, zeroTreeIndex, BASIC_DATA_LEAF_KEY)); - gas = - clampedAdd( - gas, touchAddressOnReadAndComputeGas(address, zeroTreeIndex, CODE_HASH_LEAF_KEY)); - return gas; - } + gas, touchAddressOnWriteResetAndComputeGas(caller, zeroTreeIndex, BASIC_DATA_LEAF_KEY)); - @Override - public long touchAndChargeMessageCall(final Address address) { - return touchAddressOnReadAndComputeGas(address, zeroTreeIndex, BASIC_DATA_LEAF_KEY); - } - - @Override - public long touchAndChargeValueTransfer(final Address caller, final Address target) { + if (isAccountCreation) { + gas = + clampedAdd( + gas, touchAddressOnWriteSetAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY)); + gas = + clampedAdd( + gas, touchAddressOnWriteSetAndComputeGas(target, zeroTreeIndex, CODE_HASH_LEAF_KEY)); + return gas; + } - long gas = 0; + long readTargetStatelessGas = + touchAddressOnReadAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY); + if (readTargetStatelessGas == 0) { + readTargetStatelessGas = clampedAdd(readTargetStatelessGas, warmReadCost); + } gas = clampedAdd( - gas, touchAddressOnWriteAndComputeGas(caller, zeroTreeIndex, BASIC_DATA_LEAF_KEY)); - gas = - clampedAdd( - gas, touchAddressOnWriteAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY)); + gas, touchAddressOnWriteResetAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY)); - return gas; + return clampedAdd(gas, readTargetStatelessGas); } @Override - public long touchAndChargeContractCreateInit( - final Address address, final boolean createSendsValue) { + public long touchAndChargeValueTransferSelfDestruct( + final Address caller, + final Address target, + final boolean isAccountCreation, + final long warmReadCost) { long gas = 0; gas = clampedAdd( - gas, touchAddressOnWriteAndComputeGas(address, zeroTreeIndex, BASIC_DATA_LEAF_KEY)); + gas, touchAddressOnWriteResetAndComputeGas(caller, zeroTreeIndex, BASIC_DATA_LEAF_KEY)); + + if (caller.equals(target)) { + return gas; + } - if (createSendsValue) { + if (isAccountCreation) { + gas = + clampedAdd( + gas, touchAddressOnWriteSetAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY)); gas = clampedAdd( - gas, touchAddressOnWriteAndComputeGas(address, zeroTreeIndex, BASIC_DATA_LEAF_KEY)); + gas, touchAddressOnWriteSetAndComputeGas(target, zeroTreeIndex, CODE_HASH_LEAF_KEY)); + return gas; } - return gas; + + long readTargetStatelessGas = + touchAddressOnReadAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY); + if (readTargetStatelessGas == 0) { + readTargetStatelessGas = clampedAdd(readTargetStatelessGas, warmReadCost); + } + + gas = + clampedAdd( + gas, touchAddressOnWriteResetAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY)); + + return clampedAdd(gas, readTargetStatelessGas); } @Override - public long touchAndChargeContractCreateCompleted(final Address address) { - + public long touchAndChargeProofOfAbsence(final Address address) { long gas = 0; gas = clampedAdd( - gas, touchAddressOnWriteAndComputeGas(address, zeroTreeIndex, BASIC_DATA_LEAF_KEY)); + gas, touchAddressOnReadAndComputeGas(address, zeroTreeIndex, BASIC_DATA_LEAF_KEY)); + gas = clampedAdd( - gas, touchAddressOnWriteAndComputeGas(address, zeroTreeIndex, CODE_HASH_LEAF_KEY)); + gas, touchAddressOnReadAndComputeGas(address, zeroTreeIndex, CODE_HASH_LEAF_KEY)); return gas; } - @SuppressWarnings("unused") @Override - public long touchTxOriginAndComputeGas(final Address origin) { + public long touchAndChargeContractCreateCompleted(final Address address) { long gas = 0; gas = clampedAdd( - gas, touchAddressOnReadAndComputeGas(origin, zeroTreeIndex, BASIC_DATA_LEAF_KEY)); + gas, + touchAddressOnWriteResetAndComputeGas(address, zeroTreeIndex, BASIC_DATA_LEAF_KEY)); gas = clampedAdd( - gas, touchAddressOnWriteAndComputeGas(origin, zeroTreeIndex, BASIC_DATA_LEAF_KEY)); - gas = - clampedAdd(gas, touchAddressOnReadAndComputeGas(origin, zeroTreeIndex, CODE_HASH_LEAF_KEY)); - - // modifying this after update on EIP-4762 to not charge simple transfers + gas, touchAddressOnWriteResetAndComputeGas(address, zeroTreeIndex, CODE_HASH_LEAF_KEY)); - return 0; + return gas; } - @SuppressWarnings("unused") @Override - public long touchTxExistingAndComputeGas(final Address target, final boolean sendsValue) { - - long gas = 0; + public void touchBaseTx(final Address origin, final Optional
target, final Wei value) { + LOG.atDebug().log("START OF UNCHARGED COSTS"); + touchTxOrigin(origin); + if (target.isPresent()) { // is not contract creation + final Address to = target.get(); + final boolean sendsValue = !Wei.ZERO.equals(value); + touchTxExisting(to, sendsValue); + } + LOG.atDebug().log("END OF UNCHARGED COSTS"); + } - gas = - clampedAdd( - gas, touchAddressOnReadAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY)); - gas = - clampedAdd(gas, touchAddressOnReadAndComputeGas(target, zeroTreeIndex, CODE_HASH_LEAF_KEY)); + private void touchTxOrigin(final Address origin) { + touchAddressOnWriteResetAndComputeGas(origin, zeroTreeIndex, BASIC_DATA_LEAF_KEY); + touchAddressOnReadAndComputeGas(origin, zeroTreeIndex, CODE_HASH_LEAF_KEY); + } - if (sendsValue) { - gas = - clampedAdd( - gas, touchAddressOnWriteAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY)); + private void touchTxExisting(final Address target, final boolean sendsValue) { + touchAddressOnReadAndComputeGas(target, zeroTreeIndex, CODE_HASH_LEAF_KEY); + if (!sendsValue) { + touchAddressOnReadAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY); + return; } - // modifying this after update on EIP-4762 to not charge simple transfers - - return 0; + // TODO: not done right now on Geth either - implement case if target does not exist yet - a + // CODEHASH_LEAF will be touched too and WriteSet will be called + touchAddressOnWriteResetAndComputeGas(target, zeroTreeIndex, BASIC_DATA_LEAF_KEY); } @Override @@ -177,7 +207,7 @@ public class Eip4762AccessWitness implements org.hyperledger.besu.datatypes.Acce gas = clampedAdd( gas, - touchAddressOnWriteAndComputeGas( + touchAddressOnWriteSetAndComputeGas( address, CODE_OFFSET.add(i).divide(VERKLE_NODE_WIDTH), CODE_OFFSET.add(i).mod(VERKLE_NODE_WIDTH))); @@ -187,173 +217,217 @@ public class Eip4762AccessWitness implements org.hyperledger.besu.datatypes.Acce @Override public long touchCodeChunks( - final Address address, final long startPc, final long readSize, final long codeLength) { + final Address contractAddress, + final boolean isContractInDeployment, + final long startPc, + final long readSize, + final long codeLength) { long gas = 0; - if ((readSize == 0 && codeLength == 0) || startPc > codeLength) { + + if (isContractInDeployment || readSize == 0 || startPc >= codeLength) { return 0; } - long endPc = startPc + readSize; - if (endPc > codeLength) { - endPc = codeLength; - } - if (endPc > 0) { - endPc -= 1; - } - for (long i = startPc / 31; i <= endPc / 31; i++) { + + // last byte read is limited by code length, and it is an index, hence the decrement + long endPc = Math.min(startPc + readSize, codeLength) - 1L; + for (long i = startPc / 31L; i <= endPc / 31L; i++) { gas = clampedAdd( gas, touchAddressOnReadAndComputeGas( - address, + contractAddress, CODE_OFFSET.add(i).divide(VERKLE_NODE_WIDTH), CODE_OFFSET.add(i).mod(VERKLE_NODE_WIDTH))); } + return gas; } - @Override - public long touchAddressOnWriteAndComputeGas( + private long touchAddressOnWriteResetAndComputeGas( final Address address, final UInt256 treeIndex, final UInt256 subIndex) { + return touchAddressAndChargeGas(address, treeIndex, subIndex, AccessMode.WRITE_RESET); + } - return touchAddressAndChargeGas(address, treeIndex, subIndex, true); + private long touchAddressOnWriteSetAndComputeGas( + final Address address, final UInt256 treeIndex, final UInt256 subIndex) { + // TODO: change to WRITE_SET when CHUNK_FILL is implemented. Still not implemented in devnet-7 + return touchAddressAndChargeGas(address, treeIndex, subIndex, AccessMode.WRITE_RESET); } - @Override - public long touchAddressOnReadAndComputeGas( + private long touchAddressOnReadAndComputeGas( final Address address, final UInt256 treeIndex, final UInt256 subIndex) { - return touchAddressAndChargeGas(address, treeIndex, subIndex, false); + return touchAddressAndChargeGas(address, treeIndex, subIndex, AccessMode.READ); + } + + private List 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 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 treeIndexes = getStorageSlotTreeIndexes(storageKey); + if (!hasPreviousValue) { + return touchAddressOnWriteSetAndComputeGas(address, treeIndexes.get(0), treeIndexes.get(1)); + } + return touchAddressOnWriteResetAndComputeGas(address, treeIndexes.get(0), treeIndexes.get(1)); } public long touchAddressAndChargeGas( final Address address, final UInt256 treeIndex, final UInt256 subIndex, - final boolean isWrite) { - - AccessEvents accessEvent = touchAddress(address, treeIndex, subIndex, isWrite); - boolean logEnabled = false; + final int accessMode) { + final short accessEvents = touchAddress(address, treeIndex, subIndex, accessMode); long gas = 0; - if (accessEvent.isBranchRead()) { - gas = clampedAdd(gas, WITNESS_BRANCH_READ_COST); - if (logEnabled) { - System.out.println( - "touchAddressAndChargeGas WitnessBranchReadCost " - + address - + " " - + treeIndex - + " " - + subIndex - + " " - + isWrite - + " " - + gas); - } + if (AccessEvents.isBranchRead(accessEvents)) { + gas = clampedAdd(gas, WITNESS_BRANCH_COST); + final long gasView = gas; + LOG.atDebug().log( + () -> + "touchAddressAndChargeGas WITNESS_BRANCH_COST " + + address + + " " + + treeIndex + + " " + + subIndex + + " " + + AccessMode.toString(accessMode) + + " " + + gasView); } - if (accessEvent.isChunkRead()) { - gas = clampedAdd(gas, WITNESS_CHUNK_READ_COST); - if (logEnabled) { - System.out.println( - "touchAddressAndChargeGas WitnessChunkReadCost " - + address - + " " - + treeIndex - + " " - + subIndex - + " " - + isWrite - + " " - + gas); - } + if (AccessEvents.isLeafRead(accessEvents)) { + gas = clampedAdd(gas, WITNESS_CHUNK_COST); + final long gasView = gas; + LOG.atDebug().log( + () -> + "touchAddressAndChargeGas WITNESS_CHUNK_COST " + + address + + " " + + treeIndex + + " " + + subIndex + + " " + + AccessMode.toString(accessMode) + + " " + + gasView); } - if (accessEvent.isBranchWrite()) { - gas = clampedAdd(gas, WITNESS_BRANCH_WRITE_COST); - if (logEnabled) { - System.out.println( - "touchAddressAndChargeGas WitnessBranchWriteCost " - + address - + " " - + treeIndex - + " " - + subIndex - + " " - + isWrite - + " " - + gas); - } + if (AccessEvents.isBranchWrite(accessEvents)) { + gas = clampedAdd(gas, SUBTREE_EDIT_COST); + final long gasView = gas; + LOG.atDebug().log( + () -> + "touchAddressAndChargeGas SUBTREE_EDIT_COST " + + address + + " " + + treeIndex + + " " + + subIndex + + " " + + AccessMode.toString(accessMode) + + " " + + gasView); } - if (accessEvent.isChunkWrite()) { - gas = clampedAdd(gas, WITNESS_CHUNK_WRITE_COST); - if (logEnabled) { - System.out.println( - "touchAddressAndChargeGas WitnessChunkWriteCost " - + address - + " " - + treeIndex - + " " - + subIndex - + " " - + isWrite - + " " - + gas); - } + if (AccessEvents.isLeafReset(accessEvents)) { + gas = clampedAdd(gas, CHUNK_EDIT_COST); + final long gasView = gas; + LOG.atDebug().log( + () -> + "touchAddressAndChargeGas CHUNK_EDIT_COST " + + address + + " " + + treeIndex + + " " + + subIndex + + " " + + AccessMode.toString(accessMode) + + " " + + gasView); } - if (accessEvent.isChunkFill()) { - gas = clampedAdd(gas, WITNESS_CHUNK_FILL_COST); - if (logEnabled) { - System.out.println( - "touchAddressAndChargeGas WitnessChunkFillCost " - + address - + " " - + treeIndex - + " " - + subIndex - + " " - + isWrite - + " " - + gas); - } + if (AccessEvents.isLeafSet(accessEvents)) { + gas = clampedAdd(gas, CHUNK_FILL_COST); + final long gasView = gas; + LOG.atDebug().log( + () -> + "touchAddressAndChargeGas CHUNK_FILL_COST " + + address + + " " + + treeIndex + + " " + + subIndex + + " " + + AccessMode.toString(accessMode) + + " " + + gasView); } return gas; } - public AccessEvents touchAddress( - final Address addr, final UInt256 treeIndex, final UInt256 subIndex, final boolean isWrite) { - AccessEvents accessEvents = new AccessEvents(); + public short touchAddress( + final Address addr, final UInt256 treeIndex, final UInt256 leafIndex, final int accessMode) { + short accessEvents = AccessEvents.NONE; BranchAccessKey branchKey = new BranchAccessKey(addr, treeIndex); - ChunkAccessKey chunkKey = new ChunkAccessKey(addr, treeIndex, subIndex); + accessEvents |= touchAddressForBranch(branchKey, accessMode); + if (leafIndex != null) { + accessEvents |= touchAddressForLeaf(branchKey, leafIndex, accessMode); + } + + return accessEvents; + } + + private short touchAddressForBranch(final BranchAccessKey branchKey, final int accessMode) { + short accessEvents = AccessEvents.NONE; - // Read access. if (!this.branches.containsKey(branchKey)) { - accessEvents.setBranchRead(true); - this.branches.put(branchKey, AccessWitnessReadFlag); - } - if (!this.chunks.containsKey(chunkKey)) { - accessEvents.setChunkRead(true); - this.chunks.put(chunkKey, AccessWitnessReadFlag); + accessEvents |= AccessEvents.BRANCH_READ; + this.branches.put(branchKey, AccessMode.READ); } - // TODO VERKLE: for now testnet doesn't charge - // chunk filling costs if the leaf was previously empty in the state - // boolean chunkFill = false; + // A write is always a read + if (AccessMode.isWrite(accessMode)) { + int previousAccessMode = + !this.branches.containsKey(branchKey) ? AccessMode.NONE : this.branches.get(branchKey); + if (!AccessMode.isWrite(previousAccessMode)) { + accessEvents |= AccessEvents.BRANCH_WRITE; + this.branches.put(branchKey, (previousAccessMode | accessMode)); + } + } - if (isWrite) { + return accessEvents; + } - if ((this.branches.get(branchKey) & AccessWitnessWriteFlag) == 0) { - accessEvents.setBranchWrite(true); - this.branches.put( - branchKey, (byte) (this.branches.get(branchKey) | AccessWitnessWriteFlag)); - } + private short touchAddressForLeaf( + final BranchAccessKey branchKey, final UInt256 subIndex, final int accessMode) { + LeafAccessKey leafKey = new LeafAccessKey(branchKey, subIndex); + short accessEvents = AccessEvents.NONE; - byte chunkValue = this.chunks.get(chunkKey); + if (!this.leaves.containsKey(leafKey)) { + accessEvents |= AccessEvents.LEAF_READ; + this.leaves.put(leafKey, AccessMode.READ); + } - if ((chunkValue & AccessWitnessWriteFlag) == 0) { - accessEvents.setChunkWrite(true); - this.chunks.put(chunkKey, (byte) (this.chunks.get(chunkKey) | AccessWitnessWriteFlag)); + // A write is always a read + if (AccessMode.isWrite(accessMode)) { + int previousAccessMode = + !this.leaves.containsKey(leafKey) ? AccessMode.NONE : this.leaves.get(leafKey); + if (!AccessMode.isWrite(previousAccessMode)) { + accessEvents |= AccessEvents.LEAF_RESET; + if (AccessMode.isWriteSet(accessMode)) { + accessEvents |= AccessEvents.LEAF_SET; + } + this.leaves.put(leafKey, (previousAccessMode | accessMode)); } } - return accessEvents; } @@ -362,41 +436,20 @@ public class Eip4762AccessWitness implements org.hyperledger.besu.datatypes.Acce if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Eip4762AccessWitness that = (Eip4762AccessWitness) o; - return Objects.equals(branches, that.branches) && Objects.equals(chunks, that.chunks); + return Objects.equals(branches, that.branches) && Objects.equals(leaves, that.leaves); } @Override public int hashCode() { - return Objects.hash(branches, chunks); + return Objects.hash(branches, leaves); } @Override public String toString() { - return "AccessWitness{" + "branches=" + branches + ", chunks=" + chunks + '}'; - } - - public Map getBranches() { - return branches; - } - - public Map getChunks() { - return chunks; + return "AccessWitness{" + "leaves=" + leaves + ", branches=" + branches + '}'; } public record BranchAccessKey(Address address, UInt256 treeIndex) {} - ; - public record ChunkAccessKey(BranchAccessKey branchAccessKey, UInt256 chunkIndex) { - public ChunkAccessKey( - final Address address, final UInt256 treeIndex, final UInt256 chunkIndex) { - this(new BranchAccessKey(address, treeIndex), chunkIndex); - } - } - - @Override - public List getStorageSlotTreeIndexes(final UInt256 storageKey) { - return List.of( - TRIE_KEY_ADAPTER.locateStorageKeyOffset(storageKey), - TRIE_KEY_ADAPTER.locateStorageKeySuffix(storageKey)); - } + public record LeafAccessKey(BranchAccessKey branchAccessKey, UInt256 leafIndex) {} } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/stateless/NoopAccessWitness.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/stateless/NoopAccessWitness.java index b1dae1e6a6..c88bc7d40a 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/stateless/NoopAccessWitness.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/stateless/NoopAccessWitness.java @@ -14,40 +14,55 @@ */ package org.hyperledger.besu.evm.gascalculator.stateless; +import org.hyperledger.besu.datatypes.AccessWitness; import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Wei; + +import java.util.Optional; import org.apache.tuweni.units.bigints.UInt256; -public class NoopAccessWitness extends Eip4762AccessWitness { +public class NoopAccessWitness implements AccessWitness { - @Override - public long touchAndChargeProofOfAbsence(final Address address) { - return 0; + private static NoopAccessWitness instance; + + private NoopAccessWitness() {} + + public static NoopAccessWitness get() { + if (instance == null) { + instance = new NoopAccessWitness(); + } + return instance; } @Override - public long touchAndChargeValueTransfer(final Address caller, final Address target) { + public long touchAndChargeValueTransfer( + final Address caller, + final Address target, + final boolean isAccountCreation, + final long warmReadCost) { return 0; } @Override - public long touchAndChargeMessageCall(final Address address) { + public long touchAndChargeValueTransferSelfDestruct( + final Address caller, + final Address target, + final boolean isAccountCreation, + final long warmReadCost) { return 0; } @Override - public long touchTxOriginAndComputeGas(final Address origin) { + public long touchAddressAndChargeRead(final Address address, final UInt256 leafKey) { return 0; } @Override - public long touchTxExistingAndComputeGas(final Address target, final boolean sendsValue) { - return 0; - } + public void touchBaseTx(final Address origin, final Optional
target, final Wei value) {} @Override - public long touchAndChargeContractCreateInit( - final Address address, final boolean createSendsValue) { + public long touchAndChargeProofOfAbsence(final Address address) { return 0; } @@ -57,14 +72,13 @@ public class NoopAccessWitness extends Eip4762AccessWitness { } @Override - public long touchAddressOnWriteAndComputeGas( - final Address address, final UInt256 treeIndex, final UInt256 subIndex) { + public long touchAndChargeStorageLoad(final Address address, final UInt256 storageKey) { return 0; } @Override - public long touchAddressOnReadAndComputeGas( - final Address address, final UInt256 treeIndex, final UInt256 subIndex) { + public long touchAndChargeStorageStore( + final Address address, final UInt256 storageKey, final boolean hasPreviousValue) { return 0; } @@ -75,13 +89,11 @@ public class NoopAccessWitness extends Eip4762AccessWitness { @Override public long touchCodeChunks( - final Address address, final long offset, final long readSize, final long codeLength) { - return 0; - } - - @Override - public long touchCodeChunksWithoutAccessCost( - final Address address, final long offset, final long readSize, final long codeLength) { + final Address address, + final boolean isContractInDeployment, + final long offset, + final long readSize, + final long codeLength) { return 0; } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCreateOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCreateOperation.java index 3cd0f5271a..caef3b10ae 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCreateOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCreateOperation.java @@ -141,9 +141,6 @@ public abstract class AbstractCreateOperation extends AbstractOperation { */ protected abstract long cost(final MessageFrame frame, Supplier codeSupplier); - protected abstract long statelessCost( - final MessageFrame frame, final Address contractAddress, final Wei value); - /** * Target contract address. * diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/BalanceOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/BalanceOperation.java index 735bb5b6cb..06d1581a30 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/BalanceOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/BalanceOperation.java @@ -20,12 +20,8 @@ import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; -import org.hyperledger.besu.evm.internal.OverflowException; -import org.hyperledger.besu.evm.internal.UnderflowException; import org.hyperledger.besu.evm.internal.Words; -import java.util.Optional; - import org.apache.tuweni.bytes.Bytes; /** The Balance operation. */ @@ -44,38 +40,27 @@ public class BalanceOperation extends AbstractOperation { * Gets Balance operation Gas Cost plus warm storage read cost or cold account access cost. * * @param frame current frame - * @param maybeAddress to use for get balance + * @param address to use for get balance * @param accountIsWarm true to add warm storage read cost, false to add cold account access cost * @return the long */ protected long cost( - final MessageFrame frame, final Optional
maybeAddress, final boolean accountIsWarm) { - return gasCalculator().getBalanceOperationGasCost(frame, accountIsWarm, maybeAddress); + final MessageFrame frame, final Address address, final boolean accountIsWarm) { + return gasCalculator().balanceOperationGasCost(frame, accountIsWarm, address); } @Override public OperationResult execute(final MessageFrame frame, final EVM evm) { - final Address address; - try { - address = Words.toAddress(frame.popStackItem()); - } catch (final UnderflowException ufe) { - return new OperationResult( - cost(frame, Optional.empty(), true), ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS); - } - try { - final boolean accountIsWarm = - frame.warmUpAddress(address) || gasCalculator().isPrecompile(address); - final long cost = cost(frame, Optional.of(address), accountIsWarm); - if (frame.getRemainingGas() < cost) { - return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS); - } else { - final Account account = frame.getWorldUpdater().get(address); - frame.pushStackItem(account == null ? Bytes.EMPTY : account.getBalance()); - return new OperationResult(cost, null); - } - } catch (final OverflowException ofe) { - return new OperationResult( - cost(frame, Optional.of(address), true), ExceptionalHaltReason.TOO_MANY_STACK_ITEMS); + final Address address = Words.toAddress(frame.popStackItem()); + final boolean accountIsWarm = + frame.warmUpAddress(address) || gasCalculator().isPrecompile(address); + final long cost = cost(frame, address, accountIsWarm); + if (frame.getRemainingGas() < cost) { + return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS); + } else { + final Account account = frame.getWorldUpdater().get(address); + frame.pushStackItem(account == null ? Bytes.EMPTY : account.getBalance()); + return new OperationResult(cost, null); } } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/CallCodeOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/CallCodeOperation.java index 471da16906..e9bfe77d12 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/CallCodeOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/CallCodeOperation.java @@ -18,6 +18,7 @@ import static org.hyperledger.besu.evm.internal.Words.clampedToLong; import org.hyperledger.besu.datatypes.Address; import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.internal.Words; @@ -74,6 +75,31 @@ public class CallCodeOperation extends AbstractCallOperation { return frame.getRecipientAddress(); } + @Override + public long cost(final MessageFrame frame, final boolean accountIsWarm) { + final long stipend = gas(frame); + final long inputDataOffset = inputDataOffset(frame); + final long inputDataLength = inputDataLength(frame); + final long outputDataOffset = outputDataOffset(frame); + final long outputDataLength = outputDataLength(frame); + final Account recipient = frame.getWorldUpdater().get(address(frame)); + final Address to = to(frame); + GasCalculator gasCalculator = gasCalculator(); + + return gasCalculator.callOperationGasCost( + frame, + stipend, + inputDataOffset, + inputDataLength, + outputDataOffset, + outputDataLength, + // As far as CALLCODE is concerned, there isn't a value transfer + Wei.ZERO, + recipient, + to, + accountIsWarm); + } + @Override protected Address sender(final MessageFrame frame) { return frame.getRecipientAddress(); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/Create2Operation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/Create2Operation.java index 4ab2df8c64..3338a1e26a 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/Create2Operation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/Create2Operation.java @@ -20,7 +20,6 @@ import static org.hyperledger.besu.evm.internal.Words.clampedToInt; import static org.hyperledger.besu.evm.internal.Words.clampedToLong; import org.hyperledger.besu.datatypes.Address; -import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.MessageFrame; @@ -57,12 +56,6 @@ public class Create2Operation extends AbstractCreateOperation { gasCalculator().createKeccakCost(inputSize), gasCalculator().initcodeCost(inputSize))); } - @Override - protected long statelessCost( - final MessageFrame frame, final Address contractAddress, final Wei value) { - return gasCalculator().initcodeStatelessCost(frame, contractAddress, value); - } - @Override public Address generateTargetContractAddress(final MessageFrame frame, final Code initcode) { final Address sender = frame.getRecipientAddress(); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/CreateOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/CreateOperation.java index 454b12c0f7..9537c5012d 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/CreateOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/CreateOperation.java @@ -19,7 +19,6 @@ import static org.hyperledger.besu.evm.internal.Words.clampedToInt; import static org.hyperledger.besu.evm.internal.Words.clampedToLong; import org.hyperledger.besu.datatypes.Address; -import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.account.Account; @@ -53,12 +52,6 @@ public class CreateOperation extends AbstractCreateOperation { gasCalculator().initcodeCost(inputSize)); } - @Override - protected long statelessCost( - final MessageFrame frame, final Address contractAddress, final Wei value) { - return gasCalculator().initcodeStatelessCost(frame, contractAddress, value); - } - @Override protected Address generateTargetContractAddress(final MessageFrame frame, final Code initcode) { final Account sender = frame.getWorldUpdater().get(frame.getRecipientAddress()); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCreateOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCreateOperation.java index cb4153c258..3ea3ef1407 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCreateOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/EOFCreateOperation.java @@ -20,7 +20,6 @@ import static org.hyperledger.besu.evm.internal.Words.clampedToInt; import static org.hyperledger.besu.evm.internal.Words.clampedToLong; import org.hyperledger.besu.datatypes.Address; -import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.evm.Code; import org.hyperledger.besu.evm.EVM; import org.hyperledger.besu.evm.frame.MessageFrame; @@ -59,12 +58,6 @@ public class EOFCreateOperation extends AbstractCreateOperation { gasCalculator().createKeccakCost(codeSupplier.get().getSize()))); } - @Override - protected long statelessCost( - final MessageFrame frame, final Address contractAddress, final Wei value) { - return gasCalculator().initcodeStatelessCost(frame, contractAddress, value); - } - @Override public Address generateTargetContractAddress(final MessageFrame frame, final Code initcode) { final Address sender = frame.getRecipientAddress(); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeCopyOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeCopyOperation.java index 028505e0cf..738c8864e5 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeCopyOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeCopyOperation.java @@ -103,7 +103,6 @@ public class ExtCodeCopyOperation extends AbstractOperation { } } - // TODO get account and get code before cost checking only for verkle ? maybe move this call final Bytes code = account != null ? account.getCode() : Bytes.EMPTY; final long cost = diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeHashOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeHashOperation.java index 81b796d10c..de268fbbfd 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeHashOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeHashOperation.java @@ -24,13 +24,9 @@ import org.hyperledger.besu.evm.code.EOFLayout; import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; -import org.hyperledger.besu.evm.internal.OverflowException; -import org.hyperledger.besu.evm.internal.UnderflowException; import org.hyperledger.besu.evm.internal.Words; import org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper; -import java.util.Optional; - import org.apache.tuweni.bytes.Bytes; /** The Ext code hash operation. */ @@ -65,64 +61,49 @@ public class ExtCodeHashOperation extends AbstractOperation { * Cost of Ext code hash operation. * * @param frame the current frame - * @param maybeAddress the address to use + * @param address the address to use * @param accountIsWarm the account is warm * @return the long */ protected long cost( - final MessageFrame frame, final Optional
maybeAddress, final boolean accountIsWarm) { - return gasCalculator().extCodeHashOperationGasCost(frame, accountIsWarm, maybeAddress); + final MessageFrame frame, final Address address, final boolean accountIsWarm) { + return gasCalculator().extCodeHashOperationGasCost(frame, accountIsWarm, address); } @Override public OperationResult execute(final MessageFrame frame, final EVM evm) { - final Address address; - try { - address = Words.toAddress(frame.popStackItem()); - } catch (final UnderflowException ufe) { - // TODO not sure about this case, we need to check what is the gas cost in case of underflow - // exception - return new OperationResult( - cost(frame, Optional.empty(), true), ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS); + final Address address = Words.toAddress(frame.popStackItem()); + final boolean accountIsWarm = + frame.warmUpAddress(address) || gasCalculator().isPrecompile(address); + final long cost = cost(frame, address, accountIsWarm); + if (frame.getRemainingGas() < cost) { + return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS); } - try { - final boolean accountIsWarm = - frame.warmUpAddress(address) || gasCalculator().isPrecompile(address); - final long cost = cost(frame, Optional.of(address), accountIsWarm); - if (frame.getRemainingGas() < cost) { - return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS); - } - final Account account = frame.getWorldUpdater().get(address); + final Account account = frame.getWorldUpdater().get(address); - if (account != null) { - final DelegatedCodeGasCostHelper.Result result = - deductDelegatedCodeGasCost(frame, gasCalculator(), account); - if (result.status() != DelegatedCodeGasCostHelper.Status.SUCCESS) { - return new Operation.OperationResult( - result.gasCost(), ExceptionalHaltReason.INSUFFICIENT_GAS); - } + if (account != null) { + final DelegatedCodeGasCostHelper.Result result = + deductDelegatedCodeGasCost(frame, gasCalculator(), account); + if (result.status() != DelegatedCodeGasCostHelper.Status.SUCCESS) { + return new Operation.OperationResult( + result.gasCost(), ExceptionalHaltReason.INSUFFICIENT_GAS); } + } - if (account == null || account.isEmpty()) { - frame.pushStackItem(Bytes.EMPTY); + if (account == null || account.isEmpty()) { + frame.pushStackItem(Bytes.EMPTY); + } else { + final Bytes code = account.getCode(); + if (enableEIP3540 + && code.size() >= 2 + && code.get(0) == EOFLayout.EOF_PREFIX_BYTE + && code.get(1) == 0) { + frame.pushStackItem(EOF_REPLACEMENT_HASH); } else { - final Bytes code = account.getCode(); - if (enableEIP3540 - && code.size() >= 2 - && code.get(0) == EOFLayout.EOF_PREFIX_BYTE - && code.get(1) == 0) { - frame.pushStackItem(EOF_REPLACEMENT_HASH); - } else { - frame.pushStackItem(account.getCodeHash()); - } + frame.pushStackItem(account.getCodeHash()); } - return new OperationResult(cost, null); - } catch (final OverflowException ofe) { - // TODO not sure about this case, we need to check what is the gas cost in case of overflow - // exception - return new OperationResult( - cost(frame, Optional.of(address), true), ExceptionalHaltReason.TOO_MANY_STACK_ITEMS); } + return new OperationResult(cost, null); } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeSizeOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeSizeOperation.java index 8eb4a35d44..36edbcc486 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeSizeOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/ExtCodeSizeOperation.java @@ -23,13 +23,9 @@ import org.hyperledger.besu.evm.code.EOFLayout; import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; -import org.hyperledger.besu.evm.internal.OverflowException; -import org.hyperledger.besu.evm.internal.UnderflowException; import org.hyperledger.besu.evm.internal.Words; import org.hyperledger.besu.evm.worldstate.DelegatedCodeGasCostHelper; -import java.util.Optional; - import org.apache.tuweni.bytes.Bytes; /** The Ext code size operation. */ @@ -66,57 +62,46 @@ public class ExtCodeSizeOperation extends AbstractOperation { * @return the long */ protected long cost( - final boolean accountIsWarm, final MessageFrame frame, final Optional
maybeAddress) { - return gasCalculator().getExtCodeSizeOperationGasCost(frame, accountIsWarm, maybeAddress); + final boolean accountIsWarm, final MessageFrame frame, final Address address) { + return gasCalculator().extCodeSizeOperationGasCost(frame, accountIsWarm, address); } @Override public OperationResult execute(final MessageFrame frame, final EVM evm) { - final Address address; - try { - address = Words.toAddress(frame.popStackItem()); - } catch (final UnderflowException ufe) { - return new OperationResult( - cost(true, frame, Optional.empty()), ExceptionalHaltReason.INSUFFICIENT_STACK_ITEMS); - } - try { - final boolean accountIsWarm = - frame.warmUpAddress(address) || gasCalculator().isPrecompile(address); - final long cost = cost(accountIsWarm, frame, Optional.of(address)); - if (frame.getRemainingGas() < cost) { - return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS); - } else { - final Account account = frame.getWorldUpdater().get(address); + final Address address = Words.toAddress(frame.popStackItem()); + final boolean accountIsWarm = + frame.warmUpAddress(address) || gasCalculator().isPrecompile(address); + final long cost = cost(accountIsWarm, frame, address); + if (frame.getRemainingGas() < cost) { + return new OperationResult(cost, ExceptionalHaltReason.INSUFFICIENT_GAS); + } else { + final Account account = frame.getWorldUpdater().get(address); - if (account != null) { - final DelegatedCodeGasCostHelper.Result result = - deductDelegatedCodeGasCost(frame, gasCalculator(), account); - if (result.status() != DelegatedCodeGasCostHelper.Status.SUCCESS) { - return new Operation.OperationResult( - result.gasCost(), ExceptionalHaltReason.INSUFFICIENT_GAS); - } + if (account != null) { + final DelegatedCodeGasCostHelper.Result result = + deductDelegatedCodeGasCost(frame, gasCalculator(), account); + if (result.status() != DelegatedCodeGasCostHelper.Status.SUCCESS) { + return new Operation.OperationResult( + result.gasCost(), ExceptionalHaltReason.INSUFFICIENT_GAS); } + } - Bytes codeSize; - if (account == null) { - codeSize = Bytes.EMPTY; + Bytes codeSize; + if (account == null) { + codeSize = Bytes.EMPTY; + } else { + final Bytes code = account.getCode(); + if (enableEIP3540 + && code.size() >= 2 + && code.get(0) == EOFLayout.EOF_PREFIX_BYTE + && code.get(1) == 0) { + codeSize = EOF_SIZE; } else { - final Bytes code = account.getCode(); - if (enableEIP3540 - && code.size() >= 2 - && code.get(0) == EOFLayout.EOF_PREFIX_BYTE - && code.get(1) == 0) { - codeSize = EOF_SIZE; - } else { - codeSize = Words.intBytes(code.size()); - } + codeSize = Words.intBytes(code.size()); } - frame.pushStackItem(codeSize); - return new OperationResult(cost, null); } - } catch (final OverflowException ofe) { - return new OperationResult( - cost(true, frame, Optional.of(address)), ExceptionalHaltReason.TOO_MANY_STACK_ITEMS); + frame.pushStackItem(codeSize); + return new OperationResult(cost, null); } } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/PushOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/PushOperation.java index e4f4e65cee..ab094b0e57 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/PushOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/PushOperation.java @@ -54,7 +54,7 @@ public class PushOperation extends AbstractOperation { if (code.length <= copyStart) { push = Bytes.EMPTY; } else { - final int copyLength = Math.min(length, code.length - pc - 1); + final int copyLength = Math.min(length, code.length - copyStart); push = Bytes.wrap(code, copyStart, copyLength); } frame.pushStackItem(push); diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/SLoadOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/SLoadOperation.java index 9a4d6e8428..b55d07e432 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/SLoadOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/SLoadOperation.java @@ -39,7 +39,7 @@ public class SLoadOperation extends AbstractOperation { } protected long cost(final MessageFrame frame, final Bytes32 key, final boolean slotIsWarm) { - return gasCalculator().getSloadOperationGasCost(frame, UInt256.fromBytes(key), slotIsWarm); + return gasCalculator().sloadOperationGasCost(frame, UInt256.fromBytes(key), slotIsWarm); } @Override diff --git a/evm/src/main/java/org/hyperledger/besu/evm/processor/ContractCreationProcessor.java b/evm/src/main/java/org/hyperledger/besu/evm/processor/ContractCreationProcessor.java index d71e920c94..8731767366 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/processor/ContractCreationProcessor.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/processor/ContractCreationProcessor.java @@ -99,6 +99,9 @@ public class ContractCreationProcessor extends AbstractMessageProcessor { Address contractAddress = frame.getContractAddress(); final MutableAccount contract = frame.getWorldUpdater().getOrCreate(contractAddress); + long statelessGasCost = + evm.getGasCalculator().proofOfAbsenceCost(frame, contract.getAddress()); + frame.decrementRemainingGas(statelessGasCost); if (accountExists(contract)) { LOG.trace( "Contract creation error: account has already been created for address {}", diff --git a/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java b/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java index 4c415177c8..4c5da84f31 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/operation/AbstractCreateOperationTest.java @@ -116,12 +116,6 @@ class AbstractCreateOperationTest { gasCalculator().initcodeCost(inputSize)); } - @Override - protected long statelessCost( - final MessageFrame frame, final Address contractAddress, final Wei value) { - return gasCalculator().initcodeStatelessCost(frame, contractAddress, value); - } - @Override protected Address generateTargetContractAddress(final MessageFrame frame, final Code initcode) { final Account sender = frame.getWorldUpdater().get(frame.getRecipientAddress());