Transaction simulation service (#6686)

Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net>
pull/6725/head
Fabio Di Fabio 8 months ago committed by GitHub
parent efd1bc7070
commit 4cc6b744cf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 1
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/BesuNode.java
  3. 55
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java
  4. 19
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java
  5. 8
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/NodeConfigurationFactory.java
  6. 9
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/NodeRequests.java
  7. 2
      besu/build.gradle
  8. 17
      besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
  9. 84
      besu/src/main/java/org/hyperledger/besu/services/TransactionSimulationServiceImpl.java
  10. 11
      besu/src/test/java/org/hyperledger/besu/RunnerTest.java
  11. 3
      besu/src/test/java/org/hyperledger/besu/chainimport/JsonBlockImporterTest.java
  12. 2
      besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java
  13. 2
      consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/validator/ValidatorContractController.java
  14. 4
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/AbstractEstimateGas.java
  15. 2
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugTraceCall.java
  16. 2
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCall.java
  17. 2
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGas.java
  18. 2
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceCall.java
  19. 2
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceCallMany.java
  20. 2
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/TraceRawTransaction.java
  21. 6
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCallTest.java
  22. 2
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthCreateAccessListTest.java
  23. 2
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthEstimateGasTest.java
  24. 4
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java
  25. 27
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/processing/TransactionProcessingResult.java
  26. 51
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/CallParameter.java
  27. 19
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java
  28. 52
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulatorResult.java
  29. 4
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java
  30. 3
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PermissionTransactionValidatorTest.java
  31. 2
      ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/NodeSmartContractPermissioningController.java
  32. 2
      ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/NodeSmartContractV2PermissioningController.java
  33. 2
      ethereum/permissioning/src/main/java/org/hyperledger/besu/ethereum/permissioning/TransactionSmartContractPermissioningController.java
  34. 2
      plugin-api/build.gradle
  35. 7
      plugin-api/src/main/java/org/hyperledger/besu/plugin/data/TransactionProcessingResult.java
  36. 54
      plugin-api/src/main/java/org/hyperledger/besu/plugin/data/TransactionSimulationResult.java
  37. 42
      plugin-api/src/main/java/org/hyperledger/besu/plugin/services/TransactionSimulationService.java
  38. 8
      testutil/src/main/java/org/hyperledger/besu/testutil/JsonTestParameters.java

@ -20,6 +20,7 @@
- Update Vert.x to 4.5.4 [#6666](https://github.com/hyperledger/besu/pull/6666) - Update Vert.x to 4.5.4 [#6666](https://github.com/hyperledger/besu/pull/6666)
- Refactor and extend `TransactionPoolValidatorService` [#6636](https://github.com/hyperledger/besu/pull/6636) - Refactor and extend `TransactionPoolValidatorService` [#6636](https://github.com/hyperledger/besu/pull/6636)
- Transaction call object to accept both `input` and `data` field simultaneously if they are set to equal values [#6702](https://github.com/hyperledger/besu/pull/6702) - Transaction call object to accept both `input` and `data` field simultaneously if they are set to equal values [#6702](https://github.com/hyperledger/besu/pull/6702)
- Introduce `TransactionSimulationService` [#6686](https://github.com/hyperledger/besu/pull/6686)
### Bug fixes ### Bug fixes
- Fix txpool dump/restore race condition [#6665](https://github.com/hyperledger/besu/pull/6665) - Fix txpool dump/restore race condition [#6665](https://github.com/hyperledger/besu/pull/6665)

@ -436,6 +436,7 @@ public class BesuNode implements NodeConfiguration, RunnableNode, AutoCloseable
nodeRequests = nodeRequests =
new NodeRequests( new NodeRequests(
web3jService,
new JsonRpc2_0Web3j(web3jService, 2000, Async.defaultExecutorService()), new JsonRpc2_0Web3j(web3jService, 2000, Async.defaultExecutorService()),
new CliqueRequestFactory(web3jService), new CliqueRequestFactory(web3jService),
new BftRequestFactory(web3jService, bftType), new BftRequestFactory(web3jService, bftType),

@ -28,6 +28,7 @@ import org.hyperledger.besu.crypto.KeyPairUtil;
import org.hyperledger.besu.cryptoservices.KeyPairSecurityModule; import org.hyperledger.besu.cryptoservices.KeyPairSecurityModule;
import org.hyperledger.besu.cryptoservices.NodeKey; import org.hyperledger.besu.cryptoservices.NodeKey;
import org.hyperledger.besu.ethereum.GasLimitCalculator; import org.hyperledger.besu.ethereum.GasLimitCalculator;
import org.hyperledger.besu.ethereum.api.ApiConfiguration;
import org.hyperledger.besu.ethereum.api.graphql.GraphQLConfiguration; import org.hyperledger.besu.ethereum.api.graphql.GraphQLConfiguration;
import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters; import org.hyperledger.besu.ethereum.core.ImmutableMiningParameters;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration; import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
@ -37,6 +38,7 @@ import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfigurati
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl; import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStorageProvider; import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStorageProvider;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStorageProviderBuilder; import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStorageProviderBuilder;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration; import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration;
import org.hyperledger.besu.evm.internal.EvmConfiguration; import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.metrics.MetricsSystemFactory; import org.hyperledger.besu.metrics.MetricsSystemFactory;
@ -44,6 +46,7 @@ import org.hyperledger.besu.metrics.ObservableMetricsSystem;
import org.hyperledger.besu.plugin.data.EnodeURL; import org.hyperledger.besu.plugin.data.EnodeURL;
import org.hyperledger.besu.plugin.services.BesuConfiguration; import org.hyperledger.besu.plugin.services.BesuConfiguration;
import org.hyperledger.besu.plugin.services.BesuEvents; import org.hyperledger.besu.plugin.services.BesuEvents;
import org.hyperledger.besu.plugin.services.BlockchainService;
import org.hyperledger.besu.plugin.services.PermissioningService; import org.hyperledger.besu.plugin.services.PermissioningService;
import org.hyperledger.besu.plugin.services.PicoCLIOptions; import org.hyperledger.besu.plugin.services.PicoCLIOptions;
import org.hyperledger.besu.plugin.services.PrivacyPluginService; import org.hyperledger.besu.plugin.services.PrivacyPluginService;
@ -52,10 +55,12 @@ import org.hyperledger.besu.plugin.services.SecurityModuleService;
import org.hyperledger.besu.plugin.services.StorageService; import org.hyperledger.besu.plugin.services.StorageService;
import org.hyperledger.besu.plugin.services.TransactionPoolValidatorService; import org.hyperledger.besu.plugin.services.TransactionPoolValidatorService;
import org.hyperledger.besu.plugin.services.TransactionSelectionService; import org.hyperledger.besu.plugin.services.TransactionSelectionService;
import org.hyperledger.besu.plugin.services.TransactionSimulationService;
import org.hyperledger.besu.plugin.services.storage.rocksdb.RocksDBPlugin; import org.hyperledger.besu.plugin.services.storage.rocksdb.RocksDBPlugin;
import org.hyperledger.besu.services.BesuConfigurationImpl; import org.hyperledger.besu.services.BesuConfigurationImpl;
import org.hyperledger.besu.services.BesuEventsImpl; import org.hyperledger.besu.services.BesuEventsImpl;
import org.hyperledger.besu.services.BesuPluginContextImpl; import org.hyperledger.besu.services.BesuPluginContextImpl;
import org.hyperledger.besu.services.BlockchainServiceImpl;
import org.hyperledger.besu.services.PermissioningServiceImpl; import org.hyperledger.besu.services.PermissioningServiceImpl;
import org.hyperledger.besu.services.PicoCLIOptionsImpl; import org.hyperledger.besu.services.PicoCLIOptionsImpl;
import org.hyperledger.besu.services.PrivacyPluginServiceImpl; import org.hyperledger.besu.services.PrivacyPluginServiceImpl;
@ -64,6 +69,7 @@ import org.hyperledger.besu.services.SecurityModuleServiceImpl;
import org.hyperledger.besu.services.StorageServiceImpl; import org.hyperledger.besu.services.StorageServiceImpl;
import org.hyperledger.besu.services.TransactionPoolValidatorServiceImpl; import org.hyperledger.besu.services.TransactionPoolValidatorServiceImpl;
import org.hyperledger.besu.services.TransactionSelectionServiceImpl; import org.hyperledger.besu.services.TransactionSelectionServiceImpl;
import org.hyperledger.besu.services.TransactionSimulationServiceImpl;
import java.io.File; import java.io.File;
import java.nio.file.Path; import java.nio.file.Path;
@ -95,18 +101,27 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner {
final BesuNode node, final BesuNode node,
final StorageServiceImpl storageService, final StorageServiceImpl storageService,
final SecurityModuleServiceImpl securityModuleService, final SecurityModuleServiceImpl securityModuleService,
final TransactionSimulationServiceImpl transactionSimulationServiceImpl,
final TransactionSelectionServiceImpl transactionSelectionServiceImpl, final TransactionSelectionServiceImpl transactionSelectionServiceImpl,
final TransactionPoolValidatorServiceImpl transactionPoolValidatorServiceImpl,
final BlockchainServiceImpl blockchainServiceImpl,
final RpcEndpointServiceImpl rpcEndpointServiceImpl,
final BesuConfiguration commonPluginConfiguration) { final BesuConfiguration commonPluginConfiguration) {
final CommandLine commandLine = new CommandLine(CommandSpec.create()); final CommandLine commandLine = new CommandLine(CommandSpec.create());
final BesuPluginContextImpl besuPluginContext = new BesuPluginContextImpl(); final BesuPluginContextImpl besuPluginContext = new BesuPluginContextImpl();
besuPluginContext.addService(StorageService.class, storageService); besuPluginContext.addService(StorageService.class, storageService);
besuPluginContext.addService(SecurityModuleService.class, securityModuleService); besuPluginContext.addService(SecurityModuleService.class, securityModuleService);
besuPluginContext.addService(PicoCLIOptions.class, new PicoCLIOptionsImpl(commandLine)); besuPluginContext.addService(PicoCLIOptions.class, new PicoCLIOptionsImpl(commandLine));
besuPluginContext.addService(RpcEndpointService.class, new RpcEndpointServiceImpl()); besuPluginContext.addService(RpcEndpointService.class, rpcEndpointServiceImpl);
besuPluginContext.addService( besuPluginContext.addService(
TransactionSelectionService.class, transactionSelectionServiceImpl); TransactionSelectionService.class, transactionSelectionServiceImpl);
besuPluginContext.addService( besuPluginContext.addService(
TransactionPoolValidatorService.class, new TransactionPoolValidatorServiceImpl()); TransactionPoolValidatorService.class, transactionPoolValidatorServiceImpl);
besuPluginContext.addService(
TransactionSimulationService.class, transactionSimulationServiceImpl);
besuPluginContext.addService(BlockchainService.class, blockchainServiceImpl);
besuPluginContext.addService(BesuConfiguration.class, commonPluginConfiguration);
final Path pluginsPath; final Path pluginsPath;
final String pluginDir = System.getProperty("besu.plugins.dir"); final String pluginDir = System.getProperty("besu.plugins.dir");
if (pluginDir == null || pluginDir.isEmpty()) { if (pluginDir == null || pluginDir.isEmpty()) {
@ -147,8 +162,14 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner {
final StorageServiceImpl storageService = new StorageServiceImpl(); final StorageServiceImpl storageService = new StorageServiceImpl();
final SecurityModuleServiceImpl securityModuleService = new SecurityModuleServiceImpl(); final SecurityModuleServiceImpl securityModuleService = new SecurityModuleServiceImpl();
final TransactionSimulationServiceImpl transactionSimulationServiceImpl =
new TransactionSimulationServiceImpl();
final TransactionSelectionServiceImpl transactionSelectionServiceImpl = final TransactionSelectionServiceImpl transactionSelectionServiceImpl =
new TransactionSelectionServiceImpl(); new TransactionSelectionServiceImpl();
final TransactionPoolValidatorServiceImpl transactionPoolValidatorServiceImpl =
new TransactionPoolValidatorServiceImpl();
final BlockchainServiceImpl blockchainServiceImpl = new BlockchainServiceImpl();
final RpcEndpointServiceImpl rpcEndpointServiceImpl = new RpcEndpointServiceImpl();
final Path dataDir = node.homeDirectory(); final Path dataDir = node.homeDirectory();
final BesuConfigurationImpl commonPluginConfiguration = new BesuConfigurationImpl(); final BesuConfigurationImpl commonPluginConfiguration = new BesuConfigurationImpl();
final var miningParameters = final var miningParameters =
@ -169,7 +190,11 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner {
node, node,
storageService, storageService,
securityModuleService, securityModuleService,
transactionSimulationServiceImpl,
transactionSelectionServiceImpl, transactionSelectionServiceImpl,
transactionPoolValidatorServiceImpl,
blockchainServiceImpl,
rpcEndpointServiceImpl,
commonPluginConfiguration)); commonPluginConfiguration));
GlobalOpenTelemetry.resetForTest(); GlobalOpenTelemetry.resetForTest();
@ -203,6 +228,7 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner {
ImmutableTransactionPoolConfiguration.builder() ImmutableTransactionPoolConfiguration.builder()
.from(node.getTransactionPoolConfiguration()) .from(node.getTransactionPoolConfiguration())
.strictTransactionReplayProtectionEnabled(node.isStrictTxReplayProtectionEnabled()) .strictTransactionReplayProtectionEnabled(node.isStrictTxReplayProtectionEnabled())
.transactionPoolValidatorService(transactionPoolValidatorServiceImpl)
.build(); .build();
final int maxPeers = 25; final int maxPeers = 25;
@ -236,6 +262,10 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner {
final BesuController besuController = builder.build(); final BesuController besuController = builder.build();
initTransactionSimulationService(
transactionSimulationServiceImpl, besuController, node.getApiConfiguration());
initBlockchainService(blockchainServiceImpl, besuController);
final RunnerBuilder runnerBuilder = new RunnerBuilder(); final RunnerBuilder runnerBuilder = new RunnerBuilder();
runnerBuilder.permissioningConfiguration(node.getPermissioningConfiguration()); runnerBuilder.permissioningConfiguration(node.getPermissioningConfiguration());
runnerBuilder.apiConfiguration(node.getApiConfiguration()); runnerBuilder.apiConfiguration(node.getApiConfiguration());
@ -265,7 +295,7 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner {
.besuPluginContext(new BesuPluginContextImpl()) .besuPluginContext(new BesuPluginContextImpl())
.autoLogBloomCaching(false) .autoLogBloomCaching(false)
.storageProvider(storageProvider) .storageProvider(storageProvider)
.rpcEndpointService(new RpcEndpointServiceImpl()); .rpcEndpointService(rpcEndpointServiceImpl);
node.engineRpcConfiguration().ifPresent(runnerBuilder::engineJsonRpcConfiguration); node.engineRpcConfiguration().ifPresent(runnerBuilder::engineJsonRpcConfiguration);
final Runner runner = runnerBuilder.build(); final Runner runner = runnerBuilder.build();
@ -289,6 +319,25 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner {
MDC.remove("node"); MDC.remove("node");
} }
private void initBlockchainService(
final BlockchainServiceImpl blockchainServiceImpl, final BesuController besuController) {
blockchainServiceImpl.init(
besuController.getProtocolContext(), besuController.getProtocolSchedule());
}
private void initTransactionSimulationService(
final TransactionSimulationServiceImpl transactionSimulationService,
final BesuController besuController,
final ApiConfiguration apiConfiguration) {
transactionSimulationService.init(
besuController.getProtocolContext().getBlockchain(),
new TransactionSimulator(
besuController.getProtocolContext().getBlockchain(),
besuController.getProtocolContext().getWorldStateArchive(),
besuController.getProtocolSchedule(),
apiConfiguration.getGasCap()));
}
@Override @Override
public void stopNode(final BesuNode node) { public void stopNode(final BesuNode node) {
final BesuPluginContextImpl pluginContext = besuPluginContextMap.remove(node); final BesuPluginContextImpl pluginContext = besuPluginContextMap.remove(node);

@ -49,6 +49,7 @@ import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import java.util.function.UnaryOperator; import java.util.function.UnaryOperator;
import io.vertx.core.Vertx; import io.vertx.core.Vertx;
@ -376,17 +377,27 @@ public class BesuNodeFactory {
public BesuNode createCliqueNode(final String name, final CliqueOptions cliqueOptions) public BesuNode createCliqueNode(final String name, final CliqueOptions cliqueOptions)
throws IOException { throws IOException {
return createCliqueNodeWithExtraCliOptions(name, cliqueOptions, List.of()); return createCliqueNodeWithExtraCliOptionsAndRpcApis(name, cliqueOptions, List.of());
} }
public BesuNode createCliqueNodeWithExtraCliOptions( public BesuNode createCliqueNodeWithExtraCliOptionsAndRpcApis(
final String name, final CliqueOptions cliqueOptions, final List<String> extraCliOptions) final String name, final CliqueOptions cliqueOptions, final List<String> extraCliOptions)
throws IOException { throws IOException {
return createCliqueNodeWithExtraCliOptionsAndRpcApis(
name, cliqueOptions, extraCliOptions, Set.of());
}
public BesuNode createCliqueNodeWithExtraCliOptionsAndRpcApis(
final String name,
final CliqueOptions cliqueOptions,
final List<String> extraCliOptions,
final Set<String> extraRpcApis)
throws IOException {
return create( return create(
new BesuNodeConfigurationBuilder() new BesuNodeConfigurationBuilder()
.name(name) .name(name)
.miningEnabled() .miningEnabled()
.jsonRpcConfiguration(node.createJsonRpcWithCliqueEnabledConfig()) .jsonRpcConfiguration(node.createJsonRpcWithCliqueEnabledConfig(extraRpcApis))
.webSocketConfiguration(node.createWebSocketEnabledConfig()) .webSocketConfiguration(node.createWebSocketEnabledConfig())
.devMode(false) .devMode(false)
.jsonRpcTxPool() .jsonRpcTxPool()
@ -584,7 +595,7 @@ public class BesuNodeFactory {
new BesuNodeConfigurationBuilder() new BesuNodeConfigurationBuilder()
.name(name) .name(name)
.miningEnabled() .miningEnabled()
.jsonRpcConfiguration(node.createJsonRpcWithCliqueEnabledConfig()) .jsonRpcConfiguration(node.createJsonRpcWithCliqueEnabledConfig(Set.of()))
.webSocketConfiguration(node.createWebSocketEnabledConfig()) .webSocketConfiguration(node.createWebSocketEnabledConfig())
.jsonRpcTxPool() .jsonRpcTxPool()
.devMode(false) .devMode(false)

@ -30,8 +30,10 @@ import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.genesis.Gene
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
public class NodeConfigurationFactory { public class NodeConfigurationFactory {
@ -44,8 +46,10 @@ public class NodeConfigurationFactory {
return genesisConfigProvider.create(nodes); return genesisConfigProvider.create(nodes);
} }
public JsonRpcConfiguration createJsonRpcWithCliqueEnabledConfig() { public JsonRpcConfiguration createJsonRpcWithCliqueEnabledConfig(final Set<String> extraRpcApis) {
return createJsonRpcWithRpcApiEnabledConfig(CLIQUE.name()); final var enabledApis = new HashSet<>(extraRpcApis);
enabledApis.add(CLIQUE.name());
return createJsonRpcWithRpcApiEnabledConfig(enabledApis.toArray(String[]::new));
} }
public JsonRpcConfiguration createJsonRpcWithIbft2EnabledConfig(final boolean minerEnabled) { public JsonRpcConfiguration createJsonRpcWithIbft2EnabledConfig(final boolean minerEnabled) {

@ -27,10 +27,11 @@ import org.hyperledger.besu.tests.acceptance.dsl.transaction.txpool.TxPoolReques
import java.util.Optional; import java.util.Optional;
import org.web3j.protocol.Web3j; import org.web3j.protocol.Web3j;
import org.web3j.protocol.Web3jService;
import org.web3j.protocol.websocket.WebSocketService; import org.web3j.protocol.websocket.WebSocketService;
public class NodeRequests { public class NodeRequests {
private final Web3jService web3jService;
private final Web3j netEth; private final Web3j netEth;
private final CliqueRequestFactory clique; private final CliqueRequestFactory clique;
private final BftRequestFactory bft; private final BftRequestFactory bft;
@ -44,6 +45,7 @@ public class NodeRequests {
private final TxPoolRequestFactory txPool; private final TxPoolRequestFactory txPool;
public NodeRequests( public NodeRequests(
final Web3jService web3jService,
final Web3j netEth, final Web3j netEth,
final CliqueRequestFactory clique, final CliqueRequestFactory clique,
final BftRequestFactory bft, final BftRequestFactory bft,
@ -55,6 +57,7 @@ public class NodeRequests {
final TxPoolRequestFactory txPool, final TxPoolRequestFactory txPool,
final Optional<WebSocketService> websocketService, final Optional<WebSocketService> websocketService,
final LoginRequestFactory login) { final LoginRequestFactory login) {
this.web3jService = web3jService;
this.netEth = netEth; this.netEth = netEth;
this.clique = clique; this.clique = clique;
this.bft = bft; this.bft = bft;
@ -116,4 +119,8 @@ public class NodeRequests {
netEth.shutdown(); netEth.shutdown();
websocketService.ifPresent(WebSocketService::close); websocketService.ifPresent(WebSocketService::close);
} }
public Web3jService getWeb3jService() {
return web3jService;
}
} }

@ -28,6 +28,8 @@ jar {
} }
dependencies { dependencies {
api project(':datatypes')
api 'org.slf4j:slf4j-api' api 'org.slf4j:slf4j-api'
implementation project(':config') implementation project(':config')

@ -139,6 +139,7 @@ import org.hyperledger.besu.ethereum.storage.StorageProvider;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier; import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStorageProvider; import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStorageProvider;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStorageProviderBuilder; import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStorageProviderBuilder;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
import org.hyperledger.besu.ethereum.trie.forest.pruner.PrunerConfiguration; import org.hyperledger.besu.ethereum.trie.forest.pruner.PrunerConfiguration;
import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration; import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration;
import org.hyperledger.besu.evm.precompile.AbstractAltBnPrecompiledContract; import org.hyperledger.besu.evm.precompile.AbstractAltBnPrecompiledContract;
@ -167,6 +168,7 @@ import org.hyperledger.besu.plugin.services.StorageService;
import org.hyperledger.besu.plugin.services.TraceService; import org.hyperledger.besu.plugin.services.TraceService;
import org.hyperledger.besu.plugin.services.TransactionPoolValidatorService; import org.hyperledger.besu.plugin.services.TransactionPoolValidatorService;
import org.hyperledger.besu.plugin.services.TransactionSelectionService; import org.hyperledger.besu.plugin.services.TransactionSelectionService;
import org.hyperledger.besu.plugin.services.TransactionSimulationService;
import org.hyperledger.besu.plugin.services.exception.StorageException; import org.hyperledger.besu.plugin.services.exception.StorageException;
import org.hyperledger.besu.plugin.services.metrics.MetricCategory; import org.hyperledger.besu.plugin.services.metrics.MetricCategory;
import org.hyperledger.besu.plugin.services.metrics.MetricCategoryRegistry; import org.hyperledger.besu.plugin.services.metrics.MetricCategoryRegistry;
@ -187,6 +189,7 @@ import org.hyperledger.besu.services.StorageServiceImpl;
import org.hyperledger.besu.services.TraceServiceImpl; import org.hyperledger.besu.services.TraceServiceImpl;
import org.hyperledger.besu.services.TransactionPoolValidatorServiceImpl; import org.hyperledger.besu.services.TransactionPoolValidatorServiceImpl;
import org.hyperledger.besu.services.TransactionSelectionServiceImpl; import org.hyperledger.besu.services.TransactionSelectionServiceImpl;
import org.hyperledger.besu.services.TransactionSimulationServiceImpl;
import org.hyperledger.besu.services.kvstore.InMemoryStoragePlugin; import org.hyperledger.besu.services.kvstore.InMemoryStoragePlugin;
import org.hyperledger.besu.util.InvalidConfigurationException; import org.hyperledger.besu.util.InvalidConfigurationException;
import org.hyperledger.besu.util.LogConfigurator; import org.hyperledger.besu.util.LogConfigurator;
@ -370,6 +373,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
private final TransactionSelectionServiceImpl transactionSelectionServiceImpl; private final TransactionSelectionServiceImpl transactionSelectionServiceImpl;
private final TransactionPoolValidatorServiceImpl transactionValidatorServiceImpl; private final TransactionPoolValidatorServiceImpl transactionValidatorServiceImpl;
private final TransactionSimulationServiceImpl transactionSimulationServiceImpl;
private final BlockchainServiceImpl blockchainServiceImpl; private final BlockchainServiceImpl blockchainServiceImpl;
static class P2PDiscoveryOptionGroup { static class P2PDiscoveryOptionGroup {
@ -956,6 +960,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
new RpcEndpointServiceImpl(), new RpcEndpointServiceImpl(),
new TransactionSelectionServiceImpl(), new TransactionSelectionServiceImpl(),
new TransactionPoolValidatorServiceImpl(), new TransactionPoolValidatorServiceImpl(),
new TransactionSimulationServiceImpl(),
new BlockchainServiceImpl()); new BlockchainServiceImpl());
} }
@ -978,6 +983,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
* @param rpcEndpointServiceImpl instance of RpcEndpointServiceImpl * @param rpcEndpointServiceImpl instance of RpcEndpointServiceImpl
* @param transactionSelectionServiceImpl instance of TransactionSelectionServiceImpl * @param transactionSelectionServiceImpl instance of TransactionSelectionServiceImpl
* @param transactionValidatorServiceImpl instance of TransactionValidatorServiceImpl * @param transactionValidatorServiceImpl instance of TransactionValidatorServiceImpl
* @param transactionSimulationServiceImpl instance of TransactionSimulationServiceImpl
* @param blockchainServiceImpl instance of BlockchainServiceImpl * @param blockchainServiceImpl instance of BlockchainServiceImpl
*/ */
@VisibleForTesting @VisibleForTesting
@ -998,6 +1004,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
final RpcEndpointServiceImpl rpcEndpointServiceImpl, final RpcEndpointServiceImpl rpcEndpointServiceImpl,
final TransactionSelectionServiceImpl transactionSelectionServiceImpl, final TransactionSelectionServiceImpl transactionSelectionServiceImpl,
final TransactionPoolValidatorServiceImpl transactionValidatorServiceImpl, final TransactionPoolValidatorServiceImpl transactionValidatorServiceImpl,
final TransactionSimulationServiceImpl transactionSimulationServiceImpl,
final BlockchainServiceImpl blockchainServiceImpl) { final BlockchainServiceImpl blockchainServiceImpl) {
this.besuComponent = besuComponent; this.besuComponent = besuComponent;
this.logger = besuComponent.getBesuCommandLogger(); this.logger = besuComponent.getBesuCommandLogger();
@ -1018,6 +1025,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
this.rpcEndpointServiceImpl = rpcEndpointServiceImpl; this.rpcEndpointServiceImpl = rpcEndpointServiceImpl;
this.transactionSelectionServiceImpl = transactionSelectionServiceImpl; this.transactionSelectionServiceImpl = transactionSelectionServiceImpl;
this.transactionValidatorServiceImpl = transactionValidatorServiceImpl; this.transactionValidatorServiceImpl = transactionValidatorServiceImpl;
this.transactionSimulationServiceImpl = transactionSimulationServiceImpl;
this.blockchainServiceImpl = blockchainServiceImpl; this.blockchainServiceImpl = blockchainServiceImpl;
} }
@ -1210,6 +1218,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
TransactionSelectionService.class, transactionSelectionServiceImpl); TransactionSelectionService.class, transactionSelectionServiceImpl);
besuPluginContext.addService( besuPluginContext.addService(
TransactionPoolValidatorService.class, transactionValidatorServiceImpl); TransactionPoolValidatorService.class, transactionValidatorServiceImpl);
besuPluginContext.addService(
TransactionSimulationService.class, transactionSimulationServiceImpl);
besuPluginContext.addService(BlockchainService.class, blockchainServiceImpl); besuPluginContext.addService(BlockchainService.class, blockchainServiceImpl);
// register built-in plugins // register built-in plugins
@ -1293,6 +1303,13 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
private void startPlugins() { private void startPlugins() {
blockchainServiceImpl.init( blockchainServiceImpl.init(
besuController.getProtocolContext(), besuController.getProtocolSchedule()); besuController.getProtocolContext(), besuController.getProtocolSchedule());
transactionSimulationServiceImpl.init(
besuController.getProtocolContext().getBlockchain(),
new TransactionSimulator(
besuController.getProtocolContext().getBlockchain(),
besuController.getProtocolContext().getWorldStateArchive(),
besuController.getProtocolSchedule(),
apiConfiguration.getGasCap()));
besuPluginContext.addService( besuPluginContext.addService(
BesuEvents.class, BesuEvents.class,

@ -0,0 +1,84 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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.services;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Transaction;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.mainnet.ImmutableTransactionValidationParams;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
import org.hyperledger.besu.ethereum.transaction.CallParameter;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.plugin.Unstable;
import org.hyperledger.besu.plugin.data.TransactionSimulationResult;
import org.hyperledger.besu.plugin.services.TransactionSimulationService;
import java.util.Optional;
/** TransactionSimulationServiceImpl */
@Unstable
public class TransactionSimulationServiceImpl implements TransactionSimulationService {
private static final TransactionValidationParams SIMULATOR_ALLOWING_EXCEEDING_BALANCE =
ImmutableTransactionValidationParams.builder()
.from(TransactionValidationParams.transactionSimulator())
.isAllowExceedingBalance(true)
.build();
private Blockchain blockchain;
private TransactionSimulator transactionSimulator;
/** Create an instance to be configured */
public TransactionSimulationServiceImpl() {}
/**
* Configure the service
*
* @param blockchain the blockchain
* @param transactionSimulator transaction simulator
*/
public void init(final Blockchain blockchain, final TransactionSimulator transactionSimulator) {
this.blockchain = blockchain;
this.transactionSimulator = transactionSimulator;
}
@Override
public Optional<TransactionSimulationResult> simulate(
final Transaction transaction,
final Hash blockHash,
final OperationTracer operationTracer,
final boolean isAllowExceedingBalance) {
final CallParameter callParameter = CallParameter.fromTransaction(transaction);
final var blockHeader =
blockchain
.getBlockHeader(blockHash)
.or(() -> blockchain.getBlockHeaderSafe(blockHash))
.orElseThrow(
() ->
new IllegalStateException(
"Block header not yet present for chain head hash: " + blockHash));
return transactionSimulator
.process(
callParameter,
isAllowExceedingBalance
? SIMULATOR_ALLOWING_EXCEEDING_BALANCE
: TransactionValidationParams.transactionSimulator(),
operationTracer,
blockHeader)
.map(res -> new TransactionSimulationResult(transaction, res.result()));
}
}

@ -180,7 +180,8 @@ public final class RunnerTest {
aheadDbNodeKey, aheadDbNodeKey,
createKeyValueStorageProvider( createKeyValueStorageProvider(
dataDirAhead, dbAhead, dataStorageConfiguration, miningParameters), dataDirAhead, dbAhead, dataStorageConfiguration, miningParameters),
noOpMetricsSystem); noOpMetricsSystem,
miningParameters);
setupState( setupState(
blockCount, controllerAhead.getProtocolSchedule(), controllerAhead.getProtocolContext()); blockCount, controllerAhead.getProtocolSchedule(), controllerAhead.getProtocolContext());
@ -235,7 +236,8 @@ public final class RunnerTest {
dataDirBehind, dataDirBehind,
behindDbNodeKey, behindDbNodeKey,
new InMemoryKeyValueStorageProvider(), new InMemoryKeyValueStorageProvider(),
noOpMetricsSystem); noOpMetricsSystem,
miningParameters);
final EnodeURL aheadEnode = runnerAhead.getLocalEnode().get(); final EnodeURL aheadEnode = runnerAhead.getLocalEnode().get();
final EthNetworkConfig behindEthNetworkConfiguration = final EthNetworkConfig behindEthNetworkConfiguration =
@ -452,14 +454,15 @@ public final class RunnerTest {
final Path dataDir, final Path dataDir,
final NodeKey nodeKey, final NodeKey nodeKey,
final StorageProvider storageProvider, final StorageProvider storageProvider,
final ObservableMetricsSystem metricsSystem) { final ObservableMetricsSystem metricsSystem,
final MiningParameters miningParameters) {
return new MainnetBesuControllerBuilder() return new MainnetBesuControllerBuilder()
.genesisConfigFile(genesisConfig) .genesisConfigFile(genesisConfig)
.synchronizerConfiguration(syncConfig) .synchronizerConfiguration(syncConfig)
.ethProtocolConfiguration(EthProtocolConfiguration.defaultConfig()) .ethProtocolConfiguration(EthProtocolConfiguration.defaultConfig())
.dataDirectory(dataDir) .dataDirectory(dataDir)
.networkId(NETWORK_ID) .networkId(NETWORK_ID)
.miningParameters(MiningParameters.newDefault()) .miningParameters(miningParameters)
.nodeKey(nodeKey) .nodeKey(nodeKey)
.storageProvider(storageProvider) .storageProvider(storageProvider)
.metricsSystem(metricsSystem) .metricsSystem(metricsSystem)

@ -432,8 +432,7 @@ public abstract class JsonBlockImporterTest {
return createController(genesisConfigFile); return createController(genesisConfigFile);
} }
protected BesuController createController(final GenesisConfigFile genesisConfigFile) protected BesuController createController(final GenesisConfigFile genesisConfigFile) {
throws IOException {
return new BesuController.Builder() return new BesuController.Builder()
.fromGenesisConfig(genesisConfigFile, SyncMode.FAST) .fromGenesisConfig(genesisConfigFile, SyncMode.FAST)
.synchronizerConfiguration(SynchronizerConfiguration.builder().build()) .synchronizerConfiguration(SynchronizerConfiguration.builder().build())

@ -92,6 +92,7 @@ import org.hyperledger.besu.services.SecurityModuleServiceImpl;
import org.hyperledger.besu.services.StorageServiceImpl; import org.hyperledger.besu.services.StorageServiceImpl;
import org.hyperledger.besu.services.TransactionPoolValidatorServiceImpl; import org.hyperledger.besu.services.TransactionPoolValidatorServiceImpl;
import org.hyperledger.besu.services.TransactionSelectionServiceImpl; import org.hyperledger.besu.services.TransactionSelectionServiceImpl;
import org.hyperledger.besu.services.TransactionSimulationServiceImpl;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage; import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@ -573,6 +574,7 @@ public abstract class CommandTestAbstract {
rpcEndpointServiceImpl, rpcEndpointServiceImpl,
new TransactionSelectionServiceImpl(), new TransactionSelectionServiceImpl(),
new TransactionPoolValidatorServiceImpl(), new TransactionPoolValidatorServiceImpl(),
new TransactionSimulationServiceImpl(),
new BlockchainServiceImpl()); new BlockchainServiceImpl());
} }

@ -107,7 +107,7 @@ public class ValidatorContractController {
if (result.isSuccessful()) { if (result.isSuccessful()) {
final List<Type> decodedList = final List<Type> decodedList =
FunctionReturnDecoder.decode( FunctionReturnDecoder.decode(
result.getResult().getOutput().toHexString(), function.getOutputParameters()); result.result().getOutput().toHexString(), function.getOutputParameters());
if (decodedList.isEmpty()) { if (decodedList.isEmpty()) {
throw new IllegalStateException( throw new IllegalStateException(

@ -90,7 +90,7 @@ public abstract class AbstractEstimateGas implements JsonRpcMethod {
Math.pow(SUB_CALL_REMAINING_GAS_RATIO, operationTracer.getMaxDepth()); Math.pow(SUB_CALL_REMAINING_GAS_RATIO, operationTracer.getMaxDepth());
// and minimum gas remaining is necessary for some operation (additionalStipend) // and minimum gas remaining is necessary for some operation (additionalStipend)
final long gasStipend = operationTracer.getStipendNeeded(); final long gasStipend = operationTracer.getStipendNeeded();
final long gasUsedByTransaction = result.getResult().getEstimateGasUsedByTransaction(); final long gasUsedByTransaction = result.result().getEstimateGasUsedByTransaction();
return ((long) ((gasUsedByTransaction + gasStipend) * subCallMultiplier)); return ((long) ((gasUsedByTransaction + gasStipend) * subCallMultiplier));
} }
@ -123,7 +123,7 @@ public abstract class AbstractEstimateGas implements JsonRpcMethod {
JsonRpcErrorConverter.convertTransactionInvalidReason( JsonRpcErrorConverter.convertTransactionInvalidReason(
validationResult.getInvalidReason())); validationResult.getInvalidReason()));
} else { } else {
final TransactionProcessingResult resultTrx = result.getResult(); final TransactionProcessingResult resultTrx = result.result();
if (resultTrx != null && resultTrx.getRevertReason().isPresent()) { if (resultTrx != null && resultTrx.getRevertReason().isPresent()) {
return errorResponse( return errorResponse(
request, request,

@ -83,7 +83,7 @@ public class DebugTraceCall extends AbstractTraceCall {
final TransactionTrace transactionTrace = final TransactionTrace transactionTrace =
new TransactionTrace( new TransactionTrace(
result.getTransaction(), result.getResult(), tracer.getTraceFrames()); result.transaction(), result.result(), tracer.getTraceFrames());
return new DebugTraceTransactionResult(transactionTrace); return new DebugTraceTransactionResult(transactionTrace);
}); });

@ -117,7 +117,7 @@ public class EthCall extends AbstractBlockParameterOrBlockHashMethod {
JsonRpcErrorConverter.convertTransactionInvalidReason( JsonRpcErrorConverter.convertTransactionInvalidReason(
validationResult.getInvalidReason())); validationResult.getInvalidReason()));
} else { } else {
final TransactionProcessingResult resultTrx = result.getResult(); final TransactionProcessingResult resultTrx = result.result();
if (resultTrx != null && resultTrx.getRevertReason().isPresent()) { if (resultTrx != null && resultTrx.getRevertReason().isPresent()) {
return errorResponse( return errorResponse(
request, request,

@ -85,7 +85,7 @@ public class EthEstimateGas extends AbstractEstimateGas {
return errorResponse(requestContext, gasUsed.get()); return errorResponse(requestContext, gasUsed.get());
} }
var low = gasUsed.get().getResult().getEstimateGasUsedByTransaction(); var low = gasUsed.get().result().getEstimateGasUsedByTransaction();
var lowResult = var lowResult =
executeSimulation( executeSimulation(
blockHeader, blockHeader,

@ -73,7 +73,7 @@ public class TraceCall extends AbstractTraceCall {
final TransactionTrace transactionTrace = final TransactionTrace transactionTrace =
new TransactionTrace( new TransactionTrace(
result.getTransaction(), result.getResult(), tracer.getTraceFrames()); result.transaction(), result.result(), tracer.getTraceFrames());
final Block block = final Block block =
blockchainQueriesSupplier.get().getBlockchain().getChainHeadBlock(); blockchainQueriesSupplier.get().getBlockchain().getChainHeadBlock();

@ -166,7 +166,7 @@ public class TraceCallMany extends TraceCall implements JsonRpcMethod {
final TransactionTrace transactionTrace = final TransactionTrace transactionTrace =
new TransactionTrace( new TransactionTrace(
simulatorResult.getTransaction(), simulatorResult.getResult(), tracer.getTraceFrames()); simulatorResult.transaction(), simulatorResult.result(), tracer.getTraceFrames());
final Block block = blockchainQueriesSupplier.get().getBlockchain().getChainHeadBlock(); final Block block = blockchainQueriesSupplier.get().getBlockchain().getChainHeadBlock();

@ -102,7 +102,7 @@ public class TraceRawTransaction extends AbstractTraceByBlock implements JsonRpc
result -> { result -> {
final TransactionTrace transactionTrace = final TransactionTrace transactionTrace =
new TransactionTrace( new TransactionTrace(
result.getTransaction(), result.getResult(), tracer.getTraceFrames()); result.transaction(), result.result(), tracer.getTraceFrames());
final Optional<Block> maybeBlock = final Optional<Block> maybeBlock =
blockchainQueriesSupplier blockchainQueriesSupplier
.get() .get()

@ -197,7 +197,7 @@ public class EthCallTest {
final TransactionSimulatorResult result = mock(TransactionSimulatorResult.class); final TransactionSimulatorResult result = mock(TransactionSimulatorResult.class);
when(result.isSuccessful()).thenReturn(false); when(result.isSuccessful()).thenReturn(false);
when(result.getValidationResult()).thenReturn(ValidationResult.valid()); when(result.getValidationResult()).thenReturn(ValidationResult.valid());
when(result.getResult()).thenReturn(processingResult); when(result.result()).thenReturn(processingResult);
verify(transactionSimulator).process(any(), any(), any(), mapperCaptor.capture(), any()); verify(transactionSimulator).process(any(), any(), any(), mapperCaptor.capture(), any());
assertThat(mapperCaptor.getValue().apply(mock(MutableWorldState.class), Optional.of(result))) assertThat(mapperCaptor.getValue().apply(mock(MutableWorldState.class), Optional.of(result)))
.isEqualTo(Optional.of(expectedResponse)); .isEqualTo(Optional.of(expectedResponse));
@ -236,7 +236,7 @@ public class EthCallTest {
final TransactionSimulatorResult result = mock(TransactionSimulatorResult.class); final TransactionSimulatorResult result = mock(TransactionSimulatorResult.class);
when(result.isSuccessful()).thenReturn(false); when(result.isSuccessful()).thenReturn(false);
when(result.getValidationResult()).thenReturn(ValidationResult.valid()); when(result.getValidationResult()).thenReturn(ValidationResult.valid());
when(result.getResult()).thenReturn(processingResult); when(result.result()).thenReturn(processingResult);
verify(transactionSimulator).process(any(), any(), any(), mapperCaptor.capture(), any()); verify(transactionSimulator).process(any(), any(), any(), mapperCaptor.capture(), any());
assertThat(mapperCaptor.getValue().apply(mock(MutableWorldState.class), Optional.of(result))) assertThat(mapperCaptor.getValue().apply(mock(MutableWorldState.class), Optional.of(result)))
.isEqualTo(Optional.of(expectedResponse)); .isEqualTo(Optional.of(expectedResponse));
@ -277,7 +277,7 @@ public class EthCallTest {
final TransactionSimulatorResult result = mock(TransactionSimulatorResult.class); final TransactionSimulatorResult result = mock(TransactionSimulatorResult.class);
when(result.isSuccessful()).thenReturn(false); when(result.isSuccessful()).thenReturn(false);
when(result.getValidationResult()).thenReturn(ValidationResult.valid()); when(result.getValidationResult()).thenReturn(ValidationResult.valid());
when(result.getResult()).thenReturn(processingResult); when(result.result()).thenReturn(processingResult);
verify(transactionSimulator).process(any(), any(), any(), mapperCaptor.capture(), any()); verify(transactionSimulator).process(any(), any(), any(), mapperCaptor.capture(), any());
System.out.println(result); System.out.println(result);

@ -300,7 +300,7 @@ public class EthCreateAccessListTest {
when(mockResult.getEstimateGasUsedByTransaction()).thenReturn(estimateGas); when(mockResult.getEstimateGasUsedByTransaction()).thenReturn(estimateGas);
when(mockResult.getRevertReason()) when(mockResult.getRevertReason())
.thenReturn(isReverted ? Optional.of(Bytes.of(0)) : Optional.empty()); .thenReturn(isReverted ? Optional.of(Bytes.of(0)) : Optional.empty());
when(mockTxSimResult.getResult()).thenReturn(mockResult); when(mockTxSimResult.result()).thenReturn(mockResult);
when(mockTxSimResult.isSuccessful()).thenReturn(isSuccessful); when(mockTxSimResult.isSuccessful()).thenReturn(isSuccessful);
} }

@ -451,7 +451,7 @@ public class EthEstimateGasTest {
when(mockResult.getEstimateGasUsedByTransaction()).thenReturn(estimateGas); when(mockResult.getEstimateGasUsedByTransaction()).thenReturn(estimateGas);
when(mockResult.getRevertReason()).thenReturn(revertReason); when(mockResult.getRevertReason()).thenReturn(revertReason);
when(mockTxSimResult.getResult()).thenReturn(mockResult); when(mockTxSimResult.result()).thenReturn(mockResult);
when(mockTxSimResult.isSuccessful()).thenReturn(isSuccessful); when(mockTxSimResult.isSuccessful()).thenReturn(isSuccessful);
return mockTxSimResult; return mockTxSimResult;
} }

@ -195,6 +195,10 @@ public abstract class AbstractBlockTransactionSelectorTest {
return false; return false;
} }
protected Wei getMinGasPrice() {
return Wei.ONE;
}
protected ProcessableBlockHeader createBlock(final long gasLimit) { protected ProcessableBlockHeader createBlock(final long gasLimit) {
return createBlock(gasLimit, Wei.ONE); return createBlock(gasLimit, Wei.ONE);
} }

@ -204,4 +204,31 @@ public class TransactionProcessingResult
public Optional<Bytes> getRevertReason() { public Optional<Bytes> getRevertReason() {
return revertReason; return revertReason;
} }
@Override
public Optional<String> getInvalidReason() {
return (validationResult.isValid()
? Optional.empty()
: Optional.of(validationResult.getErrorMessage()));
}
@Override
public String toString() {
return "TransactionProcessingResult{"
+ "status="
+ status
+ ", estimateGasUsedByTransaction="
+ estimateGasUsedByTransaction
+ ", gasRemaining="
+ gasRemaining
+ ", logs="
+ logs
+ ", output="
+ output
+ ", validationResult="
+ validationResult
+ ", revertReason="
+ revertReason
+ '}';
}
} }

@ -199,18 +199,61 @@ public class CallParameter {
blobVersionedHashes); blobVersionedHashes);
} }
@Override
public String toString() {
return "CallParameter{"
+ "from="
+ from
+ ", to="
+ to
+ ", gasLimit="
+ gasLimit
+ ", maxPriorityFeePerGas="
+ maxPriorityFeePerGas.map(Wei::toHumanReadableString).orElse("N/A")
+ ", maxFeePerGas="
+ maxFeePerGas.map(Wei::toHumanReadableString).orElse("N/A")
+ ", maxFeePerBlobGas="
+ maxFeePerBlobGas.map(Wei::toHumanReadableString).orElse("N/A")
+ ", gasPrice="
+ (gasPrice != null ? gasPrice.toHumanReadableString() : "N/A")
+ ", value="
+ (value != null ? value.toHumanReadableString() : "N/A")
+ ", payloadSize="
+ (payload != null ? payload.size() : "null")
+ ", accessListSize="
+ accessList.map(List::size)
+ ", blobVersionedHashesSize="
+ blobVersionedHashes.map(List::size)
+ '}';
}
public static CallParameter fromTransaction(final Transaction tx) { public static CallParameter fromTransaction(final Transaction tx) {
return new CallParameter( return new CallParameter(
tx.getSender(), tx.getSender(),
tx.getTo().orElseGet(() -> null), tx.getTo().orElse(null),
tx.getGasLimit(), tx.getGasLimit(),
Wei.fromQuantity(tx.getGasPrice().orElseGet(() -> Wei.ZERO)), tx.getGasPrice().orElse(Wei.ZERO),
Optional.of(Wei.fromQuantity(tx.getMaxPriorityFeePerGas().orElseGet(() -> Wei.ZERO))), tx.getMaxPriorityFeePerGas(),
tx.getMaxFeePerGas(), tx.getMaxFeePerGas(),
Wei.fromQuantity(tx.getValue()), tx.getValue(),
tx.getPayload(), tx.getPayload(),
tx.getAccessList(), tx.getAccessList(),
tx.getMaxFeePerBlobGas(), tx.getMaxFeePerBlobGas(),
tx.getVersionedHashes()); tx.getVersionedHashes());
} }
public static CallParameter fromTransaction(final org.hyperledger.besu.datatypes.Transaction tx) {
return new CallParameter(
tx.getSender(),
tx.getTo().orElse(null),
tx.getGasLimit(),
tx.getGasPrice().map(Wei::fromQuantity).orElse(Wei.ZERO),
tx.getMaxPriorityFeePerGas().map(Wei::fromQuantity),
tx.getMaxFeePerGas().map(Wei::fromQuantity),
Wei.fromQuantity(tx.getValue()),
tx.getPayload(),
tx.getAccessList(),
tx.getMaxFeePerBlobGas().map(Wei::fromQuantity),
tx.getVersionedHashes());
}
} }

@ -106,7 +106,21 @@ public class TransactionSimulator {
header); header);
} }
public Optional<TransactionSimulatorResult> process(
final CallParameter callParams,
final TransactionValidationParams transactionValidationParams,
final OperationTracer operationTracer,
final BlockHeader blockHeader) {
return process(
callParams,
transactionValidationParams,
operationTracer,
(mutableWorldState, transactionSimulatorResult) -> transactionSimulatorResult,
blockHeader);
}
public Optional<TransactionSimulatorResult> processAtHead(final CallParameter callParams) { public Optional<TransactionSimulatorResult> processAtHead(final CallParameter callParams) {
final var chainHeadHash = blockchain.getChainHeadHash();
return process( return process(
callParams, callParams,
ImmutableTransactionValidationParams.builder() ImmutableTransactionValidationParams.builder()
@ -115,7 +129,10 @@ public class TransactionSimulator {
.build(), .build(),
OperationTracer.NO_TRACING, OperationTracer.NO_TRACING,
(mutableWorldState, transactionSimulatorResult) -> transactionSimulatorResult, (mutableWorldState, transactionSimulatorResult) -> transactionSimulatorResult,
blockchain.getChainHeadHeader()); blockchain
.getBlockHeader(chainHeadHash)
.or(() -> blockchain.getBlockHeaderSafe(chainHeadHash))
.orElse(null));
} }
/** /**

@ -18,22 +18,10 @@ import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.mainnet.ValidationResult; import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult; import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import java.util.Objects;
import com.google.common.annotations.VisibleForTesting;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
public class TransactionSimulatorResult { public record TransactionSimulatorResult(
Transaction transaction, TransactionProcessingResult result) {
private final Transaction transaction;
private final TransactionProcessingResult result;
@VisibleForTesting
public TransactionSimulatorResult(
final Transaction transaction, final TransactionProcessingResult result) {
this.transaction = transaction;
this.result = result;
}
public boolean isSuccessful() { public boolean isSuccessful() {
return result.isSuccessful(); return result.isSuccessful();
@ -54,40 +42,4 @@ public class TransactionSimulatorResult {
public ValidationResult<TransactionInvalidReason> getValidationResult() { public ValidationResult<TransactionInvalidReason> getValidationResult() {
return result.getValidationResult(); return result.getValidationResult();
} }
public TransactionProcessingResult getResult() {
return result;
}
public Transaction getTransaction() {
return transaction;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final TransactionSimulatorResult that = (TransactionSimulatorResult) o;
return Objects.equals(transaction, that.transaction) && Objects.equals(result, that.result);
}
@Override
public int hashCode() {
return Objects.hash(transaction, result);
}
@Override
public String toString() {
return "TransactionSimulatorResult{"
+ "transaction="
+ transaction
+ ", "
+ "result="
+ result
+ "}";
}
} }

@ -72,12 +72,12 @@ public class MainnetTransactionValidatorTest {
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM = private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
Suppliers.memoize(SignatureAlgorithmFactory::getInstance); Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
private static final KeyPair senderKeys = SIGNATURE_ALGORITHM.get().generateKeyPair(); protected static final KeyPair senderKeys = SIGNATURE_ALGORITHM.get().generateKeyPair();
private static final TransactionValidationParams transactionValidationParams = private static final TransactionValidationParams transactionValidationParams =
processingBlockParams; processingBlockParams;
@Mock private GasCalculator gasCalculator; @Mock protected GasCalculator gasCalculator;
private final Transaction basicTransaction = private final Transaction basicTransaction =
new TransactionTestFixture() new TransactionTestFixture()

@ -32,7 +32,6 @@ import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture; import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason; import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.Optional; import java.util.Optional;
@ -42,7 +41,6 @@ import com.google.common.base.Suppliers;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
@ -51,7 +49,6 @@ public class PermissionTransactionValidatorTest extends MainnetTransactionValida
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM = private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
Suppliers.memoize(SignatureAlgorithmFactory::getInstance); Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
private static final KeyPair senderKeys = SIGNATURE_ALGORITHM.get().generateKeyPair(); private static final KeyPair senderKeys = SIGNATURE_ALGORITHM.get().generateKeyPair();
@Mock private GasCalculator gasCalculator;
private final Transaction basicTransaction = private final Transaction basicTransaction =
new TransactionTestFixture() new TransactionTestFixture()

@ -71,7 +71,7 @@ public class NodeSmartContractPermissioningController
transactionSimulator.processAtHead(callParams); transactionSimulator.processAtHead(callParams);
if (result.isPresent()) { if (result.isPresent()) {
switch (result.get().getResult().getStatus()) { switch (result.get().result().getStatus()) {
case INVALID: case INVALID:
throw new IllegalStateException("Permissioning transaction found to be Invalid"); throw new IllegalStateException("Permissioning transaction found to be Invalid");
case FAILED: case FAILED:

@ -114,7 +114,7 @@ public class NodeSmartContractV2PermissioningController
} }
private boolean parseResult(final TransactionSimulatorResult result) { private boolean parseResult(final TransactionSimulatorResult result) {
switch (result.getResult().getStatus()) { switch (result.result().getStatus()) {
case INVALID: case INVALID:
throw new IllegalStateException("Invalid node permissioning smart contract call"); throw new IllegalStateException("Invalid node permissioning smart contract call");
case FAILED: case FAILED:

@ -134,7 +134,7 @@ public class TransactionSmartContractPermissioningController
transactionSimulator.processAtHead(callParams); transactionSimulator.processAtHead(callParams);
if (result.isPresent()) { if (result.isPresent()) {
switch (result.get().getResult().getStatus()) { switch (result.get().result().getStatus()) {
case INVALID: case INVALID:
throw new IllegalStateException( throw new IllegalStateException(
"Transaction permissioning transaction found to be Invalid"); "Transaction permissioning transaction found to be Invalid");

@ -69,7 +69,7 @@ Calculated : ${currentHash}
tasks.register('checkAPIChanges', FileStateChecker) { tasks.register('checkAPIChanges', FileStateChecker) {
description = "Checks that the API for the Plugin-API project does not change without deliberate thought" description = "Checks that the API for the Plugin-API project does not change without deliberate thought"
files = sourceSets.main.allJava.files files = sourceSets.main.allJava.files
knownHash = 'B/pzTaARYvd/T9WtAXtX6vFbxoK5u82GigByKD0YP6M=' knownHash = 'ytjNiSzw9IR8YHyO4ikmqRTg1GTWkCX9QiQtwq2dRSg='
} }
check.dependsOn('checkAPIChanges') check.dependsOn('checkAPIChanges')

@ -88,4 +88,11 @@ public interface TransactionProcessingResult {
* @return the revert reason. * @return the revert reason.
*/ */
Optional<Bytes> getRevertReason(); Optional<Bytes> getRevertReason();
/**
* Return the reason why the transaction is invalid or empty if the transaction is successful
*
* @return the optional invalid reason as a string
*/
Optional<String> getInvalidReason();
} }

@ -0,0 +1,54 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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.plugin.data;
import org.hyperledger.besu.datatypes.Transaction;
/**
* TransactionSimulationResult
*
* @param transaction tx
* @param result res
*/
public record TransactionSimulationResult(
Transaction transaction, TransactionProcessingResult result) {
/**
* Was the simulation successful?
*
* @return boolean
*/
public boolean isSuccessful() {
return result.isSuccessful();
}
/**
* Was the transaction invalid?
*
* @return invalid
*/
public boolean isInvalid() {
return result.isInvalid();
}
/**
* Estimated gas used by the transaction
*
* @return estimated gas used
*/
public long getGasEstimate() {
return transaction.getGasLimit() - result.getGasRemaining();
}
}

@ -0,0 +1,42 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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.plugin.services;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Transaction;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.plugin.Unstable;
import org.hyperledger.besu.plugin.data.TransactionSimulationResult;
import java.util.Optional;
/** Transaction simulation service interface */
@Unstable
public interface TransactionSimulationService extends BesuService {
/**
* Simulate transaction execution at the block identified by the hash
*
* @param transaction tx
* @param blockHash the hash of the block
* @param operationTracer the tracer
* @param isAllowExceedingBalance should ignore the sender balance during the simulation?
* @return the result of the simulation
*/
Optional<TransactionSimulationResult> simulate(
Transaction transaction,
Hash blockHash,
OperationTracer operationTracer,
boolean isAllowExceedingBalance);
}

@ -242,7 +242,13 @@ public class JsonTestParameters<S, T> {
return generate(getFilteredFiles(paths)); return generate(getFilteredFiles(paths));
} }
private Collection<Object[]> generate(final Collection<File> filteredFiles) { /**
* Generate collection.
*
* @param filteredFiles the filtered files
* @return the collection
*/
public Collection<Object[]> generate(final Collection<File> filteredFiles) {
checkState(generator != null, "Missing generator function"); checkState(generator != null, "Missing generator function");
final Collector<T> collector = final Collector<T> collector =

Loading…
Cancel
Save