From 3ecb09d72157ff7a8ae1f1cf82000e955a872bc2 Mon Sep 17 00:00:00 2001 From: ahamlat Date: Fri, 23 Dec 2022 18:00:03 +0100 Subject: [PATCH] Optimize Sstore operation execution time (#4836) * Optimize Sstore operation, get current value and original value only once. Signed-off-by: Ameziane H * Fix javadoc. Signed-off-by: Ameziane H * refactoring (add a supplier implementation to better support all the forks) Signed-off-by: Ameziane H * delete unnecessary parameters, add final for some fields and modify CHANGELOG.md Signed-off-by: Ameziane H * add a missing parameter in Javadoc Signed-off-by: Ameziane H Signed-off-by: Ameziane H --- CHANGELOG.md | 1 + .../bonsai/BonsaiWorldStateUpdater.java | 2 +- .../ethereum/mainnet/RefundSstoreGasTest.java | 21 +++++++--- .../gascalculator/BerlinGasCalculator.java | 37 ++++++++++-------- .../ConstantinopleGasCalculator.java | 38 ++++++++++-------- .../gascalculator/FrontierGasCalculator.java | 15 ++++--- .../besu/evm/gascalculator/GasCalculator.java | 15 ++++--- .../gascalculator/IstanbulGasCalculator.java | 39 ++++++++++--------- .../gascalculator/LondonGasCalculator.java | 26 +++++++------ .../PetersburgGasCalculator.java | 23 +++++------ .../besu/evm/operation/SStoreOperation.java | 19 ++++++--- 11 files changed, 138 insertions(+), 98 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 054bd16b5d0..8b084a7f98a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - Optimization: Memoize transaction size and hash at the same time [#4812](https://github.com/hyperledger/besu/pull/4812) ### Breaking Changes +- Optimize SSTORE Operation execution time (memoize current and original value) [#4836](https://github.com/hyperledger/besu/pull/4836) ### Bug Fixes diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateUpdater.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateUpdater.java index 5dcdf44dae6..1eb307adb7e 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateUpdater.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/bonsai/BonsaiWorldStateUpdater.java @@ -367,8 +367,8 @@ public Optional getStorageValueBySlotHash(final Address address, final public UInt256 getPriorStorageValue(final Address address, final UInt256 storageKey) { // TODO maybe log the read into the trie layer? final Map> localAccountStorage = storageToUpdate.get(address); - final Hash slotHash = Hash.hash(storageKey); if (localAccountStorage != null) { + final Hash slotHash = Hash.hash(storageKey); final BonsaiValue value = localAccountStorage.get(slotHash); if (value != null) { if (value.isCleared()) { diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/RefundSstoreGasTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/RefundSstoreGasTest.java index 2efba9619c3..341695ec318 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/RefundSstoreGasTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/RefundSstoreGasTest.java @@ -19,12 +19,12 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator; import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.gascalculator.IstanbulGasCalculator; import org.hyperledger.besu.evm.gascalculator.PetersburgGasCalculator; +import com.google.common.base.Supplier; import org.apache.tuweni.units.bigints.UInt256; import org.assertj.core.api.Assertions; import org.junit.Before; @@ -122,24 +122,33 @@ public static Object[][] scenarios() { @Parameter(value = 6) public long expectedGasRefund; - private final Account account = mock(Account.class); + private final Supplier mockSupplierForOriginalValue = mockSupplier(); + private final Supplier mockSupplierCurrentValue = mockSupplier(); + + @SuppressWarnings("unchecked") + private Supplier mockSupplier() { + return mock(Supplier.class); + } @Before public void setUp() { - when(account.getOriginalStorageValue(UInt256.ZERO)).thenReturn(originalValue); - when(account.getStorageValue(UInt256.ZERO)).thenReturn(currentValue); + when(mockSupplierForOriginalValue.get()).thenReturn(originalValue); + when(mockSupplierCurrentValue.get()).thenReturn(currentValue); } @Test public void shouldChargeCorrectGas() { - Assertions.assertThat(gasCalculator.calculateStorageCost(account, UInt256.ZERO, newValue)) + Assertions.assertThat( + gasCalculator.calculateStorageCost( + newValue, mockSupplierCurrentValue, mockSupplierForOriginalValue)) .isEqualTo(expectedGasCost); } @Test public void shouldRefundCorrectGas() { Assertions.assertThat( - gasCalculator.calculateStorageRefundAmount(account, UInt256.ZERO, newValue)) + gasCalculator.calculateStorageRefundAmount( + newValue, mockSupplierCurrentValue, mockSupplierForOriginalValue)) .isEqualTo(expectedGasRefund); } } 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 3d208756bdf..89811e2177f 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 @@ -28,6 +28,7 @@ import java.math.BigInteger; +import com.google.common.base.Supplier; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.units.bigints.UInt256; @@ -163,15 +164,17 @@ public long extCodeCopyOperationGasCost( @Override // As per https://eips.ethereum.org/EIPS/eip-2200 public long calculateStorageCost( - final Account account, final UInt256 key, final UInt256 newValue) { + final UInt256 newValue, + final Supplier currentValue, + final Supplier originalValue) { - final UInt256 currentValue = account.getStorageValue(key); - if (currentValue.equals(newValue)) { + final UInt256 localCurrentValue = currentValue.get(); + if (localCurrentValue.equals(newValue)) { return SLOAD_GAS; } else { - final UInt256 originalValue = account.getOriginalStorageValue(key); - if (originalValue.equals(currentValue)) { - return originalValue.isZero() ? SSTORE_SET_GAS : SSTORE_RESET_GAS; + final UInt256 localOriginalValue = originalValue.get(); + if (localOriginalValue.equals(localCurrentValue)) { + return localOriginalValue.isZero() ? SSTORE_SET_GAS : SSTORE_RESET_GAS; } else { return SLOAD_GAS; } @@ -182,15 +185,17 @@ public long calculateStorageCost( @Override // As per https://eips.ethereum.org/EIPS/eip-2200 public long calculateStorageRefundAmount( - final Account account, final UInt256 key, final UInt256 newValue) { + final UInt256 newValue, + final Supplier currentValue, + final Supplier originalValue) { - final UInt256 currentValue = account.getStorageValue(key); - if (currentValue.equals(newValue)) { + final UInt256 localCurrentValue = currentValue.get(); + if (localCurrentValue.equals(newValue)) { return 0L; } else { - final UInt256 originalValue = account.getOriginalStorageValue(key); - if (originalValue.equals(currentValue)) { - if (originalValue.isZero()) { + final UInt256 localOriginalValue = originalValue.get(); + if (localOriginalValue.equals(localCurrentValue)) { + if (localOriginalValue.isZero()) { return 0L; } else if (newValue.isZero()) { return SSTORE_CLEARS_SCHEDULE; @@ -199,18 +204,18 @@ public long calculateStorageRefundAmount( } } else { long refund = 0L; - if (!originalValue.isZero()) { - if (currentValue.isZero()) { + if (!localOriginalValue.isZero()) { + if (localCurrentValue.isZero()) { refund = NEGATIVE_SSTORE_CLEARS_SCHEDULE; } else if (newValue.isZero()) { refund = SSTORE_CLEARS_SCHEDULE; } } - if (originalValue.equals(newValue)) { + if (localOriginalValue.equals(newValue)) { refund = refund - + (originalValue.isZero() + + (localOriginalValue.isZero() ? SSTORE_SET_GAS_LESS_SLOAD_GAS : SSTORE_RESET_GAS_LESS_SLOAD_GAS); } 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 c71feab43f9..c9868823285 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 @@ -18,9 +18,9 @@ import static org.hyperledger.besu.evm.internal.Words.clampedMultiply; import static org.hyperledger.besu.evm.internal.Words.clampedToLong; -import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.frame.MessageFrame; +import com.google.common.base.Supplier; import org.apache.tuweni.bytes.Bytes32; import org.apache.tuweni.units.bigints.UInt256; @@ -48,15 +48,17 @@ public long create2OperationGasCost(final MessageFrame frame) { @Override // As per https://eips.ethereum.org/EIPS/eip-1283 public long calculateStorageCost( - final Account account, final UInt256 key, final UInt256 newValue) { + final UInt256 newValue, + final Supplier currentValue, + final Supplier originalValue) { - final UInt256 currentValue = account.getStorageValue(key); - if (currentValue.equals(newValue)) { + final UInt256 localCurrentValue = currentValue.get(); + if (localCurrentValue.equals(newValue)) { return SSTORE_NO_OP_COST; } else { - final UInt256 originalValue = account.getOriginalStorageValue(key); - if (originalValue.equals(currentValue)) { - return originalValue.isZero() + final UInt256 localOriginalValue = originalValue.get(); + if (localOriginalValue.equals(localCurrentValue)) { + return localOriginalValue.isZero() ? SSTORE_FIRST_DIRTY_NEW_STORAGE_COST : SSTORE_FIRST_DIRTY_EXISTING_STORAGE_COST; } else { @@ -68,15 +70,17 @@ public long calculateStorageCost( @Override // As per https://eips.ethereum.org/EIPS/eip-1283 public long calculateStorageRefundAmount( - final Account account, final UInt256 key, final UInt256 newValue) { + final UInt256 newValue, + final Supplier currentValue, + final Supplier originalValue) { - final UInt256 currentValue = account.getStorageValue(key); - if (currentValue.equals(newValue)) { + UInt256 localCurrentValue = currentValue.get(); + if (localCurrentValue.equals(newValue)) { return 0L; } else { - final UInt256 originalValue = account.getOriginalStorageValue(key); - if (originalValue.equals(currentValue)) { - if (originalValue.isZero()) { + UInt256 localOriginalValue = originalValue.get(); + if (localOriginalValue.equals(localCurrentValue)) { + if (localOriginalValue.isZero()) { return 0L; } else if (newValue.isZero()) { return STORAGE_RESET_REFUND_AMOUNT; @@ -85,18 +89,18 @@ public long calculateStorageRefundAmount( } } else { long refund = 0L; - if (!originalValue.isZero()) { - if (currentValue.isZero()) { + if (!localOriginalValue.isZero()) { + if (localCurrentValue.isZero()) { refund = NEGATIVE_STORAGE_RESET_REFUND_AMOUNT; } else if (newValue.isZero()) { refund = STORAGE_RESET_REFUND_AMOUNT; } } - if (originalValue.equals(newValue)) { + if (localOriginalValue.equals(newValue)) { refund = refund - + (originalValue.isZero() + + (localOriginalValue.isZero() ? SSTORE_DIRTY_RETURN_TO_UNUSED_REFUND_AMOUNT : SSTORE_DIRTY_RETURN_TO_ORIGINAL_VALUE_REFUND_AMOUNT); } 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 9e18da60793..a46e645d6d4 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 @@ -25,6 +25,7 @@ import org.hyperledger.besu.evm.internal.Words; import org.hyperledger.besu.evm.operation.ExpOperation; +import com.google.common.base.Supplier; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.units.bigints.UInt256; @@ -417,18 +418,20 @@ public long getSloadOperationGasCost() { @Override public long calculateStorageCost( - final Account account, final UInt256 key, final UInt256 newValue) { - return !newValue.isZero() && account.getStorageValue(key).isZero() + final UInt256 newValue, + final Supplier currentValue, + final Supplier originalValue) { + return !newValue.isZero() && currentValue.get().isZero() ? STORAGE_SET_GAS_COST : STORAGE_RESET_GAS_COST; } @Override public long calculateStorageRefundAmount( - final Account account, final UInt256 key, final UInt256 newValue) { - return newValue.isZero() && !account.getStorageValue(key).isZero() - ? STORAGE_RESET_REFUND_AMOUNT - : 0L; + final UInt256 newValue, + final Supplier currentValue, + final Supplier originalValue) { + return newValue.isZero() && !currentValue.get().isZero() ? STORAGE_RESET_REFUND_AMOUNT : 0L; } @Override 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 0d882248454..1e02288a42d 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 @@ -41,6 +41,7 @@ import java.util.List; +import com.google.common.base.Supplier; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.units.bigints.UInt256; @@ -348,22 +349,24 @@ long callOperationGasCost( /** * Returns the cost for an SSTORE operation. * - * @param account the account that storage will be changed in - * @param key the key the new value is to be stored under * @param newValue the new value to be stored + * @param currentValue the supplier of the current value + * @param originalValue the supplier of the original value * @return the gas cost for the SSTORE operation */ - long calculateStorageCost(Account account, UInt256 key, UInt256 newValue); + long calculateStorageCost( + UInt256 newValue, Supplier currentValue, Supplier originalValue); /** * Returns the refund amount for an SSTORE operation. * - * @param account the account that storage will be changed in - * @param key the key the new value is to be stored under * @param newValue the new value to be stored + * @param currentValue the supplier of the current value + * @param originalValue the supplier of the original value * @return the gas refund for the SSTORE operation */ - long calculateStorageRefundAmount(Account account, UInt256 key, UInt256 newValue); + long calculateStorageRefundAmount( + UInt256 newValue, Supplier currentValue, Supplier originalValue); /** * Returns the refund amount for deleting an account in a {@link SelfDestructOperation}. 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 ec5caadd57a..f2ecd11da8a 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 @@ -14,8 +14,7 @@ */ package org.hyperledger.besu.evm.gascalculator; -import org.hyperledger.besu.evm.account.Account; - +import com.google.common.base.Supplier; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.units.bigints.UInt256; @@ -56,15 +55,17 @@ public long transactionIntrinsicGasCost(final Bytes payload, final boolean isCon @Override // As per https://eips.ethereum.org/EIPS/eip-2200 public long calculateStorageCost( - final Account account, final UInt256 key, final UInt256 newValue) { + final UInt256 newValue, + final Supplier currentValue, + final Supplier originalValue) { - final UInt256 currentValue = account.getStorageValue(key); - if (currentValue.equals(newValue)) { + final UInt256 localCurrentValue = currentValue.get(); + if (localCurrentValue.equals(newValue)) { return SLOAD_GAS; } else { - final UInt256 originalValue = account.getOriginalStorageValue(key); - if (originalValue.equals(currentValue)) { - return originalValue.isZero() ? SSTORE_SET_GAS : SSTORE_RESET_GAS; + final UInt256 localOriginalValue = originalValue.get(); + if (localOriginalValue.equals(localCurrentValue)) { + return localOriginalValue.isZero() ? SSTORE_SET_GAS : SSTORE_RESET_GAS; } else { return SLOAD_GAS; } @@ -74,15 +75,17 @@ public long calculateStorageCost( @Override // As per https://eips.ethereum.org/EIPS/eip-2200 public long calculateStorageRefundAmount( - final Account account, final UInt256 key, final UInt256 newValue) { + final UInt256 newValue, + final Supplier currentValue, + final Supplier originalValue) { - final UInt256 currentValue = account.getStorageValue(key); - if (currentValue.equals(newValue)) { + final UInt256 localCurrentValue = currentValue.get(); + if (localCurrentValue.equals(newValue)) { return 0L; } else { - final UInt256 originalValue = account.getOriginalStorageValue(key); - if (originalValue.equals(currentValue)) { - if (originalValue.isZero()) { + final UInt256 localOriginalValue = originalValue.get(); + if (localOriginalValue.equals(localCurrentValue)) { + if (localOriginalValue.isZero()) { return 0L; } else if (newValue.isZero()) { return SSTORE_CLEARS_SCHEDULE; @@ -91,18 +94,18 @@ public long calculateStorageRefundAmount( } } else { long refund = 0L; - if (!originalValue.isZero()) { - if (currentValue.isZero()) { + if (!localOriginalValue.isZero()) { + if (localCurrentValue.isZero()) { refund = NEGATIVE_SSTORE_CLEARS_SCHEDULE; } else if (newValue.isZero()) { refund = SSTORE_CLEARS_SCHEDULE; } } - if (originalValue.equals(newValue)) { + if (localOriginalValue.equals(newValue)) { refund = refund - + (originalValue.isZero() + + (localOriginalValue.isZero() ? SSTORE_SET_GAS_LESS_SLOAD_GAS : SSTORE_RESET_GAS_LESS_SLOAD_GAS); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/LondonGasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/LondonGasCalculator.java index cb8c4d6ce00..5990d2b1e44 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/LondonGasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/LondonGasCalculator.java @@ -14,8 +14,7 @@ */ package org.hyperledger.besu.evm.gascalculator; -import org.hyperledger.besu.evm.account.Account; - +import com.google.common.base.Supplier; import org.apache.tuweni.units.bigints.UInt256; public class LondonGasCalculator extends BerlinGasCalculator { @@ -40,14 +39,17 @@ public long getSelfDestructRefundAmount() { @Override // As per https://eips.ethereum.org/EIPS/eip-3529 public long calculateStorageRefundAmount( - final Account account, final UInt256 key, final UInt256 newValue) { - final UInt256 currentValue = account.getStorageValue(key); - if (currentValue.equals(newValue)) { + final UInt256 newValue, + final Supplier currentValue, + final Supplier originalValue) { + + final UInt256 localCurrentValue = currentValue.get(); + if (localCurrentValue.equals(newValue)) { return 0L; } else { - final UInt256 originalValue = account.getOriginalStorageValue(key); - if (originalValue.equals(currentValue)) { - if (originalValue.isZero()) { + final UInt256 localOriginalValue = originalValue.get(); + if (localOriginalValue.equals(localCurrentValue)) { + if (localOriginalValue.isZero()) { return 0L; } else if (newValue.isZero()) { return SSTORE_CLEARS_SCHEDULE; @@ -56,18 +58,18 @@ public long calculateStorageRefundAmount( } } else { long refund = 0L; - if (!originalValue.isZero()) { - if (currentValue.isZero()) { + if (!localOriginalValue.isZero()) { + if (localCurrentValue.isZero()) { refund = NEGATIVE_SSTORE_CLEARS_SCHEDULE; } else if (newValue.isZero()) { refund = SSTORE_CLEARS_SCHEDULE; } } - if (originalValue.equals(newValue)) { + if (localOriginalValue.equals(newValue)) { refund = refund - + (originalValue.isZero() + + (localOriginalValue.isZero() ? SSTORE_SET_GAS_LESS_SLOAD_GAS : SSTORE_RESET_GAS_LESS_SLOAD_GAS); } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PetersburgGasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PetersburgGasCalculator.java index e78142873c1..987639f7125 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PetersburgGasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PetersburgGasCalculator.java @@ -14,8 +14,7 @@ */ package org.hyperledger.besu.evm.gascalculator; -import org.hyperledger.besu.evm.account.Account; - +import com.google.common.base.Supplier; import org.apache.tuweni.units.bigints.UInt256; /** @@ -34,25 +33,27 @@ public class PetersburgGasCalculator extends ConstantinopleGasCalculator { private static final long STORAGE_RESET_REFUND_AMOUNT = 15_000L; /** - * Same as {#link {@link FrontierGasCalculator#calculateStorageCost(Account, UInt256, UInt256)} + * Same as {#link {@link FrontierGasCalculator#calculateStorageCost(UInt256, Supplier, Supplier)} */ @Override public long calculateStorageCost( - final Account account, final UInt256 key, final UInt256 newValue) { - return !newValue.isZero() && account.getStorageValue(key).isZero() + final UInt256 newValue, + final Supplier currentValue, + final Supplier originalValue) { + return !newValue.isZero() && currentValue.get().isZero() ? STORAGE_SET_GAS_COST : STORAGE_RESET_GAS_COST; } /** - * Same as {#link {@link FrontierGasCalculator#calculateStorageRefundAmount(Account, UInt256, - * UInt256)} + * Same as {#link {@link FrontierGasCalculator#calculateStorageRefundAmount(UInt256, Supplier, + * Supplier)} */ @Override public long calculateStorageRefundAmount( - final Account account, final UInt256 key, final UInt256 newValue) { - return newValue.isZero() && !account.getStorageValue(key).isZero() - ? STORAGE_RESET_REFUND_AMOUNT - : 0L; + final UInt256 newValue, + final Supplier currentValue, + final Supplier originalValue) { + return newValue.isZero() && !currentValue.get().isZero() ? STORAGE_RESET_REFUND_AMOUNT : 0L; } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/operation/SStoreOperation.java b/evm/src/main/java/org/hyperledger/besu/evm/operation/SStoreOperation.java index 31c89b7807f..e676a789cf9 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/operation/SStoreOperation.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/operation/SStoreOperation.java @@ -21,6 +21,8 @@ import org.hyperledger.besu.evm.frame.MessageFrame; import org.hyperledger.besu.evm.gascalculator.GasCalculator; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; import org.apache.tuweni.units.bigints.UInt256; public class SStoreOperation extends AbstractOperation { @@ -46,7 +48,7 @@ public long getMinimumGasRemaining() { public OperationResult execute(final MessageFrame frame, final EVM evm) { final UInt256 key = UInt256.fromBytes(frame.popStackItem()); - final UInt256 value = UInt256.fromBytes(frame.popStackItem()); + final UInt256 newValue = UInt256.fromBytes(frame.popStackItem()); final MutableAccount account = frame.getWorldUpdater().getAccount(frame.getRecipientAddress()).getMutable(); @@ -56,8 +58,13 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { final Address address = account.getAddress(); final boolean slotIsWarm = frame.warmUpStorage(address, key); + final Supplier currentValueSupplier = + Suppliers.memoize(() -> account.getStorageValue(key)); + final Supplier originalValueSupplier = + Suppliers.memoize(() -> account.getOriginalStorageValue(key)); + final long cost = - gasCalculator().calculateStorageCost(account, key, value) + gasCalculator().calculateStorageCost(newValue, currentValueSupplier, originalValueSupplier) + (slotIsWarm ? 0L : gasCalculator().getColdSloadCost()); final long remainingGas = frame.getRemainingGas(); @@ -70,10 +77,12 @@ public OperationResult execute(final MessageFrame frame, final EVM evm) { } // Increment the refund counter. - frame.incrementGasRefund(gasCalculator().calculateStorageRefundAmount(account, key, value)); + frame.incrementGasRefund( + gasCalculator() + .calculateStorageRefundAmount(newValue, currentValueSupplier, originalValueSupplier)); - account.setStorageValue(key, value); - frame.storageWasUpdated(key, value); + account.setStorageValue(key, newValue); + frame.storageWasUpdated(key, newValue); return new OperationResult(cost, null); } }