Add support for movePrecompileToAddress in State Overrides (eth_call) (#8115)

Signed-off-by: Gabriel-Trintinalia <gabriel.trintinalia@consensys.net>
This commit is contained in:
Gabriel-Trintinalia
2025-01-15 12:27:57 +08:00
committed by GitHub
parent 9c12ed19df
commit 5cc309a26e
17 changed files with 615 additions and 137 deletions

View File

@@ -24,6 +24,7 @@
- Allow gasPrice (legacy) and 1559 gasPrice params to be specified simultaneously for `eth_call`, `eth_createAccessList`, and `eth_estimateGas` [#8059](https://github.com/hyperledger/besu/pull/8059)
- Improve debug_traceBlock calls performance and reduce output size [#8076](https://github.com/hyperledger/besu/pull/8076)
- Add support for EIP-7702 transaction in the txpool [#8018](https://github.com/hyperledger/besu/pull/8018) [#7984](https://github.com/hyperledger/besu/pull/7984)
- Add support for `movePrecompileToAddress` in `StateOverrides` (`eth_call`)[8115](https://github.com/hyperledger/besu/pull/8115)
### Bug fixes
- Fix serialization of state overrides when `movePrecompileToAddress` is present [#8204](https://github.com/hyperledger/besu/pull/8024)

View File

@@ -36,16 +36,19 @@ public class StateOverride {
private final Optional<Long> nonce;
private final Optional<String> code;
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>> stateDiff) {
final Optional<Map<String, String>> stateDiff,
final Optional<Address> movePrecompileToAddress) {
this.balance = balance;
this.nonce = nonce;
this.code = code;
this.stateDiff = stateDiff;
this.movePrecompileToAddress = movePrecompileToAddress;
}
/**
@@ -84,6 +87,15 @@ public class StateOverride {
return stateDiff;
}
/**
* Gets the new address for the pre-compiled contract
*
* @return the new address for the pre-compiled contract if present
*/
public Optional<Address> getMovePrecompileToAddress() {
return movePrecompileToAddress;
}
/** Builder class for Account overrides */
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Builder {
@@ -91,6 +103,7 @@ public class StateOverride {
private Optional<Long> nonce = Optional.empty();
private Optional<String> code = Optional.empty();
private Optional<Map<String, String>> stateDiff = Optional.empty();
private Optional<Address> movePrecompileToAddress = Optional.empty();
/** Default constructor. */
public Builder() {}
@@ -139,13 +152,24 @@ public class StateOverride {
return this;
}
/**
* Sets the new address for the pre-compiled contract
*
* @param newPrecompileAddress the new address for the pre-compile contract
* @return the builder
*/
public Builder withMovePrecompileToAddress(final Address newPrecompileAddress) {
this.movePrecompileToAddress = Optional.ofNullable(newPrecompileAddress);
return this;
}
/**
* build the account override from the builder
*
* @return account override
*/
public StateOverride build() {
return new StateOverride(balance, nonce, code, stateDiff);
return new StateOverride(balance, nonce, code, stateDiff, movePrecompileToAddress);
}
}
@@ -195,6 +219,8 @@ public class StateOverride {
+ code
+ ", stateDiff="
+ stateDiff
+ ", movePrecompileToAddress="
+ movePrecompileToAddress
+ '}';
}
}

View File

@@ -55,6 +55,7 @@ import org.hyperledger.besu.ethereum.transaction.PreCloseStateHandler;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulatorResult;
import java.util.Map;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
@@ -123,6 +124,33 @@ public class EthCallTest {
assertThat(overrideMap).containsValue(override);
}
@Test
public void fullStateOverrides() {
StateOverrideMap suppliedOverrides = new StateOverrideMap();
StateOverride override =
new StateOverride.Builder()
.withNonce(new UnsignedLongParameter("0x9e"))
.withBalance(Wei.of(100))
.withCode("0x1234")
.withStateDiff(Map.of("0x1234", "0x5678"))
.withMovePrecompileToAddress(Address.fromHexString("0x1234"))
.build();
final Address address = Address.fromHexString("0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3");
suppliedOverrides.put(address, override);
final JsonRpcRequestContext request =
ethCallRequestWithStateOverrides(callParameter(), "latest", suppliedOverrides);
Optional<StateOverrideMap> maybeOverrideMap = method.getAddressStateOverrideMap(request);
assertThat(maybeOverrideMap.isPresent()).isTrue();
StateOverrideMap actualOverrideMap = maybeOverrideMap.get();
assertThat(actualOverrideMap.keySet()).hasSize(1);
assertThat(actualOverrideMap.values()).hasSize(1);
assertThat(actualOverrideMap).containsKey(address);
assertThat(actualOverrideMap).containsValue(override);
}
@Test
public void shouldReturnInternalErrorWhenProcessorReturnsEmpty() {
final JsonRpcRequestContext request = ethCallRequest(callParameter(), "latest");

View File

@@ -5,21 +5,21 @@
"method": "eth_call",
"params": [
{
"to": "0x6295ee1b4f6dd65047762f924ecd367c17eabf8f",
"from": "a94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"data": "0x12a7b914"
"comment": "Call to ECREC Precompiled on a different address, expect the original behaviour of ECREC precompile",
"from": "0xc100000000000000000000000000000000000000",
"to": "0x0000000000000000000000000000000000123456",
"input": "0x82f3df49d3645876de6313df2bbe9fbce593f21341a7b03acdb9423bc171fcc9000000000000000000000000000000000000000000000000000000000000001cba13918f50da910f2d55a7ea64cf716ba31dad91856f45908dde900530377d8a112d60f36900d18eb8f9d3b4f85a697b545085614509e3520e4b762e35d0d6bd"
},
"latest",
{
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
"0xc100000000000000000000000000000000000000": {
"balance": "0xde0b6b3a7640000",
"nonce": 88
},
"0xb9741079a300Cb3B8f324CdDB847c0d1d273a05E": {
"stateDiff": {
"0x1cf7945003fc5b59d2f6736f0704557aa805c4f2844084ccd1173b8d56946962": "0x000000000000000000000000000000000000000000000000000000110ed03bf7"
},
"movePrecompileToAddress":null
"0x0000000000000000000000000000000000000001": {
"comment": "Move ECREC Precompiled to address",
"code": "0x60003560010160005260206000f3",
"movePrecompileToAddress": "0x0000000000000000000000000000000000123456"
}
}
]
@@ -27,7 +27,8 @@
"response": {
"jsonrpc": "2.0",
"id": 3,
"result": "0x0000000000000000000000000000000000000000000000000000000000000001"
"comment": "The original ECREC precompile behaviour is expected, not the overridden one",
"result": "0x000000000000000000000000c6e93f4c1920eaeaa1e699f76a7a8c18e3056074"
},
"statusCode": 200
}

View File

@@ -0,0 +1,33 @@
{
"request": {
"id": 3,
"jsonrpc": "2.0",
"method": "eth_call",
"params": [
{
"to": "0x6295ee1b4f6dd65047762f924ecd367c17eabf8f",
"from": "a94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"data": "0x12a7b914"
},
"latest",
{
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
"balance": "0xde0b6b3a7640000",
"nonce": 88
},
"0xb9741079a300Cb3B8f324CdDB847c0d1d273a05E": {
"stateDiff": {
"0x1cf7945003fc5b59d2f6736f0704557aa805c4f2844084ccd1173b8d56946962": "0x000000000000000000000000000000000000000000000000000000110ed03bf7"
},
"movePrecompileToAddress":null
}
}
]
},
"response": {
"jsonrpc": "2.0",
"id": 3,
"result": "0x0000000000000000000000000000000000000000000000000000000000000001"
},
"statusCode": 200
}

View File

@@ -0,0 +1,32 @@
{
"request": {
"id": 3,
"jsonrpc": "2.0",
"method": "eth_call",
"params": [
{
"from": "0xc100000000000000000000000000000000000000",
"comment": "Call to precompile ECREC (0x01), but code was modified to add 1 to input",
"to": "0x0000000000000000000000000000000000000001",
"input": "0x0000000000000000000000000000000000000000000000000000000000000001"
},
"latest",
{
"0xc100000000000000000000000000000000000000": {
"balance": "0xde0b6b3a7640000"
},
"0x0000000000000000000000000000000000000001": {
"comment": "The code below adds one to input",
"code": "0x60003560010160005260206000f3",
"movePrecompileToAddress": "0x0000000000000000000000000000000000123456"
}
}
]
},
"response": {
"jsonrpc": "2.0",
"id": 3,
"result": "0x0000000000000000000000000000000000000000000000000000000000000002"
},
"statusCode": 200
}

View File

@@ -175,16 +175,17 @@ public class ClassicProtocolSpecs {
transactionValidatorFactory,
contractCreationProcessor,
messageCallProcessor) ->
new MainnetTransactionProcessor(
gasCalculator,
transactionValidatorFactory,
contractCreationProcessor,
messageCallProcessor,
true,
false,
evmConfiguration.evmStackSize(),
feeMarket,
CoinbaseFeePriceCalculator.frontier()))
MainnetTransactionProcessor.builder()
.gasCalculator(gasCalculator)
.transactionValidatorFactory(transactionValidatorFactory)
.contractCreationProcessor(contractCreationProcessor)
.messageCallProcessor(messageCallProcessor)
.clearEmptyAccounts(true)
.warmCoinbase(false)
.maxStackSize(evmConfiguration.evmStackSize())
.feeMarket(feeMarket)
.coinbaseFeePriceCalculator(CoinbaseFeePriceCalculator.frontier())
.build())
.name("Atlantis");
}
@@ -357,16 +358,17 @@ public class ClassicProtocolSpecs {
transactionValidatorFactory,
contractCreationProcessor,
messageCallProcessor) ->
new MainnetTransactionProcessor(
gasCalculator,
transactionValidatorFactory,
contractCreationProcessor,
messageCallProcessor,
true,
true,
evmConfiguration.evmStackSize(),
feeMarket,
CoinbaseFeePriceCalculator.frontier()))
MainnetTransactionProcessor.builder()
.gasCalculator(gasCalculator)
.transactionValidatorFactory(transactionValidatorFactory)
.contractCreationProcessor(contractCreationProcessor)
.messageCallProcessor(messageCallProcessor)
.clearEmptyAccounts(true)
.warmCoinbase(true)
.maxStackSize(evmConfiguration.evmStackSize())
.feeMarket(feeMarket)
.coinbaseFeePriceCalculator(CoinbaseFeePriceCalculator.frontier())
.build())
.name("Spiral");
}
}

