From 096ab2a0f3c4ded8adc5c9e64e59904e374ac257 Mon Sep 17 00:00:00 2001 From: Gabriel-Trintinalia Date: Fri, 24 Jan 2025 15:42:25 +1100 Subject: [PATCH 1/2] Add state to state overrides Signed-off-by: Gabriel-Trintinalia --- .../besu/datatypes/StateOverride.java | 34 ++++++++++++++- .../jsonrpc/internal/methods/EthCallTest.java | 21 ++++++++++ .../ethereum/transaction/BlockSimulator.java | 15 +------ .../transaction/TransactionSimulator.java | 10 +++++ .../transaction/TransactionSimulatorTest.java | 18 ++++++++ .../util/StateOverrideParameterTest.java | 41 +++++++++++++++---- 6 files changed, 114 insertions(+), 25 deletions(-) diff --git a/datatypes/src/main/java/org/hyperledger/besu/datatypes/StateOverride.java b/datatypes/src/main/java/org/hyperledger/besu/datatypes/StateOverride.java index 8fdd66425f7..0f85c40b20e 100644 --- a/datatypes/src/main/java/org/hyperledger/besu/datatypes/StateOverride.java +++ b/datatypes/src/main/java/org/hyperledger/besu/datatypes/StateOverride.java @@ -14,6 +14,8 @@ */ package org.hyperledger.besu.datatypes; +import static com.google.common.base.Preconditions.checkState; + import org.hyperledger.besu.datatypes.parameters.UnsignedLongParameter; import java.util.Map; @@ -35,6 +37,7 @@ public class StateOverride { private final Optional balance; private final Optional nonce; private final Optional code; + private final Optional> state; private final Optional> stateDiff; private final Optional
movePrecompileToAddress; @@ -42,11 +45,13 @@ private StateOverride( final Optional balance, final Optional nonce, final Optional code, + final Optional> state, final Optional> stateDiff, final Optional
movePrecompileToAddress) { this.balance = balance; this.nonce = nonce; this.code = code; + this.state = state; this.stateDiff = stateDiff; this.movePrecompileToAddress = movePrecompileToAddress; } @@ -83,6 +88,15 @@ public Optional getCode() { * * @return the state override map if present */ + public Optional> getState() { + return state; + } + + /** + * Gets the state diff override map + * + * @return the state diff override map if present + */ public Optional> getStateDiff() { return stateDiff; } @@ -102,6 +116,7 @@ public static class Builder { private Optional balance = Optional.empty(); private Optional nonce = Optional.empty(); private Optional code = Optional.empty(); + private Optional> state = Optional.empty(); private Optional> stateDiff = Optional.empty(); private Optional
movePrecompileToAddress = Optional.empty(); @@ -141,6 +156,17 @@ public Builder withCode(final String code) { return this; } + /** + * Sets the state override + * + * @param state the map of state overrides + * @return the builder + */ + public Builder withState(final Map state) { + this.state = Optional.ofNullable(state); + return this; + } + /** * Sets the state diff override * @@ -169,7 +195,8 @@ public Builder withMovePrecompileToAddress(final Address newPrecompileAddress) { * @return account override */ public StateOverride build() { - return new StateOverride(balance, nonce, code, stateDiff, movePrecompileToAddress); + checkState(state.isEmpty() || stateDiff.isEmpty(), "Cannot set both state and stateDiff"); + return new StateOverride(balance, nonce, code, state, stateDiff, movePrecompileToAddress); } } @@ -200,12 +227,13 @@ public boolean equals(final Object o) { return balance.equals(stateOverride.balance) && nonce.equals(stateOverride.nonce) && code.equals(stateOverride.code) + && state.equals(stateOverride.state) && stateDiff.equals(stateOverride.stateDiff); } @Override public int hashCode() { - return Objects.hash(balance, nonce, code, stateDiff); + return Objects.hash(balance, nonce, code, state, stateDiff); } @Override @@ -217,6 +245,8 @@ public String toString() { + nonce + ", code=" + code + + ", state=" + + state + ", stateDiff=" + stateDiff + ", movePrecompileToAddress=" diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCallTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCallTest.java index 4546fdb9c8e..f3f7f0b3b9d 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCallTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCallTest.java @@ -124,6 +124,27 @@ public void someStateOverrides() { assertThat(overrideMap).containsValue(override); } + @Test + public void stateOverridesWithState() { + StateOverrideMap expectedOverrides = new StateOverrideMap(); + StateOverride override = + new StateOverride.Builder().withState(Map.of("0x1234", "0x5678")).build(); + final Address address = Address.fromHexString("0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3"); + expectedOverrides.put(address, override); + + final JsonRpcRequestContext request = + ethCallRequestWithStateOverrides(callParameter(), "latest", expectedOverrides); + + Optional maybeOverrideMap = method.getAddressStateOverrideMap(request); + assertThat(maybeOverrideMap.isPresent()).isTrue(); + StateOverrideMap overrideMap = maybeOverrideMap.get(); + assertThat(overrideMap.keySet()).hasSize(1); + assertThat(overrideMap.values()).hasSize(1); + + assertThat(overrideMap).containsKey(address); + assertThat(overrideMap).containsValue(override); + } + @Test public void fullStateOverrides() { StateOverrideMap suppliedOverrides = new StateOverrideMap(); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/BlockSimulator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/BlockSimulator.java index 43bf25915ad..d4118fbb556 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/BlockSimulator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/BlockSimulator.java @@ -52,7 +52,6 @@ import com.google.common.annotations.VisibleForTesting; import org.apache.tuweni.bytes.Bytes; -import org.apache.tuweni.units.bigints.UInt256; /** * Simulates the execution of a block, processing transactions and applying state overrides. This @@ -246,19 +245,7 @@ protected void applyStateOverrides( for (Address accountToOverride : stateOverrideMap.keySet()) { final StateOverride override = stateOverrideMap.get(accountToOverride); MutableAccount account = updater.getOrCreate(accountToOverride); - override.getNonce().ifPresent(account::setNonce); - if (override.getBalance().isPresent()) { - account.setBalance(override.getBalance().get()); - } - override.getCode().ifPresent(n -> account.setCode(Bytes.fromHexString(n))); - override - .getStateDiff() - .ifPresent( - d -> - d.forEach( - (key, value) -> - account.setStorageValue( - UInt256.fromHexString(key), UInt256.fromHexString(value)))); + transactionSimulator.applyOverrides(account, override); } updater.commit(); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java index 0baa3a0b1c3..dbba22c28dd 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java @@ -466,6 +466,16 @@ protected void applyOverrides(final MutableAccount account, final StateOverride account.setBalance(override.getBalance().get()); } override.getCode().ifPresent(n -> account.setCode(Bytes.fromHexString(n))); + override + .getState() + .ifPresent( + d -> { + account.clearStorage(); + d.forEach( + (key, value) -> + account.setStorageValue( + UInt256.fromHexString(key), UInt256.fromHexString(value))); + }); override .getStateDiff() .ifPresent( diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java index 43d44911b13..57449d8f568 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java @@ -150,6 +150,24 @@ public void testOverrides_whenStateDiffOverrides_stateIsUpdated() { eq(UInt256.fromHexString(storageKey)), eq(UInt256.fromHexString(storageValue))); } + @Test + public void testOverrides_whenStateOverrides_stateIsUpdated() { + MutableAccount mutableAccount = mock(MutableAccount.class); + when(mutableAccount.getAddress()).thenReturn(DEFAULT_FROM); + final String storageKey = "0x01a2"; + final String storageValue = "0x00ff"; + StateOverride.Builder builder = + new StateOverride.Builder().withState(Map.of(storageKey, storageValue)); + StateOverride override = builder.build(); + transactionSimulator.applyOverrides(mutableAccount, override); + + verify(mutableAccount).clearStorage(); + + verify(mutableAccount) + .setStorageValue( + eq(UInt256.fromHexString(storageKey)), eq(UInt256.fromHexString(storageValue))); + } + @Test public void shouldReturnEmptyWhenBlockDoesNotExist() { when(blockchain.getBlockHeader(eq(1L))).thenReturn(Optional.empty()); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/util/StateOverrideParameterTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/util/StateOverrideParameterTest.java index 1f095a1b1bc..6fb452d9afe 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/util/StateOverrideParameterTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/util/StateOverrideParameterTest.java @@ -16,6 +16,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import org.hyperledger.besu.datatypes.Address; @@ -25,6 +26,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext; +import java.util.Map; import java.util.Optional; import com.fasterxml.jackson.databind.ObjectMapper; @@ -63,6 +65,7 @@ public void jsonDeserializesCorrectly() throws Exception { assertThat(stateOverride.getNonce().get()).isEqualTo(158); assertThat(stateOverride.getBalance()).isEqualTo(Optional.of(Wei.of(1))); + assertFalse(stateOverride.getState().isPresent()); assertFalse(stateOverride.getStateDiff().isPresent()); } @@ -91,6 +94,7 @@ public void jsonWithCodeDeserializesCorrectly() throws Exception { assertFalse(stateOverride.getNonce().isPresent()); assertThat(stateOverride.getBalance()).isEqualTo(Optional.of(Wei.of(1))); assertThat(stateOverride.getCode()).isEqualTo(Optional.of(CODE_STRING)); + assertFalse(stateOverride.getState().isPresent()); assertFalse(stateOverride.getStateDiff().isPresent()); } @@ -118,6 +122,7 @@ public void jsonWithHexNonceDeserializesCorrectly() throws Exception { assertThat(stateOverride.getBalance()).isEqualTo(Optional.of(Wei.of(1))); assertThat(stateOverride.getNonce().get()).isEqualTo(158); // 0x9e + assertFalse(stateOverride.getState().isPresent()); assertFalse(stateOverride.getStateDiff().isPresent()); } @@ -133,7 +138,7 @@ public void jsonWithStorageOverridesDeserializesCorrectly() throws Exception { + "{" + "\"balance\": \"0x01\"," + "\"nonce\": \"0x9E\"," - + "\"stateDiff\": {" + + "\"state\": {" + "\"" + STORAGE_KEY + "\": \"" @@ -150,8 +155,9 @@ public void jsonWithStorageOverridesDeserializesCorrectly() throws Exception { final StateOverride stateOverride = stateOverrideParam.get(Address.fromHexString(ADDRESS_HEX1)); assertThat(stateOverride.getNonce().get()).isEqualTo(158); - assertTrue(stateOverride.getStateDiff().isPresent()); - assertThat(stateOverride.getStateDiff().get().get(STORAGE_KEY)).isEqualTo(STORAGE_VALUE); + assertTrue(stateOverride.getState().isPresent()); + assertThat(stateOverride.getState().get().get(STORAGE_KEY)).isEqualTo(STORAGE_VALUE); + assertFalse(stateOverride.getStateDiff().isPresent()); } @Test @@ -166,7 +172,7 @@ public void jsonWithMultipleStateOverridesDeserializesCorrectly() throws Excepti + "{" + "\"balance\": \"0x01\"," + "\"nonce\": \"0x9E\"," - + "\"stateDiff\": {" + + "\"state\": {" + "\"" + STORAGE_KEY + "\": \"" @@ -179,7 +185,7 @@ public void jsonWithMultipleStateOverridesDeserializesCorrectly() throws Excepti + "{" + "\"balance\": \"0xFF\"," + "\"nonce\": \"0x9D\"," - + "\"stateDiff\": {" + + "\"state\": {" + "\"" + STORAGE_KEY + "\": \"" @@ -197,18 +203,35 @@ public void jsonWithMultipleStateOverridesDeserializesCorrectly() throws Excepti stateOverrideParam.get(Address.fromHexString(ADDRESS_HEX1)); assertThat(stateOverride1.getNonce().get()).isEqualTo(158); assertThat(stateOverride1.getBalance()).isEqualTo(Optional.of(Wei.fromHexString("0x01"))); - assertTrue(stateOverride1.getStateDiff().isPresent()); - assertThat(stateOverride1.getStateDiff().get().get(STORAGE_KEY)).isEqualTo(STORAGE_VALUE); + assertTrue(stateOverride1.getState().isPresent()); + assertThat(stateOverride1.getState().get().get(STORAGE_KEY)).isEqualTo(STORAGE_VALUE); + assertFalse(stateOverride1.getStateDiff().isPresent()); final StateOverride stateOverride2 = stateOverrideParam.get(Address.fromHexString(ADDRESS_HEX2)); assertThat(stateOverride2.getNonce().get()).isEqualTo(157); assertThat(stateOverride2.getBalance()).isEqualTo(Optional.of(Wei.fromHexString("0xFF"))); - assertTrue(stateOverride2.getStateDiff().isPresent()); - assertThat(stateOverride2.getStateDiff().get().get(STORAGE_KEY)).isEqualTo(STORAGE_VALUE); + assertTrue(stateOverride2.getState().isPresent()); + assertThat(stateOverride2.getState().get().get(STORAGE_KEY)).isEqualTo(STORAGE_VALUE); + assertFalse(stateOverride2.getStateDiff().isPresent()); } private JsonRpcRequest readJsonAsJsonRpcRequest(final String json) throws java.io.IOException { return new ObjectMapper().readValue(json, JsonRpcRequest.class); } + + @Test + public void shouldThrowExceptionWhenStateAndStateDiffAreBothPresent() { + Exception exception = + assertThrows( + IllegalStateException.class, + () -> + new StateOverride.Builder() + .withState(Map.of("0x1234", "0x5678")) + .withStateDiff(Map.of("0x1234", "0x5678")) + .build()); + + final String expectedMessage = "Cannot set both state and stateDiff"; + assertThat(exception.getMessage()).isEqualTo(expectedMessage); + } } From 965bdfcee09d4221bcf9874abf3a6d9830414de5 Mon Sep 17 00:00:00 2001 From: Gabriel-Trintinalia Date: Fri, 24 Jan 2025 17:03:08 +1100 Subject: [PATCH 2/2] Fix test Signed-off-by: Gabriel-Trintinalia --- .../ethereum/transaction/BlockSimulator.java | 2 +- .../transaction/TransactionSimulator.java | 8 +++---- .../transaction/BlockSimulatorTest.java | 24 +++++++++---------- .../transaction/TransactionSimulatorTest.java | 8 +++---- 4 files changed, 20 insertions(+), 22 deletions(-) diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/BlockSimulator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/BlockSimulator.java index d4118fbb556..2bee89fd782 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/BlockSimulator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/BlockSimulator.java @@ -245,7 +245,7 @@ protected void applyStateOverrides( for (Address accountToOverride : stateOverrideMap.keySet()) { final StateOverride override = stateOverrideMap.get(accountToOverride); MutableAccount account = updater.getOrCreate(accountToOverride); - transactionSimulator.applyOverrides(account, override); + TransactionSimulator.applyOverrides(account, override); } updater.commit(); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java index dbba22c28dd..aab9d1809ca 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java @@ -459,13 +459,11 @@ public Optional processWithWorldUpdater( } @VisibleForTesting - protected void applyOverrides(final MutableAccount account, final StateOverride override) { + protected static void applyOverrides(final MutableAccount account, final StateOverride override) { LOG.debug("applying overrides to state for account {}", account.getAddress()); override.getNonce().ifPresent(account::setNonce); - if (override.getBalance().isPresent()) { - account.setBalance(override.getBalance().get()); - } - override.getCode().ifPresent(n -> account.setCode(Bytes.fromHexString(n))); + override.getBalance().ifPresent(account::setBalance); + override.getCode().ifPresent(code -> account.setCode(Bytes.fromHexString(code))); override .getState() .ifPresent( diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/BlockSimulatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/BlockSimulatorTest.java index 4a615aad006..c55298bff49 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/BlockSimulatorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/BlockSimulatorTest.java @@ -30,6 +30,7 @@ import org.hyperledger.besu.datatypes.StateOverride; import org.hyperledger.besu.datatypes.StateOverrideMap; import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.datatypes.parameters.UnsignedLongParameter; import org.hyperledger.besu.ethereum.GasLimitCalculator; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder; @@ -49,11 +50,9 @@ import java.math.BigInteger; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Set; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.units.bigints.UInt256; @@ -167,24 +166,25 @@ public void shouldStopWhenTransactionSimulationIsEmpty() { @Test public void shouldApplyStateOverridesCorrectly() { - StateOverrideMap stateOverrideMap = mock(StateOverrideMap.class); + StateOverrideMap stateOverrideMap = new StateOverrideMap(); Address address = mock(Address.class); - StateOverride stateOverride = mock(StateOverride.class); - MutableAccount mutableAccount = mock(MutableAccount.class); + StateOverride stateOverride = + new StateOverride.Builder() + .withBalance(Wei.of(456L)) + .withNonce(new UnsignedLongParameter(123L)) + .withCode("") + .withStateDiff(Map.of("0x0", "0x1")) + .build(); - when(stateOverrideMap.keySet()).thenReturn(Set.of(address)); - when(stateOverrideMap.get(address)).thenReturn(stateOverride); + stateOverrideMap.put(address, stateOverride); WorldUpdater worldUpdater = mock(WorldUpdater.class); when(mutableWorldState.updater()).thenReturn(worldUpdater); + MutableAccount mutableAccount = mock(MutableAccount.class); + when(mutableAccount.getAddress()).thenReturn(address); when(worldUpdater.getOrCreate(address)).thenReturn(mutableAccount); - when(stateOverride.getNonce()).thenReturn(Optional.of(123L)); - when(stateOverride.getBalance()).thenReturn(Optional.of(Wei.of(456L))); - when(stateOverride.getCode()).thenReturn(Optional.of("")); - when(stateOverride.getStateDiff()).thenReturn(Optional.of(new HashMap<>(Map.of("0x0", "0x1")))); - blockSimulator.applyStateOverrides(stateOverrideMap, mutableWorldState); verify(mutableAccount).setNonce(anyLong()); diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java index 57449d8f568..60a86e18a1a 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorTest.java @@ -120,7 +120,7 @@ public void testOverrides_whenNoOverrides_noUpdates() { when(mutableAccount.getAddress()).thenReturn(DEFAULT_FROM); // called from logging StateOverride.Builder builder = new StateOverride.Builder(); StateOverride override = builder.build(); - transactionSimulator.applyOverrides(mutableAccount, override); + TransactionSimulator.applyOverrides(mutableAccount, override); verify(mutableAccount).getAddress(); verifyNoMoreInteractions(mutableAccount); } @@ -131,7 +131,7 @@ public void testOverrides_whenBalanceOverrides_balanceIsUpdated() { when(mutableAccount.getAddress()).thenReturn(DEFAULT_FROM); StateOverride.Builder builder = new StateOverride.Builder().withBalance(Wei.of(99)); StateOverride override = builder.build(); - transactionSimulator.applyOverrides(mutableAccount, override); + TransactionSimulator.applyOverrides(mutableAccount, override); verify(mutableAccount).setBalance(eq(Wei.of(99))); } @@ -144,7 +144,7 @@ public void testOverrides_whenStateDiffOverrides_stateIsUpdated() { StateOverride.Builder builder = new StateOverride.Builder().withStateDiff(Map.of(storageKey, storageValue)); StateOverride override = builder.build(); - transactionSimulator.applyOverrides(mutableAccount, override); + TransactionSimulator.applyOverrides(mutableAccount, override); verify(mutableAccount) .setStorageValue( eq(UInt256.fromHexString(storageKey)), eq(UInt256.fromHexString(storageValue))); @@ -159,7 +159,7 @@ public void testOverrides_whenStateOverrides_stateIsUpdated() { StateOverride.Builder builder = new StateOverride.Builder().withState(Map.of(storageKey, storageValue)); StateOverride override = builder.build(); - transactionSimulator.applyOverrides(mutableAccount, override); + TransactionSimulator.applyOverrides(mutableAccount, override); verify(mutableAccount).clearStorage();