Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simulation: Add state to StateOverride (eth_call, eth_simulateV1). #8166

Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -35,18 +37,21 @@ public class StateOverride {
private final Optional<Wei> balance;
private final Optional<Long> nonce;
private final Optional<String> code;
private final Optional<Map<String, String>> state;
private final Optional<Map<String, String>> stateDiff;
private final Optional<Address> movePrecompileToAddress;

private StateOverride(
final Optional<Wei> balance,
final Optional<Long> nonce,
final Optional<String> code,
final Optional<Map<String, String>> state,
final Optional<Map<String, String>> stateDiff,
final Optional<Address> movePrecompileToAddress) {
this.balance = balance;
this.nonce = nonce;
this.code = code;
this.state = state;
this.stateDiff = stateDiff;
this.movePrecompileToAddress = movePrecompileToAddress;
}
Expand Down Expand Up @@ -83,6 +88,15 @@ public Optional<String> getCode() {
*
* @return the state override map if present
*/
public Optional<Map<String, String>> getState() {
return state;
}

/**
* Gets the state diff override map
*
* @return the state diff override map if present
*/
public Optional<Map<String, String>> getStateDiff() {
return stateDiff;
}
Expand All @@ -102,6 +116,7 @@ public static class Builder {
private Optional<Wei> balance = Optional.empty();
private Optional<Long> nonce = Optional.empty();
private Optional<String> code = Optional.empty();
private Optional<Map<String, String>> state = Optional.empty();
private Optional<Map<String, String>> stateDiff = Optional.empty();
private Optional<Address> movePrecompileToAddress = Optional.empty();

Expand Down Expand Up @@ -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<String, String> state) {
this.state = Optional.ofNullable(state);
return this;
}

/**
* Sets the state diff override
*
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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
Expand All @@ -217,6 +245,8 @@ public String toString() {
+ nonce
+ ", code="
+ code
+ ", state="
+ state
+ ", stateDiff="
+ stateDiff
+ ", movePrecompileToAddress="
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<StateOverrideMap> 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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -459,13 +459,21 @@ public Optional<TransactionSimulatorResult> 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(
d -> {
account.clearStorage();
d.forEach(
(key, value) ->
account.setStorageValue(
UInt256.fromHexString(key), UInt256.fromHexString(value)));
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe extract lines 472-475 to a method and call it for state and stateDiff - something like setStorageValues or applyStorageOverrides ...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or maybe 470-475 and pass a boolean for whether to clear first

override
.getStateDiff()
.ifPresent(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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)));
}

Expand All @@ -144,7 +144,25 @@ 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)));
}

@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)));
Expand Down
Loading
Loading