View File

@@ -136,16 +136,17 @@ public abstract class MainnetProtocolSpecs {
transactionValidatorFactory,
contractCreationProcessor,
messageCallProcessor) ->
new MainnetTransactionProcessor(
gasCalculator,
transactionValidatorFactory,
contractCreationProcessor,
messageCallProcessor,
false,
false,
evmConfiguration.evmStackSize(),
FeeMarket.legacy(),
CoinbaseFeePriceCalculator.frontier()))
MainnetTransactionProcessor.builder()
.gasCalculator(gasCalculator)
.transactionValidatorFactory(transactionValidatorFactory)
.contractCreationProcessor(contractCreationProcessor)
.messageCallProcessor(messageCallProcessor)
.clearEmptyAccounts(false)
.warmCoinbase(false)
.maxStackSize(evmConfiguration.evmStackSize())
.feeMarket(FeeMarket.legacy())
.coinbaseFeePriceCalculator(CoinbaseFeePriceCalculator.frontier())
.build())
.privateTransactionProcessorBuilder(
(transactionValidatorFactory,
contractCreationProcessor,
@@ -297,16 +298,17 @@ public abstract class MainnetProtocolSpecs {
transactionValidator,
contractCreationProcessor,
messageCallProcessor) ->
new MainnetTransactionProcessor(
gasCalculator,
transactionValidator,
contractCreationProcessor,
messageCallProcessor,
true,
false,
evmConfiguration.evmStackSize(),
feeMarket,
CoinbaseFeePriceCalculator.frontier()))
MainnetTransactionProcessor.builder()
.gasCalculator(gasCalculator)
.transactionValidatorFactory(transactionValidator)
.contractCreationProcessor(contractCreationProcessor)
.messageCallProcessor(messageCallProcessor)
.clearEmptyAccounts(true)
.warmCoinbase(false)
.maxStackSize(evmConfiguration.evmStackSize())
.feeMarket(feeMarket)
.coinbaseFeePriceCalculator(CoinbaseFeePriceCalculator.frontier())
.build())
.name("SpuriousDragon");
}
@@ -503,16 +505,17 @@ public abstract class MainnetProtocolSpecs {
transactionValidatorFactory,
contractCreationProcessor,
messageCallProcessor) ->
new MainnetTransactionProcessor(
gasCalculator,
transactionValidatorFactory,
contractCreationProcessor,
messageCallProcessor,
true,
false,
evmConfiguration.evmStackSize(),
feeMarket,
CoinbaseFeePriceCalculator.eip1559()))
MainnetTransactionProcessor.builder()
.gasCalculator(gasCalculator)
.transactionValidatorFactory(transactionValidatorFactory)
.contractCreationProcessor(contractCreationProcessor)
.messageCallProcessor(messageCallProcessor)
.clearEmptyAccounts(true)
.warmCoinbase(false)
.maxStackSize(evmConfiguration.evmStackSize())
.feeMarket(feeMarket)
.coinbaseFeePriceCalculator(CoinbaseFeePriceCalculator.eip1559())
.build())
.contractCreationProcessorBuilder(
evm ->
new ContractCreationProcessor(
@@ -635,16 +638,17 @@ public abstract class MainnetProtocolSpecs {
transactionValidatorFactory,
contractCreationProcessor,
messageCallProcessor) ->
new MainnetTransactionProcessor(
gasCalculator,
transactionValidatorFactory,
contractCreationProcessor,
messageCallProcessor,
true,
true,
evmConfiguration.evmStackSize(),
feeMarket,
CoinbaseFeePriceCalculator.eip1559()))
MainnetTransactionProcessor.builder()
.gasCalculator(gasCalculator)
.transactionValidatorFactory(transactionValidatorFactory)
.contractCreationProcessor(contractCreationProcessor)
.messageCallProcessor(messageCallProcessor)
.clearEmptyAccounts(true)
.warmCoinbase(true)
.maxStackSize(evmConfiguration.evmStackSize())
.feeMarket(feeMarket)
.coinbaseFeePriceCalculator(CoinbaseFeePriceCalculator.eip1559())
.build())
// Contract creation rules for EIP-3860 Limit and meter intitcode
.transactionValidatorFactoryBuilder(
(evm, gasLimitCalculator, feeMarket) ->
@@ -722,18 +726,20 @@ public abstract class MainnetProtocolSpecs {
transactionValidator,
contractCreationProcessor,
messageCallProcessor) ->
new MainnetTransactionProcessor(
gasCalculator,
transactionValidator,
contractCreationProcessor,
messageCallProcessor,
true,
true,
evmConfiguration.evmStackSize(),
feeMarket,
CoinbaseFeePriceCalculator.eip1559(),
new CodeDelegationProcessor(
chainId, SIGNATURE_ALGORITHM.get().getHalfCurveOrder())))
MainnetTransactionProcessor.builder()
.gasCalculator(gasCalculator)
.transactionValidatorFactory(transactionValidator)
.contractCreationProcessor(contractCreationProcessor)
.messageCallProcessor(messageCallProcessor)
.clearEmptyAccounts(true)
.warmCoinbase(true)
.maxStackSize(evmConfiguration.evmStackSize())
.feeMarket(feeMarket)
.coinbaseFeePriceCalculator(CoinbaseFeePriceCalculator.eip1559())
.codeDelegationProcessor(
new CodeDelegationProcessor(
chainId, SIGNATURE_ALGORITHM.get().getHalfCurveOrder()))
.build())
// change to check for max blob gas per block for EIP-4844
.transactionValidatorFactoryBuilder(
(evm, gasLimitCalculator, feeMarket) ->

View File

@@ -41,6 +41,8 @@ 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.processor.AbstractMessageProcessor;
import org.hyperledger.besu.evm.processor.ContractCreationProcessor;
import org.hyperledger.besu.evm.processor.MessageCallProcessor;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.evm.worldstate.EVMWorldUpdater;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
@@ -68,9 +70,9 @@ public class MainnetTransactionProcessor {
protected final TransactionValidatorFactory transactionValidatorFactory;
private final AbstractMessageProcessor contractCreationProcessor;
private final ContractCreationProcessor contractCreationProcessor;
private final AbstractMessageProcessor messageCallProcessor;
private final MessageCallProcessor messageCallProcessor;
private final int maxStackSize;
@@ -83,34 +85,11 @@ public class MainnetTransactionProcessor {
private final Optional<CodeDelegationProcessor> maybeCodeDelegationProcessor;
public MainnetTransactionProcessor(
private MainnetTransactionProcessor(
final GasCalculator gasCalculator,
final TransactionValidatorFactory transactionValidatorFactory,
final AbstractMessageProcessor contractCreationProcessor,
final AbstractMessageProcessor messageCallProcessor,
final boolean clearEmptyAccounts,
final boolean warmCoinbase,
final int maxStackSize,
final FeeMarket feeMarket,
final CoinbaseFeePriceCalculator coinbaseFeePriceCalculator) {
this(
gasCalculator,
transactionValidatorFactory,
contractCreationProcessor,
messageCallProcessor,
clearEmptyAccounts,
warmCoinbase,
maxStackSize,
feeMarket,
coinbaseFeePriceCalculator,
null);
}
public MainnetTransactionProcessor(
final GasCalculator gasCalculator,
final TransactionValidatorFactory transactionValidatorFactory,
final AbstractMessageProcessor contractCreationProcessor,
final AbstractMessageProcessor messageCallProcessor,
final ContractCreationProcessor contractCreationProcessor,
final MessageCallProcessor messageCallProcessor,
final boolean clearEmptyAccounts,
final boolean warmCoinbase,
final int maxStackSize,
@@ -632,6 +611,10 @@ public class MainnetTransactionProcessor {
};
}
public MessageCallProcessor getMessageCallProcessor() {
return messageCallProcessor;
}
private String printableStackTraceFromThrowable(final RuntimeException re) {
final StringBuilder builder = new StringBuilder();
@@ -641,4 +624,103 @@ public class MainnetTransactionProcessor {
return builder.toString();
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private GasCalculator gasCalculator;
private TransactionValidatorFactory transactionValidatorFactory;
private ContractCreationProcessor contractCreationProcessor;
private MessageCallProcessor messageCallProcessor;
private boolean clearEmptyAccounts;
private boolean warmCoinbase;
private int maxStackSize;
private FeeMarket feeMarket;
private CoinbaseFeePriceCalculator coinbaseFeePriceCalculator;
private CodeDelegationProcessor codeDelegationProcessor;
public Builder gasCalculator(final GasCalculator gasCalculator) {
this.gasCalculator = gasCalculator;
return this;
}
public Builder transactionValidatorFactory(
final TransactionValidatorFactory transactionValidatorFactory) {
this.transactionValidatorFactory = transactionValidatorFactory;
return this;
}
public Builder contractCreationProcessor(
final ContractCreationProcessor contractCreationProcessor) {
this.contractCreationProcessor = contractCreationProcessor;
return this;
}
public Builder messageCallProcessor(final MessageCallProcessor messageCallProcessor) {
this.messageCallProcessor = messageCallProcessor;
return this;
}
public Builder clearEmptyAccounts(final boolean clearEmptyAccounts) {
this.clearEmptyAccounts = clearEmptyAccounts;
return this;
}
public Builder warmCoinbase(final boolean warmCoinbase) {
this.warmCoinbase = warmCoinbase;
return this;
}
public Builder maxStackSize(final int maxStackSize) {
this.maxStackSize = maxStackSize;
return this;
}
public Builder feeMarket(final FeeMarket feeMarket) {
this.feeMarket = feeMarket;
return this;
}
public Builder coinbaseFeePriceCalculator(
final CoinbaseFeePriceCalculator coinbaseFeePriceCalculator) {
this.coinbaseFeePriceCalculator = coinbaseFeePriceCalculator;
return this;
}
public Builder codeDelegationProcessor(
final CodeDelegationProcessor maybeCodeDelegationProcessor) {
this.codeDelegationProcessor = maybeCodeDelegationProcessor;
return this;
}
public Builder populateFrom(final MainnetTransactionProcessor processor) {
this.gasCalculator = processor.gasCalculator;
this.transactionValidatorFactory = processor.transactionValidatorFactory;
this.contractCreationProcessor = processor.contractCreationProcessor;
this.messageCallProcessor = processor.messageCallProcessor;
this.clearEmptyAccounts = processor.clearEmptyAccounts;
this.warmCoinbase = processor.warmCoinbase;
this.maxStackSize = processor.maxStackSize;
this.feeMarket = processor.feeMarket;
this.coinbaseFeePriceCalculator = processor.coinbaseFeePriceCalculator;
this.codeDelegationProcessor = processor.maybeCodeDelegationProcessor.orElse(null);
return this;
}
public MainnetTransactionProcessor build() {
return new MainnetTransactionProcessor(
gasCalculator,
transactionValidatorFactory,
contractCreationProcessor,
messageCallProcessor,
clearEmptyAccounts,
warmCoinbase,
maxStackSize,
feeMarket,
coinbaseFeePriceCalculator,
codeDelegationProcessor);
}
}
}

View File

@@ -41,6 +41,8 @@ import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.precompile.PrecompileContractRegistry;
import org.hyperledger.besu.evm.processor.AbstractMessageProcessor;
import org.hyperledger.besu.evm.processor.ContractCreationProcessor;
import org.hyperledger.besu.evm.processor.MessageCallProcessor;
import java.util.Optional;
import java.util.function.BiFunction;
@@ -62,10 +64,10 @@ public class ProtocolSpecBuilder {
private Function<FeeMarket, BlockHeaderValidator.Builder> blockHeaderValidatorBuilder;
private Function<FeeMarket, BlockHeaderValidator.Builder> ommerHeaderValidatorBuilder;
private Function<ProtocolSchedule, BlockBodyValidator> blockBodyValidatorBuilder;
private Function<EVM, AbstractMessageProcessor> contractCreationProcessorBuilder;
private Function<EVM, ContractCreationProcessor> contractCreationProcessorBuilder;
private Function<PrecompiledContractConfiguration, PrecompileContractRegistry>
precompileContractRegistryBuilder;
private BiFunction<EVM, PrecompileContractRegistry, AbstractMessageProcessor>
private BiFunction<EVM, PrecompileContractRegistry, MessageCallProcessor>
messageCallProcessorBuilder;
private TransactionProcessorBuilder transactionProcessorBuilder;
@@ -158,7 +160,7 @@ public class ProtocolSpecBuilder {
}
public ProtocolSpecBuilder contractCreationProcessorBuilder(
final Function<EVM, AbstractMessageProcessor> contractCreationProcessorBuilder) {
final Function<EVM, ContractCreationProcessor> contractCreationProcessorBuilder) {
this.contractCreationProcessorBuilder = contractCreationProcessorBuilder;
return this;
}
@@ -180,7 +182,7 @@ public class ProtocolSpecBuilder {
}
public ProtocolSpecBuilder messageCallProcessorBuilder(
final BiFunction<EVM, PrecompileContractRegistry, AbstractMessageProcessor>
final BiFunction<EVM, PrecompileContractRegistry, MessageCallProcessor>
messageCallProcessorBuilder) {
this.messageCallProcessorBuilder = messageCallProcessorBuilder;
return this;
@@ -330,11 +332,11 @@ public class ProtocolSpecBuilder {
new PrecompiledContractConfiguration(gasCalculator, privacyParameters);
final TransactionValidatorFactory transactionValidatorFactory =
transactionValidatorFactoryBuilder.apply(evm, gasLimitCalculator, feeMarket);
final AbstractMessageProcessor contractCreationProcessor =
final ContractCreationProcessor contractCreationProcessor =
contractCreationProcessorBuilder.apply(evm);
final PrecompileContractRegistry precompileContractRegistry =
precompileContractRegistryBuilder.apply(precompiledContractConfiguration);
final AbstractMessageProcessor messageCallProcessor =
final MessageCallProcessor messageCallProcessor =
messageCallProcessorBuilder.apply(evm, precompileContractRegistry);
final MainnetTransactionProcessor transactionProcessor =
transactionProcessorBuilder.apply(
@@ -469,8 +471,8 @@ public class ProtocolSpecBuilder {
GasCalculator gasCalculator,
FeeMarket feeMarket,
TransactionValidatorFactory transactionValidatorFactory,
AbstractMessageProcessor contractCreationProcessor,
AbstractMessageProcessor messageCallProcessor);
ContractCreationProcessor contractCreationProcessor,
MessageCallProcessor messageCallProcessor);
}
public interface PrivateTransactionProcessorBuilder {

View File

@@ -0,0 +1,85 @@
/*
* Copyright contributors to 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.ethereum.transaction;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.StateOverrideMap;
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.evm.processor.OverriddenPrecompilesMessageCallProcessor;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
public class SimulationTransactionProcessorFactory {
private final ProtocolSchedule protocolSchedule;
/**
* Creates a factory capable of producing transaction processors.
*
* @param protocolSchedule the protocol schedule used for creating processors
*/
public SimulationTransactionProcessorFactory(final ProtocolSchedule protocolSchedule) {
this.protocolSchedule = protocolSchedule;
}
/**
* Creates a transaction processor, optionally applying state overrides.
*
* @param processableHeader the block header to process transactions against
* @param maybeStateOverrides optional state overrides for simulation
* @return a transaction processor, with overrides applied if provided
*/
public MainnetTransactionProcessor getTransactionProcessor(
final ProcessableBlockHeader processableHeader,
final Optional<StateOverrideMap> maybeStateOverrides) {
MainnetTransactionProcessor baseProcessor =
protocolSchedule.getByBlockHeader(processableHeader).getTransactionProcessor();
return maybeStateOverrides
.flatMap(this::extractPrecompileAddressOverrides)
.map(
precompileOverrides -> createProcessorWithOverrides(baseProcessor, precompileOverrides))
.orElse(baseProcessor);
}
private Optional<Map<Address, Address>> extractPrecompileAddressOverrides(
final StateOverrideMap stateOverrides) {
Map<Address, Address> addressOverrides =
stateOverrides.entrySet().stream()
.filter(entry -> entry.getValue().getMovePrecompileToAddress().isPresent())
.collect(
Collectors.toMap(
Map.Entry::getKey,
entry -> entry.getValue().getMovePrecompileToAddress().get()));
return addressOverrides.isEmpty() ? Optional.empty() : Optional.of(addressOverrides);
}
private MainnetTransactionProcessor createProcessorWithOverrides(
final MainnetTransactionProcessor baseProcessor,
final Map<Address, Address> precompileAddressOverrides) {
return MainnetTransactionProcessor.builder()
.populateFrom(baseProcessor)
.messageCallProcessor(
new OverriddenPrecompilesMessageCallProcessor(
baseProcessor.getMessageCallProcessor(), precompileAddressOverrides))
.build();
}
}

View File

@@ -87,6 +87,7 @@ public class TransactionSimulator {
private final WorldStateArchive worldStateArchive;
private final ProtocolSchedule protocolSchedule;
private final MiningConfiguration miningConfiguration;
private final SimulationTransactionProcessorFactory simulationTransactionProcessorFactory;
private final long rpcGasCap;
public TransactionSimulator(
@@ -100,6 +101,8 @@ public class TransactionSimulator {
this.protocolSchedule = protocolSchedule;
this.miningConfiguration = miningConfiguration;
this.rpcGasCap = rpcGasCap;
this.simulationTransactionProcessorFactory =
new SimulationTransactionProcessorFactory(protocolSchedule);
}
public Optional<TransactionSimulatorResult> process(
@@ -402,8 +405,9 @@ public class TransactionSimulator {
final long simulationGasCap =
calculateSimulationGasCap(callParams.getGasLimit(), blockHeaderToProcess.getGasLimit());
final MainnetTransactionProcessor transactionProcessor =
protocolSchedule.getByBlockHeader(blockHeaderToProcess).getTransactionProcessor();
MainnetTransactionProcessor transactionProcessor =
simulationTransactionProcessorFactory.getTransactionProcessor(
processableHeader, maybeStateOverrides);
final Optional<BlockHeader> maybeParentHeader =
blockchain.getBlockHeader(blockHeaderToProcess.getParentHash());

View File

@@ -34,7 +34,8 @@ import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.gascalculator.LondonGasCalculator;
import org.hyperledger.besu.evm.log.Log;
import org.hyperledger.besu.evm.processor.AbstractMessageProcessor;
import org.hyperledger.besu.evm.processor.ContractCreationProcessor;
import org.hyperledger.besu.evm.processor.MessageCallProcessor;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.evm.worldstate.WorldView;
@@ -67,8 +68,8 @@ class MainnetTransactionProcessorTest {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private TransactionValidatorFactory transactionValidatorFactory;
@Mock private AbstractMessageProcessor contractCreationProcessor;
@Mock private AbstractMessageProcessor messageCallProcessor;
@Mock private ContractCreationProcessor contractCreationProcessor;
@Mock private MessageCallProcessor messageCallProcessor;
@Mock private WorldUpdater worldState;
@Mock private ProcessableBlockHeader blockHeader;
@@ -79,17 +80,19 @@ class MainnetTransactionProcessorTest {
@Mock private MutableAccount receiverAccount;
MainnetTransactionProcessor createTransactionProcessor(final boolean warmCoinbase) {
return new MainnetTransactionProcessor(
gasCalculator,
transactionValidatorFactory,
contractCreationProcessor,
messageCallProcessor,
false,
warmCoinbase,
MAX_STACK_SIZE,
FeeMarket.legacy(),
CoinbaseFeePriceCalculator.frontier(),
new CodeDelegationProcessor(Optional.of(BigInteger.ONE), BigInteger.TEN));
return MainnetTransactionProcessor.builder()
.gasCalculator(gasCalculator)
.transactionValidatorFactory(transactionValidatorFactory)
.contractCreationProcessor(contractCreationProcessor)
.messageCallProcessor(messageCallProcessor)
.clearEmptyAccounts(false)
.warmCoinbase(warmCoinbase)
.maxStackSize(MAX_STACK_SIZE)
.feeMarket(FeeMarket.legacy())
.coinbaseFeePriceCalculator(CoinbaseFeePriceCalculator.frontier())
.codeDelegationProcessor(
new CodeDelegationProcessor(Optional.of(BigInteger.ONE), BigInteger.TEN))
.build();
}
@Test

View File

@@ -0,0 +1,91 @@
/*
* Copyright contributors to 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.ethereum.transaction;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.StateOverride;
import org.hyperledger.besu.datatypes.StateOverrideMap;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.evm.EVM;
import org.hyperledger.besu.evm.precompile.PrecompileContractRegistry;
import org.hyperledger.besu.evm.precompile.PrecompiledContract;
import org.hyperledger.besu.evm.processor.MessageCallProcessor;
import java.util.Optional;
import java.util.Set;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class SimulationTransactionProcessorFactoryTest {
private SimulationTransactionProcessorFactory factory;
private final Address originalPrecompileAddress = Address.fromHexString("0x1");
private final Address newPrecompileAddress = Address.fromHexString("0x2");
@BeforeEach
void setUp() {
ProtocolSpec protocolSpec = mock(ProtocolSpec.class);
ProtocolSchedule protocolSchedule = mock(ProtocolSchedule.class);
when(protocolSchedule.getByBlockHeader(any())).thenReturn(protocolSpec);
PrecompileContractRegistry precompileContractRegistry = new PrecompileContractRegistry();
precompileContractRegistry.put(originalPrecompileAddress, mock(PrecompiledContract.class));
MainnetTransactionProcessor mainnetTransactionProcessor =
MainnetTransactionProcessor.builder()
.messageCallProcessor(
new MessageCallProcessor(mock(EVM.class), precompileContractRegistry))
.build();
when(protocolSpec.getTransactionProcessor()).thenReturn(mainnetTransactionProcessor);
factory = new SimulationTransactionProcessorFactory(protocolSchedule);
}
@Test
void shouldReturnProcessorWithOriginalPrecompileAddressesIfNoOverrides() {
MainnetTransactionProcessor simulationTransactionProcessor =
factory.getTransactionProcessor(null, Optional.empty());
Set<Address> precompileAddresses =
simulationTransactionProcessor.getMessageCallProcessor().getPrecompileAddresses();
assertThat(precompileAddresses).containsExactlyInAnyOrder(originalPrecompileAddress);
}
@Test
void shouldReturnProcessorWithNewPrecompileAddressesWithOverrides() {
StateOverrideMap stateOverrideMap = new StateOverrideMap();
stateOverrideMap.put(
originalPrecompileAddress,
new StateOverride.Builder().withMovePrecompileToAddress(newPrecompileAddress).build());
MainnetTransactionProcessor simulationTransactionProcessor =
factory.getTransactionProcessor(null, Optional.of(stateOverrideMap));
Set<Address> precompileAddresses =
simulationTransactionProcessor.getMessageCallProcessor().getPrecompileAddresses();
assertThat(precompileAddresses).containsExactlyInAnyOrder(newPrecompileAddress);
}
}

View File

@@ -16,8 +16,10 @@ package org.hyperledger.besu.evm.precompile;
import org.hyperledger.besu.datatypes.Address;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/** Encapsulates a group of {@link PrecompiledContract}s used together. */
public class PrecompileContractRegistry {
@@ -48,4 +50,13 @@ public class PrecompileContractRegistry {
public void put(final Address address, final PrecompiledContract precompile) {
precompiles.put(address, precompile);
}
/**
* Gets the addresses of the precompiled contracts.
*
* @return the addresses
*/
public Set<Address> getPrecompileAddresses() {
return Collections.unmodifiableSet(precompiles.keySet());
}
}

View File

@@ -30,6 +30,7 @@ import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import com.google.common.annotations.VisibleForTesting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -37,7 +38,8 @@ import org.slf4j.LoggerFactory;
public class MessageCallProcessor extends AbstractMessageProcessor {
private static final Logger LOG = LoggerFactory.getLogger(MessageCallProcessor.class);
private final PrecompileContractRegistry precompiles;
/** The precompiles. */
protected final PrecompileContractRegistry precompiles;
/**
* Instantiates a new Message call processor.
@@ -171,4 +173,14 @@ public class MessageCallProcessor extends AbstractMessageProcessor {
frame.setExceptionalHaltReason(result.getHaltReason());
}
}
/**
* Gets the precompile addresses.
*
* @return the precompile addresses
*/
@VisibleForTesting
public Set<Address> getPrecompileAddresses() {
return precompiles.getPrecompileAddresses();
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright contributors to 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.processor;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.evm.precompile.PrecompileContractRegistry;
import java.util.Map;
/**
* A message call processor designed specifically for simulation purposes that allows for overriding
* precompile addresses.
*/
public class OverriddenPrecompilesMessageCallProcessor extends MessageCallProcessor {
/**
* Instantiates a new Modifiable precompiles message call processor for simulation.
*
* @param originalProcessor the original processor
* @param precompileOverrides the address overrides
*/
public OverriddenPrecompilesMessageCallProcessor(
final MessageCallProcessor originalProcessor,
final Map<Address, Address> precompileOverrides) {
super(
originalProcessor.evm,
createRegistryWithPrecompileOverrides(originalProcessor.precompiles, precompileOverrides));
}
/**
* Creates a new PrecompileContractRegistry with the specified address overrides.
*
* @param originalRegistry the original precompile contract registry
* @param precompileOverrides the address overrides
* @return a new PrecompileContractRegistry with the overrides applied
*/
private static PrecompileContractRegistry createRegistryWithPrecompileOverrides(
final PrecompileContractRegistry originalRegistry,
final Map<Address, Address> precompileOverrides) {
PrecompileContractRegistry newRegistry = new PrecompileContractRegistry();
for (Address originalAddress : originalRegistry.getPrecompileAddresses()) {
Address effectiveAddress = precompileOverrides.getOrDefault(originalAddress, originalAddress);
newRegistry.put(effectiveAddress, originalRegistry.get(originalAddress));
}
return newRegistry;
}
}