Merge branch 'main' into zkbesu

# Conflicts:
#	.github/workflows/pre-review.yml
#	.github/workflows/reference-tests.yml
This commit is contained in:
Fabio Di Fabio
2025-02-12 16:05:20 +01:00
46 changed files with 2408 additions and 379 deletions

View File

@@ -15,7 +15,11 @@
- Proof of Work consensus
- Fast Sync
### Additions and Improvements
- Add TLS/mTLS options and configure the GraphQL HTTP service[#7910](https://github.com/hyperledger/besu/pull/7910)
- Allow plugins to propose transactions during block creation [#8268](https://github.com/hyperledger/besu/pull/8268)
### Bug fixes
- Upgrade Netty to version 4.1.118 to fix CVE-2025-24970 [#8275](https://github.com/hyperledger/besu/pull/8275)
- Add missing RPC method `debug_accountRange` to `RpcMethod.java` and implemented its handler. [#8153](https://github.com/hyperledger/besu/issues/8153)
## 25.2.0
@@ -37,6 +41,7 @@
- Add a tx selector to skip txs from the same sender after the first not selected [#8216](https://github.com/hyperledger/besu/pull/8216)
- `rpc-gas-cap` default value has changed from 0 (unlimited) to 50M [#8251](https://github.com/hyperledger/besu/issues/8251)
#### Prague
- Add timestamps to enable Prague hardfork on Sepolia and Holesky test networks [#8163](https://github.com/hyperledger/besu/pull/8163)
- Update system call addresses to match [devnet-6](https://github.com/ethereum/execution-spec-tests/releases/) values [#8209](https://github.com/hyperledger/besu/issues/8209)
@@ -74,7 +79,7 @@
- Retrieve all transaction receipts for a block in one request [#6646](https://github.com/hyperledger/besu/pull/6646)
- Implement EIP-7840: Add blob schedule to config files [#8042](https://github.com/hyperledger/besu/pull/8042)
- 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)
- Improve debug_traceBlock calls performance and reduce output size [#8103](https://github.com/hyperledger/besu/pull/8103)
- 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)
- Default target-gas-limit to 36M for holesky [#8125](https://github.com/hyperledger/besu/pull/8125)
@@ -82,7 +87,7 @@
- Add nonce to transaction call object [#8139](https://github.com/hyperledger/besu/pull/8139)
### Bug fixes
- Fix serialization of state overrides when `movePrecompileToAddress` is present [#8204](https://github.com/hyperledger/besu/pull/8024)
- Fix serialization of state overrides when `movePrecompileToAddress` is present [#8024](https://github.com/hyperledger/besu/pull/8024)
- Revise the approach for setting level_compaction_dynamic_level_bytes RocksDB configuration option [#8037](https://github.com/hyperledger/besu/pull/8037)
- Fix possible incomplete txpool restore from dump file [#7991](https://github.com/hyperledger/besu/pull/7991)
@@ -93,14 +98,14 @@ This is an optional hotfix to address serialization of state overrides parameter
There is no need to upgrade from 24.12.0 (or 24.12.1) to this release if you are not yet using this functionality.
### Bug fixes
- Fix serialization of state overrides when `movePrecompileToAddress` is present [#8204](https://github.com/hyperledger/besu/pull/8024)
- Fix serialization of state overrides when `movePrecompileToAddress` is present [#8024](https://github.com/hyperledger/besu/pull/8024)
## 24.12.1 Hotfix
This is a hotfix to address publishing besu maven artifacts. There are no issues with 24.12.0 other than incomplete artifact publishing, and there is no functional difference between 24.12.0 and 24.12.1 release binaries.
### Bug fixes
- Fix BOM pom publication to Artifactory [#8201](https://github.com/hyperledger/besu/pull/8021)
- Fix BOM pom publication to Artifactory [#8021](https://github.com/hyperledger/besu/pull/8021)
## 24.12.0

View File

@@ -57,6 +57,36 @@ public class GraphQlOptions {
private final CorsAllowedOriginsProperty graphQLHttpCorsAllowedOrigins =
new CorsAllowedOriginsProperty();
@CommandLine.Option(
names = {"--graphql-tls-enabled"},
description = "Enable TLS for GraphQL HTTP service")
private Boolean graphqlTlsEnabled = false;
@CommandLine.Option(
names = {"--graphql-tls-keystore-file"},
description = "Path to the TLS keystore file for GraphQL HTTP service")
private String graphqlTlsKeystoreFile;
@CommandLine.Option(
names = {"--graphql-tls-keystore-password-file"},
description = "Path to the file containing the password for the TLS keystore")
private String graphqlTlsKeystorePasswordFile;
@CommandLine.Option(
names = {"--graphql-mtls-enabled"},
description = "Enable mTLS for GraphQL HTTP service")
private Boolean graphqlMtlsEnabled = false;
@CommandLine.Option(
names = {"--graphql-tls-truststore-file"},
description = "Path to the TLS truststore file for GraphQL HTTP service")
private String graphqlTlsTruststoreFile;
@CommandLine.Option(
names = {"--graphql-tls-truststore-password-file"},
description = "Path to the file containing the password for the TLS truststore")
private String graphqlTlsTruststorePasswordFile;
/** Default constructor */
public GraphQlOptions() {}
@@ -72,7 +102,28 @@ public class GraphQlOptions {
commandLine,
"--graphql-http-enabled",
!isGraphQLHttpEnabled,
asList("--graphql-http-cors-origins", "--graphql-http-host", "--graphql-http-port"));
asList(
"--graphql-http-cors-origins",
"--graphql-http-host",
"--graphql-http-port",
"--graphql-tls-enabled"));
CommandLineUtils.checkOptionDependencies(
logger,
commandLine,
"--graphql-tls-enabled",
!graphqlTlsEnabled,
asList(
"--graphql-tls-keystore-file",
"--graphql-tls-keystore-password-file",
"--graphql-mtls-enabled"));
CommandLineUtils.checkOptionDependencies(
logger,
commandLine,
"--graphql-mtls-enabled",
!graphqlMtlsEnabled,
asList("--graphql-tls-truststore-file", "--graphql-tls-truststore-password-file"));
}
/**
@@ -93,6 +144,13 @@ public class GraphQlOptions {
graphQLConfiguration.setHostsAllowlist(hostsAllowlist);
graphQLConfiguration.setCorsAllowedDomains(graphQLHttpCorsAllowedOrigins);
graphQLConfiguration.setHttpTimeoutSec(timoutSec);
graphQLConfiguration.setTlsEnabled(graphqlTlsEnabled);
graphQLConfiguration.setTlsKeyStorePath(graphqlTlsKeystoreFile);
graphQLConfiguration.setTlsKeyStorePasswordFile(graphqlTlsKeystorePasswordFile);
graphQLConfiguration.setMtlsEnabled(graphqlMtlsEnabled);
graphQLConfiguration.setTlsTrustStorePath(graphqlTlsTruststoreFile);
graphQLConfiguration.setTlsTrustStorePasswordFile(graphqlTlsTruststorePasswordFile);
return graphQLConfiguration;
}

View File

@@ -14,11 +14,14 @@
*/
package org.hyperledger.besu.services;
import static com.google.common.base.Preconditions.checkState;
import org.hyperledger.besu.plugin.data.ProcessableBlockHeader;
import org.hyperledger.besu.plugin.services.TransactionSelectionService;
import org.hyperledger.besu.plugin.services.txselection.BlockTransactionSelectionService;
import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelector;
import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelectorFactory;
import java.util.Optional;
import org.hyperledger.besu.plugin.services.txselection.SelectorsStateManager;
/** The Transaction Selection service implementation. */
public class TransactionSelectionServiceImpl implements TransactionSelectionService {
@@ -26,18 +29,27 @@ public class TransactionSelectionServiceImpl implements TransactionSelectionServ
/** Default Constructor. */
public TransactionSelectionServiceImpl() {}
private Optional<PluginTransactionSelectorFactory> factory = Optional.empty();
private PluginTransactionSelectorFactory factory = PluginTransactionSelectorFactory.NO_OP_FACTORY;
@Override
public PluginTransactionSelector createPluginTransactionSelector() {
return factory
.map(PluginTransactionSelectorFactory::create)
.orElse(PluginTransactionSelector.ACCEPT_ALL);
public PluginTransactionSelector createPluginTransactionSelector(
final SelectorsStateManager selectorsStateManager) {
return factory.create(selectorsStateManager);
}
@Override
public void selectPendingTransactions(
final BlockTransactionSelectionService selectionService,
final ProcessableBlockHeader pendingBlockHeader) {
factory.selectPendingTransactions(selectionService, pendingBlockHeader);
}
@Override
public void registerPluginTransactionSelectorFactory(
final PluginTransactionSelectorFactory pluginTransactionSelectorFactory) {
factory = Optional.ofNullable(pluginTransactionSelectorFactory);
checkState(
factory == PluginTransactionSelectorFactory.NO_OP_FACTORY,
"PluginTransactionSelectorFactory was already registered");
factory = pluginTransactionSelectorFactory;
}
}

View File

@@ -109,6 +109,12 @@ graphql-http-enabled=false
graphql-http-host="6.7.8.9"
graphql-http-port=6789
graphql-http-cors-origins=["none"]
graphql-tls-enabled=false
graphql-tls-keystore-file="none.pfx"
graphql-tls-keystore-password-file="none.passwd"
graphql-mtls-enabled=false
graphql-tls-truststore-file="none.pfx"
graphql-tls-truststore-password-file="none.passwd"
# WebSockets API
rpc-ws-enabled=false

View File

@@ -80,7 +80,7 @@ public interface HardforkId {
/** Cancun + EOF fork. */
CANCUN_EOF(false, "Cancun + EOF"),
/** Prague fork. */
PRAGUE(false, "Prague"),
PRAGUE(true, "Prague"),
/** Osaka fork. */
OSAKA(false, "Osaka"),
/** Amsterdam fork. */

View File

@@ -18,6 +18,9 @@ import static com.google.common.base.Preconditions.checkNotNull;
import org.hyperledger.besu.ethereum.api.handlers.TimeoutOptions;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
@@ -44,6 +47,13 @@ public class GraphQLConfiguration {
private List<String> hostsAllowlist = Arrays.asList("localhost", DEFAULT_GRAPHQL_HTTP_HOST);
private long httpTimeoutSec = TimeoutOptions.defaultOptions().getTimeoutSeconds();
private String tlsKeyStorePath;
private String tlsKeyStorePasswordFile;
private String tlsTrustStorePath;
private String tlsTrustStorePasswordFile;
private boolean tlsEnabled;
private boolean mtlsEnabled;
/**
* Creates a default configuration for GraphQL.
*
@@ -174,6 +184,120 @@ public class GraphQLConfiguration {
this.httpTimeoutSec = httpTimeoutSec;
}
/**
* Retrieves the TLS key store path.
*
* @return the TLS key store path
*/
public String getTlsKeyStorePath() {
return tlsKeyStorePath;
}
/**
* Sets the TLS key store path.
*
* @param tlsKeyStorePath the path to the TLS key store
*/
public void setTlsKeyStorePath(final String tlsKeyStorePath) {
this.tlsKeyStorePath = tlsKeyStorePath;
}
/**
* Retrieves the TLS key store password.
*
* @return the TLS key store password
* @throws Exception if an error occurs while reading the password file
*/
public String getTlsKeyStorePassword() throws Exception {
return new String(
Files.readAllBytes(Paths.get(tlsKeyStorePasswordFile)), Charset.defaultCharset())
.trim();
}
/**
* Sets the TLS key store password file.
*
* @param tlsKeyStorePasswordFile the path to the TLS key store password file
*/
public void setTlsKeyStorePasswordFile(final String tlsKeyStorePasswordFile) {
this.tlsKeyStorePasswordFile = tlsKeyStorePasswordFile;
}
/**
* Retrieves the TLS trust store path.
*
* @return the TLS trust store path
*/
public String getTlsTrustStorePath() {
return tlsTrustStorePath;
}
/**
* Sets the TLS trust store path.
*
* @param tlsTrustStorePath the path to the TLS trust store
*/
public void setTlsTrustStorePath(final String tlsTrustStorePath) {
this.tlsTrustStorePath = tlsTrustStorePath;
}
/**
* Retrieves the TLS trust store password.
*
* @return the TLS trust store password
* @throws Exception if an error occurs while reading the password file
*/
public String getTlsTrustStorePassword() throws Exception {
return new String(
Files.readAllBytes(Paths.get(tlsTrustStorePasswordFile)), Charset.defaultCharset())
.trim();
}
/**
* Sets the TLS trust store password file.
*
* @param tlsTrustStorePasswordFile the path to the TLS trust store password file
*/
public void setTlsTrustStorePasswordFile(final String tlsTrustStorePasswordFile) {
this.tlsTrustStorePasswordFile = tlsTrustStorePasswordFile;
}
/**
* Retrieves the TLS enabled status.
*
* @return true if TLS is enabled, false otherwise
*/
public boolean isTlsEnabled() {
return tlsEnabled;
}
/**
* Sets the TLS enabled status.
*
* @param tlsEnabled the status to set. true to enable TLS, false to disable it
*/
public void setTlsEnabled(final boolean tlsEnabled) {
this.tlsEnabled = tlsEnabled;
}
/**
* Retrieves the mTLS enabled status.
*
* @return true if mTLS is enabled, false otherwise
*/
public boolean isMtlsEnabled() {
return mtlsEnabled;
}
/**
* Sets the mTLS enabled status.
*
* @param mtlsEnabled the status to set. true to enable mTLS, false to disable it
*/
public void setMtlsEnabled(final boolean mtlsEnabled) {
this.mtlsEnabled = mtlsEnabled;
}
@Override
public String toString() {
return MoreObjects.toStringHelper(this)

View File

@@ -51,6 +51,7 @@ import graphql.GraphQLError;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.http.ClientAuth;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerOptions;
@@ -60,6 +61,7 @@ import io.vertx.core.json.DecodeException;
import io.vertx.core.json.Json;
import io.vertx.core.json.jackson.JacksonCodec;
import io.vertx.core.net.HostAndPort;
import io.vertx.core.net.JksOptions;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler;
@@ -147,13 +149,43 @@ public class GraphQLHttpService {
public CompletableFuture<?> start() {
LOG.info("Starting GraphQL HTTP service on {}:{}", config.getHost(), config.getPort());
// Create the HTTP server and a router object.
httpServer =
vertx.createHttpServer(
new HttpServerOptions()
.setHost(config.getHost())
.setPort(config.getPort())
.setHandle100ContinueAutomatically(true)
.setCompressionSupported(true));
HttpServerOptions options =
new HttpServerOptions()
.setHost(config.getHost())
.setPort(config.getPort())
.setHandle100ContinueAutomatically(true)
.setCompressionSupported(true);
if (config.isTlsEnabled()) {
try {
options
.setSsl(true)
.setKeyCertOptions(
new JksOptions()
.setPath(config.getTlsKeyStorePath())
.setPassword(config.getTlsKeyStorePassword()));
} catch (Exception e) {
LOG.error("Failed to get TLS keystore password", e);
return CompletableFuture.failedFuture(e);
}
if (config.isMtlsEnabled()) {
try {
options
.setTrustOptions(
new JksOptions()
.setPath(config.getTlsTrustStorePath())
.setPassword(config.getTlsTrustStorePassword()))
.setClientAuth(ClientAuth.REQUIRED);
} catch (Exception e) {
LOG.error("Failed to get TLS truststore password", e);
return CompletableFuture.failedFuture(e);
}
}
}
LOG.info("Options {}", options);
httpServer = vertx.createHttpServer(options);
// Handle graphql http requests
final Router router = Router.router(vertx);
@@ -303,7 +335,8 @@ public class GraphQLHttpService {
if (httpServer == null) {
return "";
}
return NetworkUtility.urlForSocketAddress("http", socketAddress());
String scheme = config.isTlsEnabled() ? "https" : "http";
return NetworkUtility.urlForSocketAddress(scheme, socketAddress());
}
// Empty Get/Post requests to / will be redirected to /graphql using 308 Permanent Redirect

View File

@@ -33,6 +33,7 @@ public enum RpcMethod {
CLIQUE_PROPOSE("clique_propose"),
CLIQUE_GET_SIGNER_METRICS("clique_getSignerMetrics"),
DEBUG_ACCOUNT_AT("debug_accountAt"),
DEBUG_ACCOUNT_RANGE("debug_accountRange"),
DEBUG_METRICS("debug_metrics"),
DEBUG_RESYNC_WORLDSTATE("debug_resyncWorldState"),
DEBUG_SET_HEAD("debug_setHead"),

View File

@@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.exception.InvalidJsonRpcParameters;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.BlockParameterOrBlockHash;
@@ -52,9 +53,7 @@ public class DebugAccountRange implements JsonRpcMethod {
@Override
public String getName() {
// TODO(shemnon) 5229b899 is the last stable commit of retesteth, after this they rename the
// method to just "debug_accountRange". Once the tool is stable we will support the new name.
return "debug_accountRange";
return RpcMethod.DEBUG_ACCOUNT_RANGE.getMethodName();
}
@Override

View File

@@ -0,0 +1,490 @@
/*
* 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.api.graphql;
import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.api.query.BlockWithMetadata;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.api.query.TransactionWithMetadata;
import org.hyperledger.besu.ethereum.blockcreation.PoWMiningCoordinator;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Synchronizer;
import org.hyperledger.besu.ethereum.eth.EthProtocol;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
import org.hyperledger.besu.testutil.BlockTestUtil;
import java.io.File;
import java.io.FileOutputStream;
import java.io.Writer;
import java.net.InetSocketAddress;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import graphql.GraphQL;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import io.vertx.core.Vertx;
import io.vertx.core.json.JsonObject;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import org.apache.tuweni.bytes.Bytes;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.mockito.Mockito;
public class GraphQLHttpsServiceTest {
// this tempDir is deliberately static
@TempDir private static Path folder;
private static final Vertx vertx = Vertx.vertx();
private static GraphQLHttpService service;
private static OkHttpClient client;
private static String baseUrl;
protected static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
protected static final MediaType GRAPHQL = MediaType.parse("application/graphql; charset=utf-8");
private static BlockchainQueries blockchainQueries;
private static GraphQL graphQL;
private static Map<GraphQLContextType, Object> graphQlContextMap;
private static PoWMiningCoordinator miningCoordinatorMock;
private final GraphQLTestHelper testHelper = new GraphQLTestHelper();
// Generate a self-signed certificate
private static SelfSignedCertificate ssc;
private static SelfSignedCertificate clientSsc;
@BeforeAll
public static void initServerAndClient() throws Exception {
blockchainQueries = Mockito.mock(BlockchainQueries.class);
final Synchronizer synchronizer = Mockito.mock(Synchronizer.class);
graphQL = Mockito.mock(GraphQL.class);
ssc = new SelfSignedCertificate();
clientSsc = new SelfSignedCertificate();
miningCoordinatorMock = Mockito.mock(PoWMiningCoordinator.class);
graphQlContextMap =
Map.of(
GraphQLContextType.BLOCKCHAIN_QUERIES,
blockchainQueries,
GraphQLContextType.TRANSACTION_POOL,
Mockito.mock(TransactionPool.class),
GraphQLContextType.MINING_COORDINATOR,
miningCoordinatorMock,
GraphQLContextType.SYNCHRONIZER,
synchronizer);
final Set<Capability> supportedCapabilities = new HashSet<>();
supportedCapabilities.add(EthProtocol.ETH62);
supportedCapabilities.add(EthProtocol.ETH63);
final GraphQLDataFetchers dataFetchers = new GraphQLDataFetchers(supportedCapabilities);
graphQL = GraphQLProvider.buildGraphQL(dataFetchers);
service = createGraphQLHttpService();
service.start().join();
// Build an OkHttp client.
client = createHttpClientforMtls();
baseUrl = service.url() + "/graphql/";
}
public static OkHttpClient createHttpClientforMtls() throws Exception {
// Create a temporary truststore file
File truststoreFile = File.createTempFile("truststore", ".jks");
truststoreFile.deleteOnExit();
// Create a PKCS12 truststore and load the server's certificate
KeyStore trustStore = KeyStore.getInstance("JKS");
trustStore.load(null, null);
trustStore.setCertificateEntry("alias", ssc.cert());
// Save the truststore to the temporary file
try (FileOutputStream fos = new FileOutputStream(truststoreFile)) {
trustStore.store(fos, "password".toCharArray());
}
// Create TrustManagerFactory
TrustManagerFactory trustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStore);
// Get TrustManagers
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
// Create a temporary keystore file
File keystoreFile = File.createTempFile("keystore", ".jks");
keystoreFile.deleteOnExit();
// Create a PKCS12 keystore and load the client's certificate
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(null, "password".toCharArray());
keyStore.setKeyEntry(
"alias",
clientSsc.key(),
"password".toCharArray(),
new java.security.cert.Certificate[] {clientSsc.cert()});
// Save the keystore to the temporary file
try (FileOutputStream fos = new FileOutputStream(keystoreFile)) {
keyStore.store(fos, "password".toCharArray());
}
// Create KeyManagerFactory
KeyManagerFactory keyManagerFactory =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, "password".toCharArray());
// Get KeyManagers
KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
// Initialize SSLContext
SSLContext sslContext = SSLContext.getInstance("TLS");
// Obtain a SecureRandom instance
SecureRandom secureRandom = SecureRandom.getInstanceStrong();
// Initialize SSLContext
sslContext.init(keyManagers, trustManagers, secureRandom);
if (!(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException(
"Unexpected default trust managers: " + Arrays.toString(trustManagers));
}
// Create OkHttpClient with custom SSLSocketFactory and TrustManager
return new OkHttpClient.Builder()
.sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustManagers[0])
.hostnameVerifier((hostname, session) -> "localhost".equals(hostname))
.followRedirects(false)
.build();
}
private static GraphQLHttpService createGraphQLHttpService(final GraphQLConfiguration config)
throws Exception {
return new GraphQLHttpService(
vertx, folder, config, graphQL, graphQlContextMap, Mockito.mock(EthScheduler.class));
}
private static GraphQLHttpService createGraphQLHttpService() throws Exception {
return new GraphQLHttpService(
vertx,
folder,
createGraphQLConfig(),
graphQL,
graphQlContextMap,
Mockito.mock(EthScheduler.class));
}
private static GraphQLConfiguration createGraphQLConfig() throws Exception {
final GraphQLConfiguration config = GraphQLConfiguration.createDefault();
// Create a temporary keystore file
File keystoreFile = File.createTempFile("keystore", ".jks");
keystoreFile.deleteOnExit();
// Create a PKCS12 keystore and load the self-signed certificate
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(null, "password".toCharArray());
keyStore.setKeyEntry(
"alias",
ssc.key(),
"password".toCharArray(),
new java.security.cert.Certificate[] {ssc.cert()});
// Save the keystore to the temporary file
FileOutputStream fos = new FileOutputStream(keystoreFile);
keyStore.store(fos, "password".toCharArray());
// Create a temporary password file
File keystorePasswordFile = File.createTempFile("keystorePassword", ".txt");
keystorePasswordFile.deleteOnExit();
try (Writer writer =
Files.newBufferedWriter(keystorePasswordFile.toPath(), Charset.defaultCharset())) {
writer.write("password");
}
// Create a temporary truststore file
File truststoreFile = File.createTempFile("truststore", ".jks");
truststoreFile.deleteOnExit();
// Create a JKS truststore and load the client's certificate
KeyStore trustStore = KeyStore.getInstance("JKS");
trustStore.load(null, "password".toCharArray());
trustStore.setCertificateEntry("clientAlias", clientSsc.cert());
// Save the truststore to the temporary file
try (FileOutputStream fos2 = new FileOutputStream(truststoreFile)) {
trustStore.store(fos2, "password".toCharArray());
}
config.setPort(0);
config.setTlsEnabled(true);
config.setTlsKeyStorePath(keystoreFile.getAbsolutePath());
config.setTlsKeyStorePasswordFile(keystorePasswordFile.getAbsolutePath());
config.setMtlsEnabled(true);
config.setTlsTrustStorePath(truststoreFile.getAbsolutePath());
config.setTlsTrustStorePasswordFile(keystorePasswordFile.getAbsolutePath());
return config;
}
@BeforeAll
public static void setupConstants() {
final URL blocksUrl = BlockTestUtil.getTestBlockchainUrl();
final URL genesisJsonUrl = BlockTestUtil.getTestGenesisUrl();
Assertions.assertThat(blocksUrl).isNotNull();
Assertions.assertThat(genesisJsonUrl).isNotNull();
}
/** Tears down the HTTP server. */
@AfterAll
public static void shutdownServer() {
client.dispatcher().executorService().shutdown();
client.connectionPool().evictAll();
service.stop().join();
vertx.close();
}
@Test
public void invalidCallToStart() {
service
.start()
.whenComplete(
(unused, exception) -> assertThat(exception).isInstanceOf(IllegalStateException.class));
}
@Test
public void http404() throws Exception {
try (final Response resp = client.newCall(buildGetRequest("/foo")).execute()) {
Assertions.assertThat(resp.code()).isEqualTo(404);
}
}
@Test
public void handleEmptyRequestAndRedirect_post() throws Exception {
final RequestBody body = RequestBody.create("", null);
try (final Response resp =
client.newCall(new Request.Builder().post(body).url(service.url()).build()).execute()) {
Assertions.assertThat(resp.code()).isEqualTo(HttpResponseStatus.PERMANENT_REDIRECT.code());
final String location = resp.header("Location");
Assertions.assertThat(location).isNotEmpty().isNotNull();
final HttpUrl redirectUrl = resp.request().url().resolve(location);
Assertions.assertThat(redirectUrl).isNotNull();
final Request.Builder redirectBuilder = resp.request().newBuilder();
redirectBuilder.post(resp.request().body());
resp.body().close();
try (final Response redirectResp =
client.newCall(redirectBuilder.url(redirectUrl).build()).execute()) {
Assertions.assertThat(redirectResp.code()).isEqualTo(HttpResponseStatus.BAD_REQUEST.code());
}
}
}
@Test
public void handleEmptyRequestAndRedirect_get() throws Exception {
String url = service.url();
Request req = new Request.Builder().get().url(url).build();
try (final Response resp = client.newCall(req).execute()) {
Assertions.assertThat(resp.code()).isEqualTo(HttpResponseStatus.PERMANENT_REDIRECT.code());
final String location = resp.header("Location");
Assertions.assertThat(location).isNotEmpty().isNotNull();
final HttpUrl redirectUrl = resp.request().url().resolve(location);
Assertions.assertThat(redirectUrl).isNotNull();
final Request.Builder redirectBuilder = resp.request().newBuilder();
redirectBuilder.get();
resp.body().close();
try (final Response redirectResp =
client.newCall(redirectBuilder.url(redirectUrl).build()).execute()) {
Assertions.assertThat(redirectResp.code()).isEqualTo(HttpResponseStatus.BAD_REQUEST.code());
}
}
}
@Test
public void handleInvalidQuerySchema() throws Exception {
final RequestBody body = RequestBody.create("{gasPrice1}", GRAPHQL);
try (final Response resp = client.newCall(buildPostRequest(body)).execute()) {
final JsonObject json = new JsonObject(resp.body().string());
testHelper.assertValidGraphQLError(json);
Assertions.assertThat(resp.code()).isEqualTo(400);
}
}
@Test
public void query_get() throws Exception {
final Wei price = Wei.of(16);
Mockito.when(blockchainQueries.gasPrice()).thenReturn(price);
Mockito.when(miningCoordinatorMock.getMinTransactionGasPrice()).thenReturn(price);
try (final Response resp = client.newCall(buildGetRequest("?query={gasPrice}")).execute()) {
Assertions.assertThat(resp.code()).isEqualTo(200);
final JsonObject json = new JsonObject(resp.body().string());
testHelper.assertValidGraphQLResult(json);
final String result = json.getJsonObject("data").getString("gasPrice");
Assertions.assertThat(result).isEqualTo("0x10");
}
}
@Test
public void query_jsonPost() throws Exception {
final RequestBody body = RequestBody.create("{\"query\":\"{gasPrice}\"}", JSON);
final Wei price = Wei.of(16);
Mockito.when(miningCoordinatorMock.getMinTransactionGasPrice()).thenReturn(price);
try (final Response resp = client.newCall(buildPostRequest(body)).execute()) {
Assertions.assertThat(resp.code()).isEqualTo(200); // Check general format of result
final JsonObject json = new JsonObject(resp.body().string());
testHelper.assertValidGraphQLResult(json);
final String result = json.getJsonObject("data").getString("gasPrice");
Assertions.assertThat(result).isEqualTo("0x10");
}
}
@Test
public void query_graphqlPost() throws Exception {
final RequestBody body = RequestBody.create("{gasPrice}", GRAPHQL);
final Wei price = Wei.of(16);
Mockito.when(miningCoordinatorMock.getMinTransactionGasPrice()).thenReturn(price);
try (final Response resp = client.newCall(buildPostRequest(body)).execute()) {
Assertions.assertThat(resp.code()).isEqualTo(200); // Check general format of result
final JsonObject json = new JsonObject(resp.body().string());
testHelper.assertValidGraphQLResult(json);
final String result = json.getJsonObject("data").getString("gasPrice");
Assertions.assertThat(result).isEqualTo("0x10");
}
}
@Test
public void query_untypedPost() throws Exception {
final RequestBody body = RequestBody.create("{gasPrice}", null);
final Wei price = Wei.of(16);
Mockito.when(miningCoordinatorMock.getMinTransactionGasPrice()).thenReturn(price);
try (final Response resp = client.newCall(buildPostRequest(body)).execute()) {
Assertions.assertThat(resp.code()).isEqualTo(200); // Check general format of result
final JsonObject json = new JsonObject(resp.body().string());
testHelper.assertValidGraphQLResult(json);
final String result = json.getJsonObject("data").getString("gasPrice");
Assertions.assertThat(result).isEqualTo("0x10");
}
}
@Test
public void getSocketAddressWhenActive() {
final InetSocketAddress socketAddress = service.socketAddress();
Assertions.assertThat(socketAddress.getAddress().getHostAddress()).isEqualTo("127.0.0.1");
Assertions.assertThat(socketAddress.getPort()).isPositive();
}
@Test
public void getSocketAddressWhenStoppedIsEmpty() throws Exception {
final GraphQLHttpService service = createGraphQLHttpService();
final InetSocketAddress socketAddress = service.socketAddress();
Assertions.assertThat(socketAddress.getAddress().getHostAddress()).isEqualTo("0.0.0.0");
Assertions.assertThat(socketAddress.getPort()).isZero();
Assertions.assertThat(service.url()).isEmpty();
}
@Test
public void getSocketAddressWhenBindingToAllInterfaces() throws Exception {
final GraphQLConfiguration config = createGraphQLConfig();
config.setHost("0.0.0.0");
final GraphQLHttpService service = createGraphQLHttpService(config);
service.start().join();
try {
final InetSocketAddress socketAddress = service.socketAddress();
Assertions.assertThat(socketAddress.getAddress().getHostAddress()).isEqualTo("0.0.0.0");
Assertions.assertThat(socketAddress.getPort()).isPositive();
Assertions.assertThat(!service.url().contains("0.0.0.0")).isTrue();
} finally {
service.stop().join();
}
}
@Test
public void responseContainsJsonContentTypeHeader() throws Exception {
final RequestBody body = RequestBody.create("{gasPrice}", GRAPHQL);
try (final Response resp = client.newCall(buildPostRequest(body)).execute()) {
Assertions.assertThat(resp.header("Content-Type")).isEqualTo(JSON.toString());
}
}
@Test
public void ethGetBlockNumberByBlockHash() throws Exception {
final long blockNumber = 12345L;
final Hash blockHash = Hash.hash(Bytes.of(1));
@SuppressWarnings("unchecked")
final BlockWithMetadata<TransactionWithMetadata, Hash> block =
Mockito.mock(BlockWithMetadata.class);
@SuppressWarnings("unchecked")
final BlockHeader blockHeader = Mockito.mock(BlockHeader.class);
Mockito.when(blockchainQueries.blockByHash(blockHash)).thenReturn(Optional.of(block));
Mockito.when(block.getHeader()).thenReturn(blockHeader);
Mockito.when(blockHeader.getNumber()).thenReturn(blockNumber);
final String query = "{block(hash:\"" + blockHash + "\") {number}}";
final RequestBody body = RequestBody.create(query, GRAPHQL);
try (final Response resp = client.newCall(buildPostRequest(body)).execute()) {
Assertions.assertThat(resp.code()).isEqualTo(200);
final String jsonStr = resp.body().string();
final JsonObject json = new JsonObject(jsonStr);
testHelper.assertValidGraphQLResult(json);
final String result = json.getJsonObject("data").getJsonObject("block").getString("number");
Assertions.assertThat(Integer.parseInt(result.substring(2), 16)).isEqualTo(blockNumber);
}
}
private Request buildPostRequest(final RequestBody body) {
return new Request.Builder().post(body).url(baseUrl).build();
}
private Request buildGetRequest(final String path) {
return new Request.Builder().get().url(baseUrl + path).build();
}
}

View File

@@ -55,8 +55,8 @@ import org.hyperledger.besu.evm.account.MutableAccount;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.plugin.services.exception.StorageException;
import org.hyperledger.besu.plugin.services.securitymodule.SecurityModuleException;
import org.hyperledger.besu.plugin.services.tracer.BlockAwareOperationTracer;
import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelector;
import org.hyperledger.besu.plugin.services.txselection.SelectorsStateManager;
import java.util.List;
import java.util.Optional;
@@ -213,11 +213,15 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
throwIfStopped();
final PluginTransactionSelector pluginTransactionSelector =
miningConfiguration.getTransactionSelectionService().createPluginTransactionSelector();
final BlockAwareOperationTracer operationTracer =
pluginTransactionSelector.getOperationTracer();
final var selectorsStateManager = new SelectorsStateManager();
final var pluginTransactionSelector =
miningConfiguration
.getTransactionSelectionService()
.createPluginTransactionSelector(selectorsStateManager);
final var operationTracer = pluginTransactionSelector.getOperationTracer();
pluginTransactionSelector
.getOperationTracer()
.traceStartBlock(processableBlockHeader, miningBeneficiary);
operationTracer.traceStartBlock(processableBlockHeader, miningBeneficiary);
BlockProcessingContext blockProcessingContext =
@@ -240,6 +244,7 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
miningBeneficiary,
newProtocolSpec,
pluginTransactionSelector,
selectorsStateManager,
parentHeader);
transactionResults.logSelectionStats();
timings.register("txsSelection");
@@ -362,6 +367,7 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
final Address miningBeneficiary,
final ProtocolSpec protocolSpec,
final PluginTransactionSelector pluginTransactionSelector,
final SelectorsStateManager selectorsStateManager,
final BlockHeader parentHeader)
throws RuntimeException {
final MainnetTransactionProcessor transactionProcessor = protocolSpec.getTransactionProcessor();
@@ -391,7 +397,8 @@ public abstract class AbstractBlockCreator implements AsyncBlockCreator {
protocolSpec.getGasLimitCalculator(),
protocolSpec.getBlockHashProcessor(),
pluginTransactionSelector,
ethScheduler);
ethScheduler,
selectorsStateManager);
if (transactions.isPresent()) {
return selector.evaluateTransactions(transactions.get());

View File

@@ -50,10 +50,14 @@ import org.hyperledger.besu.evm.blockhash.BlockHashLookup;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.plugin.data.TransactionSelectionResult;
import org.hyperledger.besu.plugin.services.TransactionSelectionService;
import org.hyperledger.besu.plugin.services.tracer.BlockAwareOperationTracer;
import org.hyperledger.besu.plugin.services.txselection.BlockTransactionSelectionService;
import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelector;
import org.hyperledger.besu.plugin.services.txselection.SelectorsStateManager;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
@@ -88,7 +92,7 @@ import org.slf4j.LoggerFactory;
* Once "used" this class must be discarded and another created. This class contains state which is
* not cleared between executions of buildTransactionListForBlock().
*/
public class BlockTransactionSelector {
public class BlockTransactionSelector implements BlockTransactionSelectionService {
private static final Logger LOG = LoggerFactory.getLogger(BlockTransactionSelector.class);
private final Supplier<Boolean> isCancelled;
private final MainnetTransactionProcessor transactionProcessor;
@@ -99,13 +103,17 @@ public class BlockTransactionSelector {
private final TransactionSelectionResults transactionSelectionResults =
new TransactionSelectionResults();
private final List<AbstractTransactionSelector> transactionSelectors;
private final SelectorsStateManager selectorsStateManager;
private final TransactionSelectionService transactionSelectionService;
private final PluginTransactionSelector pluginTransactionSelector;
private final BlockAwareOperationTracer operationTracer;
private final EthScheduler ethScheduler;
private final AtomicBoolean isTimeout = new AtomicBoolean(false);
private final long blockTxsSelectionMaxTime;
private WorldUpdater blockWorldStateUpdater;
private WorldUpdater txWorldStateUpdater;
private volatile TransactionEvaluationContext currTxEvaluationContext;
private final List<Runnable> selectedTxPendingActions = new ArrayList<>(1);
public BlockTransactionSelector(
final MiningConfiguration miningConfiguration,
@@ -123,7 +131,8 @@ public class BlockTransactionSelector {
final GasLimitCalculator gasLimitCalculator,
final BlockHashProcessor blockHashProcessor,
final PluginTransactionSelector pluginTransactionSelector,
final EthScheduler ethScheduler) {
final EthScheduler ethScheduler,
final SelectorsStateManager selectorsStateManager) {
this.transactionProcessor = transactionProcessor;
this.blockchain = blockchain;
this.worldState = worldState;
@@ -141,20 +150,24 @@ public class BlockTransactionSelector {
blobGasPrice,
miningBeneficiary,
transactionPool);
transactionSelectors = createTransactionSelectors(blockSelectionContext);
this.selectorsStateManager = selectorsStateManager;
this.transactionSelectionService = miningConfiguration.getTransactionSelectionService();
this.transactionSelectors =
createTransactionSelectors(blockSelectionContext, selectorsStateManager);
this.pluginTransactionSelector = pluginTransactionSelector;
this.operationTracer =
new InterruptibleOperationTracer(pluginTransactionSelector.getOperationTracer());
blockWorldStateUpdater = worldState.updater();
txWorldStateUpdater = blockWorldStateUpdater.updater();
blockTxsSelectionMaxTime = miningConfiguration.getBlockTxsSelectionMaxTime();
}
private List<AbstractTransactionSelector> createTransactionSelectors(
final BlockSelectionContext context) {
final BlockSelectionContext context, final SelectorsStateManager selectorsStateManager) {
return List.of(
new SkipSenderTransactionSelector(context),
new BlockSizeTransactionSelector(context),
new BlobSizeTransactionSelector(context),
new BlockSizeTransactionSelector(context, selectorsStateManager),
new BlobSizeTransactionSelector(context, selectorsStateManager),
new PriceTransactionSelector(context),
new BlobPriceTransactionSelector(context),
new MinPriorityFeePerGasTransactionSelector(context),
@@ -171,10 +184,6 @@ public class BlockTransactionSelector {
* evaluation.
*/
public TransactionSelectionResults buildTransactionListForBlock() {
LOG.atDebug()
.setMessage("Transaction pool stats {}")
.addArgument(blockSelectionContext.transactionPool()::logStats)
.log();
timeLimitedSelection();
LOG.atTrace()
.setMessage("Transaction selection result {}")
@@ -186,10 +195,19 @@ public class BlockTransactionSelector {
private void timeLimitedSelection() {
final var txSelectionTask =
new FutureTask<Void>(
() ->
blockSelectionContext
.transactionPool()
.selectTransactions(this::evaluateTransaction),
() -> {
selectorsStateManager.blockSelectionStarted();
LOG.debug("Starting plugin transaction selection");
transactionSelectionService.selectPendingTransactions(
this, blockSelectionContext.pendingBlockHeader());
LOG.atDebug()
.setMessage("Starting internal pool transaction selection, stats {}")
.addArgument(blockSelectionContext.transactionPool()::logStats)
.log();
blockSelectionContext.transactionPool().selectTransactions(this::evaluateTransaction);
},
null);
ethScheduler.scheduleBlockCreationTask(txSelectionTask);
@@ -208,11 +226,18 @@ public class BlockTransactionSelector {
cancelEvaluatingTxWithGraceTime(txSelectionTask);
LOG.warn(
"Interrupting the selection of transactions for block inclusion as it exceeds the maximum configured duration of "
+ blockTxsSelectionMaxTime
+ "ms",
e);
final var logBuilder =
LOG.atWarn()
.setMessage(
"Interrupting the selection of transactions for block inclusion as it exceeds"
+ " the maximum configured duration of {}ms")
.addArgument(blockTxsSelectionMaxTime);
if (LOG.isTraceEnabled()) {
logBuilder.setCause(e).log();
} else {
logBuilder.log();
}
}
}
@@ -260,11 +285,25 @@ public class BlockTransactionSelector {
* evaluations.
*/
public TransactionSelectionResults evaluateTransactions(final List<Transaction> transactions) {
selectorsStateManager.blockSelectionStarted();
transactions.forEach(
transaction -> evaluateTransaction(new PendingTransaction.Local.Priority(transaction)));
return transactionSelectionResults;
}
private TransactionSelectionResult evaluateTransaction(
final PendingTransaction pendingTransaction) {
final var evaluationResult = evaluatePendingTransaction(pendingTransaction);
if (evaluationResult.selected()) {
return commit() ? evaluationResult : BLOCK_SELECTION_TIMEOUT;
} else {
rollback();
return evaluationResult;
}
}
/**
* Passed into the PendingTransactions, and is called on each transaction until sufficient
* transactions are found which fill a block worth of gas. This function will continue to be
@@ -275,10 +314,14 @@ public class BlockTransactionSelector {
* @return The result of the transaction evaluation process.
* @throws CancellationException if the transaction selection process is cancelled.
*/
private TransactionSelectionResult evaluateTransaction(
final PendingTransaction pendingTransaction) {
@Override
public TransactionSelectionResult evaluatePendingTransaction(
final org.hyperledger.besu.datatypes.PendingTransaction pendingTransaction) {
checkCancellation();
LOG.atTrace().setMessage("Starting evaluation of {}").addArgument(pendingTransaction).log();
final TransactionEvaluationContext evaluationContext =
createTransactionEvaluationContext(pendingTransaction);
currTxEvaluationContext = evaluationContext;
@@ -288,27 +331,56 @@ public class BlockTransactionSelector {
return handleTransactionNotSelected(evaluationContext, selectionResult);
}
final WorldUpdater txWorldStateUpdater = blockWorldStateUpdater.updater();
final TransactionProcessingResult processingResult =
processTransaction(evaluationContext.getTransaction(), txWorldStateUpdater);
processTransaction(evaluationContext.getTransaction());
txWorldStateUpdater.markTransactionBoundary();
var postProcessingSelectionResult = evaluatePostProcessing(evaluationContext, processingResult);
if (postProcessingSelectionResult.selected()) {
return handleTransactionSelected(evaluationContext, processingResult, txWorldStateUpdater);
return postProcessingSelectionResult.selected()
? handleTransactionSelected(evaluationContext, processingResult)
: handleTransactionNotSelected(evaluationContext, postProcessingSelectionResult);
}
@Override
public boolean commit() {
// only add this tx to the selected set if it is not too late,
// this need to be done synchronously to avoid that a concurrent timeout
// could start packing a block while we are updating the state here
synchronized (isTimeout) {
if (!isTimeout.get()) {
selectorsStateManager.commit();
txWorldStateUpdater.commit();
blockWorldStateUpdater.commit();
for (final var pendingAction : selectedTxPendingActions) {
pendingAction.run();
}
selectedTxPendingActions.clear();
return true;
}
}
return handleTransactionNotSelected(
evaluationContext, postProcessingSelectionResult, txWorldStateUpdater);
selectedTxPendingActions.clear();
blockWorldStateUpdater = worldState.updater();
txWorldStateUpdater = blockWorldStateUpdater.updater();
return false;
}
@Override
public void rollback() {
selectedTxPendingActions.clear();
selectorsStateManager.rollback();
txWorldStateUpdater = blockWorldStateUpdater.updater();
}
private TransactionEvaluationContext createTransactionEvaluationContext(
final PendingTransaction pendingTransaction) {
final org.hyperledger.besu.datatypes.PendingTransaction pendingTransaction) {
final Wei transactionGasPriceInBlock =
blockSelectionContext
.feeMarket()
.getTransactionPriceCalculator()
.price(
pendingTransaction.getTransaction(),
(Transaction) pendingTransaction.getTransaction(),
blockSelectionContext.pendingBlockHeader().getBaseFee());
return new TransactionEvaluationContext(
@@ -333,7 +405,7 @@ public class BlockTransactionSelector {
for (var selector : transactionSelectors) {
TransactionSelectionResult result =
selector.evaluateTransactionPreProcessing(evaluationContext, transactionSelectionResults);
selector.evaluateTransactionPreProcessing(evaluationContext);
if (!result.equals(SELECTED)) {
return result;
}
@@ -357,8 +429,7 @@ public class BlockTransactionSelector {
for (var selector : transactionSelectors) {
TransactionSelectionResult result =
selector.evaluateTransactionPostProcessing(
evaluationContext, transactionSelectionResults, processingResult);
selector.evaluateTransactionPostProcessing(evaluationContext, processingResult);
if (!result.equals(SELECTED)) {
return result;
}
@@ -371,17 +442,15 @@ public class BlockTransactionSelector {
* Processes a transaction
*
* @param transaction The transaction to be processed.
* @param worldStateUpdater The world state updater.
* @return The result of the transaction processing.
*/
private TransactionProcessingResult processTransaction(
final Transaction transaction, final WorldUpdater worldStateUpdater) {
private TransactionProcessingResult processTransaction(final Transaction transaction) {
final BlockHashLookup blockHashLookup =
blockSelectionContext
.blockHashProcessor()
.createBlockHashLookup(blockchain, blockSelectionContext.pendingBlockHeader());
return transactionProcessor.processTransaction(
worldStateUpdater,
txWorldStateUpdater,
blockSelectionContext.pendingBlockHeader(),
transaction,
blockSelectionContext.miningBeneficiary(),
@@ -399,56 +468,48 @@ public class BlockTransactionSelector {
*
* @param evaluationContext The current selection session data.
* @param processingResult The result of the transaction processing.
* @param txWorldStateUpdater The world state updater.
* @return The result of the transaction selection process.
*/
private TransactionSelectionResult handleTransactionSelected(
final TransactionEvaluationContext evaluationContext,
final TransactionProcessingResult processingResult,
final WorldUpdater txWorldStateUpdater) {
final TransactionProcessingResult processingResult) {
final Transaction transaction = evaluationContext.getTransaction();
final long gasUsedByTransaction =
transaction.getGasLimit() - processingResult.getGasRemaining();
final long cumulativeGasUsed =
transactionSelectionResults.getCumulativeGasUsed() + gasUsedByTransaction;
final long blobGasUsed =
blockSelectionContext.gasCalculator().blobGasCost(transaction.getBlobCount());
final boolean tooLate;
// queue the creation of the receipt and the update of the final results
// there actions will be performed on commit if the pending tx is definitely selected
selectedTxPendingActions.add(
() -> {
final long cumulativeGasUsed =
transactionSelectionResults.getCumulativeGasUsed() + gasUsedByTransaction;
// only add this tx to the selected set if it is not too late,
// this need to be done synchronously to avoid that a concurrent timeout
// could start packing a block while we are updating the state here
synchronized (isTimeout) {
tooLate = isTimeout.get();
if (!tooLate) {
txWorldStateUpdater.commit();
blockWorldStateUpdater.commit();
final TransactionReceipt receipt =
transactionReceiptFactory.create(
transaction.getType(), processingResult, worldState, cumulativeGasUsed);
final TransactionReceipt receipt =
transactionReceiptFactory.create(
transaction.getType(), processingResult, worldState, cumulativeGasUsed);
transactionSelectionResults.updateSelected(
transaction, receipt, gasUsedByTransaction, blobGasUsed);
}
}
transactionSelectionResults.updateSelected(transaction, receipt, gasUsedByTransaction);
if (tooLate) {
notifySelected(evaluationContext, processingResult);
LOG.atTrace()
.setMessage("Selected and commited {} for block creation")
.addArgument(transaction::toTraceLog)
.log();
});
if (isTimeout.get()) {
// even if this tx passed all the checks, it is too late to include it in this block,
// so we need to treat it as not selected
// do not rely on the presence of this result, since by the time it is added, the code
// reading it could have been already executed by another thread
return handleTransactionNotSelected(
evaluationContext, BLOCK_SELECTION_TIMEOUT, txWorldStateUpdater);
return handleTransactionNotSelected(evaluationContext, BLOCK_SELECTION_TIMEOUT);
}
notifySelected(evaluationContext, processingResult);
blockWorldStateUpdater = worldState.updater();
LOG.atTrace()
.setMessage("Selected {} for block creation, evaluated in {}")
.setMessage(
"Potentially selected {} for block creation, evaluated in {}, waiting for commit")
.addArgument(transaction::toTraceLog)
.addArgument(evaluationContext.getEvaluationTimer())
.log();
@@ -479,12 +540,16 @@ public class BlockTransactionSelector {
transactionSelectionResults.updateNotSelected(evaluationContext.getTransaction(), actualResult);
notifyNotSelected(evaluationContext, actualResult);
LOG.atTrace()
.setMessage(
"Not selected {} for block creation with result {} (original result {}), evaluated in {}")
.setMessage("Not selected {} for block creation with result {}{}, evaluated in {}")
.addArgument(pendingTransaction::toTraceLog)
.addArgument(actualResult)
.addArgument(selectionResult)
.addArgument(
() ->
selectionResult.equals(actualResult)
? ""
: " (original result " + selectionResult + ")")
.addArgument(evaluationContext.getEvaluationTimer())
.log();
@@ -545,14 +610,6 @@ public class BlockTransactionSelector {
return false;
}
private TransactionSelectionResult handleTransactionNotSelected(
final TransactionEvaluationContext evaluationContext,
final TransactionSelectionResult selectionResult,
final WorldUpdater txWorldStateUpdater) {
txWorldStateUpdater.revert();
return handleTransactionNotSelected(evaluationContext, selectionResult);
}
private void notifySelected(
final TransactionEvaluationContext evaluationContext,
final TransactionProcessingResult processingResult) {

View File

@@ -48,27 +48,20 @@ public class TransactionSelectionResults {
new ConcurrentHashMap<>();
private long cumulativeGasUsed = 0;
private long cumulativeBlobGasUsed = 0;
void updateSelected(
final Transaction transaction,
final TransactionReceipt receipt,
final long gasUsed,
final long blobGasUsed) {
final Transaction transaction, final TransactionReceipt receipt, final long gasUsed) {
selectedTransactions.add(transaction);
transactionsByType
.computeIfAbsent(transaction.getType(), type -> new ArrayList<>())
.add(transaction);
receipts.add(receipt);
cumulativeGasUsed += gasUsed;
cumulativeBlobGasUsed += blobGasUsed;
LOG.atTrace()
.setMessage(
"New selected transaction {}, total transactions {}, cumulative gas used {}, cumulative blob gas used {}")
.setMessage("New selected transaction {}, total transactions {}, cumulative gas used {}")
.addArgument(transaction::toTraceLog)
.addArgument(selectedTransactions::size)
.addArgument(cumulativeGasUsed)
.addArgument(cumulativeBlobGasUsed)
.log();
}
@@ -93,10 +86,6 @@ public class TransactionSelectionResults {
return cumulativeGasUsed;
}
public long getCumulativeBlobGasUsed() {
return cumulativeBlobGasUsed;
}
public Map<Transaction, TransactionSelectionResult> getNotSelectedTransactions() {
return Map.copyOf(notSelectedTransactions);
}
@@ -135,7 +124,6 @@ public class TransactionSelectionResults {
}
TransactionSelectionResults that = (TransactionSelectionResults) o;
return cumulativeGasUsed == that.cumulativeGasUsed
&& cumulativeBlobGasUsed == that.cumulativeBlobGasUsed
&& selectedTransactions.equals(that.selectedTransactions)
&& notSelectedTransactions.equals(that.notSelectedTransactions)
&& receipts.equals(that.receipts);
@@ -143,19 +131,12 @@ public class TransactionSelectionResults {
@Override
public int hashCode() {
return Objects.hash(
selectedTransactions,
notSelectedTransactions,
receipts,
cumulativeGasUsed,
cumulativeBlobGasUsed);
return Objects.hash(selectedTransactions, notSelectedTransactions, receipts, cumulativeGasUsed);
}
public String toTraceLog() {
return "cumulativeGasUsed="
+ cumulativeGasUsed
+ ", cumulativeBlobGasUsed="
+ cumulativeBlobGasUsed
+ ", selectedTransactions="
+ selectedTransactions.stream()
.map(Transaction::toTraceLog)

View File

@@ -0,0 +1,57 @@
/*
* Copyright contributors to Hyperledger 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.blockcreation.txselection.selectors;
import org.hyperledger.besu.ethereum.blockcreation.txselection.BlockSelectionContext;
import org.hyperledger.besu.plugin.services.txselection.SelectorsStateManager;
/**
* This class represents an abstract transaction selector which provides methods to access and set
* the selector working state.
*
* @param <S> The type of state specified by the implementing selector, not to be confused with the
* world state.
*/
public abstract class AbstractStatefulTransactionSelector<S> extends AbstractTransactionSelector {
private final SelectorsStateManager selectorsStateManager;
public AbstractStatefulTransactionSelector(
final BlockSelectionContext context,
final SelectorsStateManager selectorsStateManager,
final S initialState,
final SelectorsStateManager.StateDuplicator<S> stateDuplicator) {
super(context);
this.selectorsStateManager = selectorsStateManager;
selectorsStateManager.createSelectorState(this, initialState, stateDuplicator);
}
/**
* Get the working state specific to this selector.
*
* @return the working state of the selector
*/
protected S getWorkingState() {
return selectorsStateManager.getSelectorWorkingState(this);
}
/**
* Set the working state specific to this selector.
*
* @param newState the new state of the selector
*/
protected void setWorkingState(final S newState) {
selectorsStateManager.setSelectorWorkingState(this, newState);
}
}

View File

@@ -16,15 +16,15 @@ package org.hyperledger.besu.ethereum.blockcreation.txselection.selectors;
import org.hyperledger.besu.ethereum.blockcreation.txselection.BlockSelectionContext;
import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionEvaluationContext;
import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionSelectionResults;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.plugin.data.TransactionSelectionResult;
import org.hyperledger.besu.plugin.services.txselection.TransactionSelector;
/**
* This class represents an abstract transaction selector which provides methods to evaluate
* transactions.
*/
public abstract class AbstractTransactionSelector {
public abstract class AbstractTransactionSelector implements TransactionSelector {
protected final BlockSelectionContext context;
public AbstractTransactionSelector(final BlockSelectionContext context) {
@@ -35,25 +35,21 @@ public abstract class AbstractTransactionSelector {
* Evaluates a transaction in the context of other transactions in the same block.
*
* @param evaluationContext The current selection session data.
* @param blockTransactionResults The results of other transaction evaluations in the same block.
* @return The result of the transaction evaluation
*/
public abstract TransactionSelectionResult evaluateTransactionPreProcessing(
final TransactionEvaluationContext evaluationContext,
final TransactionSelectionResults blockTransactionResults);
final TransactionEvaluationContext evaluationContext);
/**
* Evaluates a transaction considering other transactions in the same block and a transaction
* processing result.
*
* @param evaluationContext The current selection session data.
* @param blockTransactionResults The results of other transaction evaluations in the same block.
* @param processingResult The result of transaction processing.
* @return The result of the transaction evaluation
*/
public abstract TransactionSelectionResult evaluateTransactionPostProcessing(
final TransactionEvaluationContext evaluationContext,
final TransactionSelectionResults blockTransactionResults,
final TransactionProcessingResult processingResult);
/**

View File

@@ -16,7 +16,6 @@ package org.hyperledger.besu.ethereum.blockcreation.txselection.selectors;
import org.hyperledger.besu.ethereum.blockcreation.txselection.BlockSelectionContext;
import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionEvaluationContext;
import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionSelectionResults;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.plugin.data.TransactionSelectionResult;
@@ -40,13 +39,11 @@ public class BlobPriceTransactionSelector extends AbstractTransactionSelector {
* Evaluates a transaction considering its blob price.
*
* @param evaluationContext The current selection session data.
* @param ignored The results of other transaction evaluations in the same block.
* @return The result of the transaction selection.
*/
@Override
public TransactionSelectionResult evaluateTransactionPreProcessing(
final TransactionEvaluationContext evaluationContext,
final TransactionSelectionResults ignored) {
final TransactionEvaluationContext evaluationContext) {
if (transactionBlobPriceBelowMin(evaluationContext.getTransaction())) {
return TransactionSelectionResult.BLOB_PRICE_BELOW_CURRENT_MIN;
}
@@ -56,7 +53,6 @@ public class BlobPriceTransactionSelector extends AbstractTransactionSelector {
@Override
public TransactionSelectionResult evaluateTransactionPostProcessing(
final TransactionEvaluationContext evaluationContext,
final TransactionSelectionResults blockTransactionResults,
final TransactionProcessingResult processingResult) {
// All necessary checks were done in the pre-processing method, so nothing to do here.
return TransactionSelectionResult.SELECTED;

View File

@@ -16,9 +16,9 @@ package org.hyperledger.besu.ethereum.blockcreation.txselection.selectors;
import org.hyperledger.besu.ethereum.blockcreation.txselection.BlockSelectionContext;
import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionEvaluationContext;
import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionSelectionResults;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.plugin.data.TransactionSelectionResult;
import org.hyperledger.besu.plugin.services.txselection.SelectorsStateManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -28,11 +28,12 @@ import org.slf4j.LoggerFactory;
* evaluating transactions based on blobs size. It checks if a transaction supports blobs, and if
* so, checks that there is enough remaining blob gas in the block to fit the blobs of the tx.
*/
public class BlobSizeTransactionSelector extends AbstractTransactionSelector {
public class BlobSizeTransactionSelector extends AbstractStatefulTransactionSelector<Long> {
private static final Logger LOG = LoggerFactory.getLogger(BlobSizeTransactionSelector.class);
public BlobSizeTransactionSelector(final BlockSelectionContext context) {
super(context);
public BlobSizeTransactionSelector(
final BlockSelectionContext context, final SelectorsStateManager selectorsStateManager) {
super(context, selectorsStateManager, 0L, SelectorsStateManager.StateDuplicator::duplicateLong);
}
/**
@@ -43,21 +44,19 @@ public class BlobSizeTransactionSelector extends AbstractTransactionSelector {
* number of blobs or not.
*
* @param evaluationContext The current selection session data.
* @param transactionSelectionResults The results of other transaction evaluations in the same
* block.
* @return The result of the transaction selection.
*/
@Override
public TransactionSelectionResult evaluateTransactionPreProcessing(
final TransactionEvaluationContext evaluationContext,
final TransactionSelectionResults transactionSelectionResults) {
final TransactionEvaluationContext evaluationContext) {
final var tx = evaluationContext.getTransaction();
if (tx.getType().supportsBlob()) {
final var cumulativeBlobGasUsed = getWorkingState();
final var remainingBlobGas =
context.gasLimitCalculator().currentBlobGasLimit()
- transactionSelectionResults.getCumulativeBlobGasUsed();
context.gasLimitCalculator().currentBlobGasLimit() - cumulativeBlobGasUsed;
if (remainingBlobGas == 0) {
LOG.atTrace()
@@ -88,9 +87,11 @@ public class BlobSizeTransactionSelector extends AbstractTransactionSelector {
@Override
public TransactionSelectionResult evaluateTransactionPostProcessing(
final TransactionEvaluationContext evaluationContext,
final TransactionSelectionResults blockTransactionResults,
final TransactionProcessingResult processingResult) {
// All necessary checks were done in the pre-processing method, so nothing to do here.
final var tx = evaluationContext.getTransaction();
if (tx.getType().supportsBlob()) {
setWorkingState(getWorkingState() + context.gasCalculator().blobGasCost(tx.getBlobCount()));
}
return TransactionSelectionResult.SELECTED;
}
}

View File

@@ -16,10 +16,10 @@ package org.hyperledger.besu.ethereum.blockcreation.txselection.selectors;
import org.hyperledger.besu.ethereum.blockcreation.txselection.BlockSelectionContext;
import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionEvaluationContext;
import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionSelectionResults;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.plugin.data.TransactionSelectionResult;
import org.hyperledger.besu.plugin.services.txselection.SelectorsStateManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -29,11 +29,15 @@ import org.slf4j.LoggerFactory;
* evaluating transactions based on block size. It checks if a transaction is too large for the
* block and determines the selection result accordingly.
*/
public class BlockSizeTransactionSelector extends AbstractTransactionSelector {
public class BlockSizeTransactionSelector extends AbstractStatefulTransactionSelector<Long> {
private static final Logger LOG = LoggerFactory.getLogger(BlockSizeTransactionSelector.class);
public BlockSizeTransactionSelector(final BlockSelectionContext context) {
super(context);
private final long blockGasLimit;
public BlockSizeTransactionSelector(
final BlockSelectionContext context, final SelectorsStateManager selectorsStateManager) {
super(context, selectorsStateManager, 0L, SelectorsStateManager.StateDuplicator::duplicateLong);
this.blockGasLimit = context.pendingBlockHeader().getGasLimit();
}
/**
@@ -41,25 +45,23 @@ public class BlockSizeTransactionSelector extends AbstractTransactionSelector {
* too large for the block returns a selection result based on block occupancy.
*
* @param evaluationContext The current selection session data.
* @param transactionSelectionResults The results of other transaction evaluations in the same
* block.
* @return The result of the transaction selection.
*/
@Override
public TransactionSelectionResult evaluateTransactionPreProcessing(
final TransactionEvaluationContext evaluationContext,
final TransactionSelectionResults transactionSelectionResults) {
final TransactionEvaluationContext evaluationContext) {
if (transactionTooLargeForBlock(
evaluationContext.getTransaction(), transactionSelectionResults)) {
final long cumulativeGasUsed = getWorkingState();
if (transactionTooLargeForBlock(evaluationContext.getTransaction(), cumulativeGasUsed)) {
LOG.atTrace()
.setMessage("Transaction {} too large to select for block creation")
.addArgument(evaluationContext.getPendingTransaction()::toTraceLog)
.log();
if (blockOccupancyAboveThreshold(transactionSelectionResults)) {
if (blockOccupancyAboveThreshold(cumulativeGasUsed)) {
LOG.trace("Block occupancy above threshold, completing operation");
return TransactionSelectionResult.BLOCK_OCCUPANCY_ABOVE_THRESHOLD;
} else if (blockFull(transactionSelectionResults)) {
} else if (blockFull(cumulativeGasUsed)) {
LOG.trace("Block full, completing operation");
return TransactionSelectionResult.BLOCK_FULL;
} else {
@@ -72,49 +74,42 @@ public class BlockSizeTransactionSelector extends AbstractTransactionSelector {
@Override
public TransactionSelectionResult evaluateTransactionPostProcessing(
final TransactionEvaluationContext evaluationContext,
final TransactionSelectionResults blockTransactionResults,
final TransactionProcessingResult processingResult) {
// All necessary checks were done in the pre-processing method, so nothing to do here.
final long gasUsedByTransaction =
evaluationContext.getTransaction().getGasLimit() - processingResult.getGasRemaining();
setWorkingState(getWorkingState() + gasUsedByTransaction);
return TransactionSelectionResult.SELECTED;
}
/**
* Checks if the transaction is too large for the block.
*
* @param transaction The transaction to be checked.
* @param transactionSelectionResults The results of other transaction evaluations in the same
* block.
* @param transaction The transaction to be checked. block.
* @param cumulativeGasUsed The cumulative gas used by previous txs.
* @return True if the transaction is too large for the block, false otherwise.
*/
private boolean transactionTooLargeForBlock(
final Transaction transaction,
final TransactionSelectionResults transactionSelectionResults) {
final Transaction transaction, final long cumulativeGasUsed) {
return transaction.getGasLimit()
> context.pendingBlockHeader().getGasLimit()
- transactionSelectionResults.getCumulativeGasUsed();
return transaction.getGasLimit() > blockGasLimit - cumulativeGasUsed;
}
/**
* Checks if the block occupancy is above the threshold.
*
* @param transactionSelectionResults The results of other transaction evaluations in the same
* block.
* @param cumulativeGasUsed The cumulative gas used by previous txs.
* @return True if the block occupancy is above the threshold, false otherwise.
*/
private boolean blockOccupancyAboveThreshold(
final TransactionSelectionResults transactionSelectionResults) {
final long gasAvailable = context.pendingBlockHeader().getGasLimit();
final long gasUsed = transactionSelectionResults.getCumulativeGasUsed();
final long gasRemaining = gasAvailable - gasUsed;
final double occupancyRatio = (double) gasUsed / (double) gasAvailable;
private boolean blockOccupancyAboveThreshold(final long cumulativeGasUsed) {
final long gasRemaining = blockGasLimit - cumulativeGasUsed;
final double occupancyRatio = (double) cumulativeGasUsed / (double) blockGasLimit;
LOG.trace(
"Min block occupancy ratio {}, gas used {}, available {}, remaining {}, used/available {}",
context.miningConfiguration().getMinBlockOccupancyRatio(),
gasUsed,
gasAvailable,
cumulativeGasUsed,
blockGasLimit,
gasRemaining,
occupancyRatio);
@@ -124,15 +119,11 @@ public class BlockSizeTransactionSelector extends AbstractTransactionSelector {
/**
* Checks if the block is full.
*
* @param transactionSelectionResults The results of other transaction evaluations in the same
* block.
* @param cumulativeGasUsed The cumulative gas used by previous txs.
* @return True if the block is full, false otherwise.
*/
private boolean blockFull(final TransactionSelectionResults transactionSelectionResults) {
final long gasAvailable = context.pendingBlockHeader().getGasLimit();
final long gasUsed = transactionSelectionResults.getCumulativeGasUsed();
final long gasRemaining = gasAvailable - gasUsed;
private boolean blockFull(final long cumulativeGasUsed) {
final long gasRemaining = blockGasLimit - cumulativeGasUsed;
if (gasRemaining < context.gasCalculator().getMinimumTransactionCost()) {
LOG.trace(

View File

@@ -17,7 +17,6 @@ package org.hyperledger.besu.ethereum.blockcreation.txselection.selectors;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.blockcreation.txselection.BlockSelectionContext;
import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionEvaluationContext;
import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionSelectionResults;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.plugin.data.TransactionSelectionResult;
@@ -37,15 +36,12 @@ public class MinPriorityFeePerGasTransactionSelector extends AbstractTransaction
* Evaluates a transaction before processing.
*
* @param evaluationContext The current selection session data.
* @param transactionSelectionResults The results of other transaction evaluations in the same
* block.
* @return TransactionSelectionResult. If the priority fee is below the minimum, it returns an
* invalid transient result. Otherwise, it returns a selected result.
*/
@Override
public TransactionSelectionResult evaluateTransactionPreProcessing(
final TransactionEvaluationContext evaluationContext,
final TransactionSelectionResults transactionSelectionResults) {
final TransactionEvaluationContext evaluationContext) {
if (isPriorityFeePriceBelowMinimum(evaluationContext)) {
return TransactionSelectionResult.PRIORITY_FEE_PER_GAS_BELOW_CURRENT_MIN;
}
@@ -82,7 +78,6 @@ public class MinPriorityFeePerGasTransactionSelector extends AbstractTransaction
@Override
public TransactionSelectionResult evaluateTransactionPostProcessing(
final TransactionEvaluationContext evaluationContext,
final TransactionSelectionResults blockTransactionResults,
final TransactionProcessingResult processingResult) {
return TransactionSelectionResult.SELECTED;
}

View File

@@ -16,7 +16,6 @@ package org.hyperledger.besu.ethereum.blockcreation.txselection.selectors;
import org.hyperledger.besu.ethereum.blockcreation.txselection.BlockSelectionContext;
import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionEvaluationContext;
import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionSelectionResults;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.plugin.data.TransactionSelectionResult;
@@ -40,13 +39,11 @@ public class PriceTransactionSelector extends AbstractTransactionSelector {
* minimum, it returns a selection result indicating the reason.
*
* @param evaluationContext The current selection session data.
* @param ignored The results of other transaction evaluations in the same block.
* @return The result of the transaction selection.
*/
@Override
public TransactionSelectionResult evaluateTransactionPreProcessing(
final TransactionEvaluationContext evaluationContext,
final TransactionSelectionResults ignored) {
final TransactionEvaluationContext evaluationContext) {
if (transactionCurrentPriceBelowMin(evaluationContext)) {
return TransactionSelectionResult.CURRENT_TX_PRICE_BELOW_MIN;
}
@@ -56,7 +53,6 @@ public class PriceTransactionSelector extends AbstractTransactionSelector {
@Override
public TransactionSelectionResult evaluateTransactionPostProcessing(
final TransactionEvaluationContext evaluationContext,
final TransactionSelectionResults blockTransactionResults,
final TransactionProcessingResult processingResult) {
// All necessary checks were done in the pre-processing method, so nothing to do here.
return TransactionSelectionResult.SELECTED;

View File

@@ -16,7 +16,6 @@ package org.hyperledger.besu.ethereum.blockcreation.txselection.selectors;
import org.hyperledger.besu.ethereum.blockcreation.txselection.BlockSelectionContext;
import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionEvaluationContext;
import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionSelectionResults;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
@@ -41,8 +40,7 @@ public class ProcessingResultTransactionSelector extends AbstractTransactionSele
@Override
public TransactionSelectionResult evaluateTransactionPreProcessing(
final TransactionEvaluationContext evaluationContext,
final TransactionSelectionResults blockTransactionResults) {
final TransactionEvaluationContext evaluationContext) {
// All checks depend on processingResult and will be done in the post-processing method, so
// nothing to do here.
return TransactionSelectionResult.SELECTED;
@@ -54,14 +52,12 @@ public class ProcessingResultTransactionSelector extends AbstractTransactionSele
* result.
*
* @param evaluationContext The current selection session data.
* @param blockTransactionResults The results of other transaction evaluations in the same block.
* @param processingResult The processing result of the transaction.
* @return The result of the transaction selection.
*/
@Override
public TransactionSelectionResult evaluateTransactionPostProcessing(
final TransactionEvaluationContext evaluationContext,
final TransactionSelectionResults blockTransactionResults,
final TransactionProcessingResult processingResult) {
if (processingResult.isInvalid()) {

View File

@@ -17,34 +17,41 @@ package org.hyperledger.besu.ethereum.blockcreation.txselection.selectors;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.ethereum.blockcreation.txselection.BlockSelectionContext;
import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionEvaluationContext;
import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionSelectionResults;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.plugin.data.TransactionSelectionResult;
import java.util.HashSet;
import java.util.Set;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SkipSenderTransactionSelector extends AbstractTransactionSelector {
private static final Logger LOG = LoggerFactory.getLogger(SkipSenderTransactionSelector.class);
private final Set<Address> skippedSenders = new HashSet<>();
private final Map<Address, Long> skippedSenderNonceMap = new HashMap<>();
public SkipSenderTransactionSelector(final BlockSelectionContext context) {
super(context);
}
/**
* Check if this pending tx belongs to a sender with a previous pending tx not selected, in which
* case we can safely skip the evaluation due to the nonce gap
*
* @param evaluationContext The current selection session data.
* @return SENDER_WITH_PREVIOUS_TX_NOT_SELECTED if there is a nonce gas for this sender
*/
@Override
public TransactionSelectionResult evaluateTransactionPreProcessing(
final TransactionEvaluationContext evaluationContext,
final TransactionSelectionResults ignored) {
final TransactionEvaluationContext evaluationContext) {
final var sender = evaluationContext.getTransaction().getSender();
if (skippedSenders.contains(sender)) {
final var skippedNonce = skippedSenderNonceMap.get(sender);
if (nonceGap(evaluationContext, skippedNonce)) {
LOG.atTrace()
.setMessage("Not selecting tx {} since its sender {} is in the skip list")
.setMessage("Not selecting tx {} since its sender {} is in the skip list with nonce {}")
.addArgument(() -> evaluationContext.getPendingTransaction().toTraceLog())
.addArgument(sender)
.addArgument(skippedNonce)
.log();
return TransactionSelectionResult.SENDER_WITH_PREVIOUS_TX_NOT_SELECTED;
@@ -52,10 +59,14 @@ public class SkipSenderTransactionSelector extends AbstractTransactionSelector {
return TransactionSelectionResult.SELECTED;
}
private static boolean nonceGap(
final TransactionEvaluationContext evaluationContext, final Long skippedNonce) {
return skippedNonce != null && evaluationContext.getTransaction().getNonce() > skippedNonce;
}
@Override
public TransactionSelectionResult evaluateTransactionPostProcessing(
final TransactionEvaluationContext evaluationContext,
final TransactionSelectionResults blockTransactionResults,
final TransactionProcessingResult processingResult) {
// All necessary checks were done in the pre-processing method, so nothing to do here.
return TransactionSelectionResult.SELECTED;
@@ -66,7 +77,7 @@ public class SkipSenderTransactionSelector extends AbstractTransactionSelector {
* same sender, since it will never be selected due to the nonce gap, so we add the sender to the
* skip list.
*
* @param evaluationContext The current selection context
* @param evaluationContext The current evaluation context
* @param transactionSelectionResult The transaction selection result
*/
@Override
@@ -74,7 +85,27 @@ public class SkipSenderTransactionSelector extends AbstractTransactionSelector {
final TransactionEvaluationContext evaluationContext,
final TransactionSelectionResult transactionSelectionResult) {
final var sender = evaluationContext.getTransaction().getSender();
skippedSenders.add(sender);
LOG.trace("Sender {} added to the skip list", sender);
final var nonce = evaluationContext.getTransaction().getNonce();
skippedSenderNonceMap.put(sender, nonce);
LOG.trace("Sender {} added to the skip list with nonce {}", sender, nonce);
}
/**
* When a transaction is selected we can remove it from the list. This could happen when the same
* pending tx is present both in the internal pool and the plugin pool, and for example it is not
* selected by the plugin but could be later selected from the internal pool.
*
* @param evaluationContext The current evaluation context
* @param processingResult The transaction processing result
*/
@Override
public void onTransactionSelected(
final TransactionEvaluationContext evaluationContext,
final TransactionProcessingResult processingResult) {
final var sender = evaluationContext.getTransaction().getSender();
final var skippedNonce = skippedSenderNonceMap.remove(sender);
if (skippedNonce != null) {
LOG.trace("Sender {} removed from the skip list, skipped nonce was {}", sender, skippedNonce);
}
}
}

View File

@@ -90,6 +90,7 @@ import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.plugin.services.TransactionSelectionService;
import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelector;
import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelectorFactory;
import org.hyperledger.besu.plugin.services.txselection.SelectorsStateManager;
import org.hyperledger.besu.plugin.services.txselection.TransactionEvaluationContext;
import org.hyperledger.besu.services.TransactionSelectionServiceImpl;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
@@ -648,33 +649,41 @@ public abstract class AbstractBlockTransactionSelectorTest {
final Transaction notSelectedInvalid = createTransaction(2, Wei.of(10), 21_000, SENDER2);
ensureTransactionIsValid(notSelectedInvalid, 21_000, 0);
final PluginTransactionSelectorFactory transactionSelectorFactory =
() ->
new PluginTransactionSelector() {
@Override
public TransactionSelectionResult evaluateTransactionPreProcessing(
final TransactionEvaluationContext evaluationContext) {
if (evaluationContext
.getPendingTransaction()
.getTransaction()
.equals(notSelectedTransient))
return PluginTransactionSelectionResult.GENERIC_PLUGIN_INVALID_TRANSIENT;
if (evaluationContext
.getPendingTransaction()
.getTransaction()
.equals(notSelectedInvalid))
return PluginTransactionSelectionResult.GENERIC_PLUGIN_INVALID;
return SELECTED;
}
final PluginTransactionSelector pluginTransactionSelector =
new PluginTransactionSelector() {
@Override
public TransactionSelectionResult evaluateTransactionPreProcessing(
final TransactionEvaluationContext evaluationContext) {
if (evaluationContext
.getPendingTransaction()
.getTransaction()
.equals(notSelectedTransient))
return PluginTransactionSelectionResult.GENERIC_PLUGIN_INVALID_TRANSIENT;
if (evaluationContext
.getPendingTransaction()
.getTransaction()
.equals(notSelectedInvalid))
return PluginTransactionSelectionResult.GENERIC_PLUGIN_INVALID;
return SELECTED;
}
@Override
public TransactionSelectionResult evaluateTransactionPostProcessing(
final TransactionEvaluationContext evaluationContext,
final org.hyperledger.besu.plugin.data.TransactionProcessingResult processingResult) {
return SELECTED;
}
};
final PluginTransactionSelectorFactory transactionSelectorFactory =
new PluginTransactionSelectorFactory() {
@Override
public PluginTransactionSelector create(
final SelectorsStateManager selectorsStateManager) {
return pluginTransactionSelector;
}
};
@Override
public TransactionSelectionResult evaluateTransactionPostProcessing(
final TransactionEvaluationContext evaluationContext,
final org.hyperledger.besu.plugin.data.TransactionProcessingResult
processingResult) {
return SELECTED;
}
};
transactionSelectionService.registerPluginTransactionSelectorFactory(
transactionSelectorFactory);
@@ -717,30 +726,35 @@ public abstract class AbstractBlockTransactionSelectorTest {
final Transaction notSelected = createTransaction(1, Wei.of(10), 30_000);
ensureTransactionIsValid(notSelected, maxGasUsedByTransaction + 1, 0);
final Transaction selected3 = createTransaction(3, Wei.of(10), 21_000);
ensureTransactionIsValid(selected3, maxGasUsedByTransaction, 0);
final PluginTransactionSelector pluginTransactionSelector =
new PluginTransactionSelector() {
@Override
public TransactionSelectionResult evaluateTransactionPreProcessing(
final TransactionEvaluationContext evaluationContext) {
return SELECTED;
}
@Override
public TransactionSelectionResult evaluateTransactionPostProcessing(
final TransactionEvaluationContext evaluationContext,
final org.hyperledger.besu.plugin.data.TransactionProcessingResult processingResult) {
// the transaction with max gas +1 should fail
if (processingResult.getEstimateGasUsedByTransaction() > maxGasUsedByTransaction) {
return PluginTransactionSelectionResult.GENERIC_PLUGIN_INVALID_TRANSIENT;
}
return SELECTED;
}
};
final PluginTransactionSelectorFactory transactionSelectorFactory =
() ->
new PluginTransactionSelector() {
@Override
public TransactionSelectionResult evaluateTransactionPreProcessing(
final TransactionEvaluationContext evaluationContext) {
return SELECTED;
}
new PluginTransactionSelectorFactory() {
@Override
public PluginTransactionSelector create(
final SelectorsStateManager selectorsStateManager) {
return pluginTransactionSelector;
}
};
@Override
public TransactionSelectionResult evaluateTransactionPostProcessing(
final TransactionEvaluationContext evaluationContext,
final org.hyperledger.besu.plugin.data.TransactionProcessingResult
processingResult) {
// the transaction with max gas +1 should fail
if (processingResult.getEstimateGasUsedByTransaction() > maxGasUsedByTransaction) {
return PluginTransactionSelectionResult.GENERIC_PLUGIN_INVALID_TRANSIENT;
}
return SELECTED;
}
};
transactionSelectionService.registerPluginTransactionSelectorFactory(
transactionSelectorFactory);
@@ -776,7 +790,7 @@ public abstract class AbstractBlockTransactionSelectorTest {
PluginTransactionSelector transactionSelector = mock(PluginTransactionSelector.class);
when(transactionSelector.evaluateTransactionPreProcessing(any())).thenReturn(SELECTED);
when(transactionSelector.evaluateTransactionPostProcessing(any(), any())).thenReturn(SELECTED);
when(transactionSelectorFactory.create()).thenReturn(transactionSelector);
when(transactionSelectorFactory.create(any())).thenReturn(transactionSelector);
transactionSelectionService.registerPluginTransactionSelectorFactory(
transactionSelectorFactory);
@@ -1070,7 +1084,7 @@ public abstract class AbstractBlockTransactionSelectorTest {
final PluginTransactionSelectorFactory transactionSelectorFactory =
mock(PluginTransactionSelectorFactory.class);
when(transactionSelectorFactory.create()).thenReturn(transactionSelector);
when(transactionSelectorFactory.create(any())).thenReturn(transactionSelector);
transactionSelectionService.registerPluginTransactionSelectorFactory(
transactionSelectorFactory);
@@ -1233,7 +1247,7 @@ public abstract class AbstractBlockTransactionSelectorTest {
final PluginTransactionSelectorFactory transactionSelectorFactory =
mock(PluginTransactionSelectorFactory.class);
when(transactionSelectorFactory.create()).thenReturn(transactionSelector);
when(transactionSelectorFactory.create(any())).thenReturn(transactionSelector);
transactionSelectionService.registerPluginTransactionSelectorFactory(
transactionSelectorFactory);
@@ -1320,6 +1334,7 @@ public abstract class AbstractBlockTransactionSelectorTest {
final Wei blobGasPrice,
final TransactionSelectionService transactionSelectionService) {
final var selectorsStateManager = new SelectorsStateManager();
final BlockTransactionSelector selector =
new BlockTransactionSelector(
miningConfiguration,
@@ -1336,8 +1351,9 @@ public abstract class AbstractBlockTransactionSelectorTest {
new LondonGasCalculator(),
GasLimitCalculator.constant(),
protocolSchedule.getByBlockHeader(blockHeader).getBlockHashProcessor(),
transactionSelectionService.createPluginTransactionSelector(),
ethScheduler);
transactionSelectionService.createPluginTransactionSelector(selectorsStateManager),
ethScheduler,
selectorsStateManager);
return selector;
}

View File

@@ -16,10 +16,11 @@ package org.hyperledger.besu.ethereum.blockcreation.txselection.selectors;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail;
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.BLOBS_FULL;
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.SELECTED;
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.TX_TOO_LARGE_FOR_REMAINING_BLOB_GAS;
import static org.mockito.Answers.RETURNS_DEEP_STUBS;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.crypto.KeyPair;
@@ -36,12 +37,13 @@ import org.hyperledger.besu.datatypes.VersionedHash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.blockcreation.txselection.BlockSelectionContext;
import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionEvaluationContext;
import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionSelectionResults;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator;
import org.hyperledger.besu.plugin.data.TransactionSelectionResult;
import org.hyperledger.besu.plugin.services.txselection.SelectorsStateManager;
import java.util.Optional;
import java.util.stream.IntStream;
@@ -50,6 +52,7 @@ import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes48;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
@@ -68,86 +71,103 @@ class BlobSizeTransactionSelectorTest {
@Mock(answer = RETURNS_DEEP_STUBS)
BlockSelectionContext blockSelectionContext;
@Mock TransactionSelectionResults selectionResults;
@Mock TransactionProcessingResult transactionProcessingResult;
SelectorsStateManager selectorsStateManager;
BlobSizeTransactionSelector selector;
@BeforeEach
void setup() {
when(blockSelectionContext.gasLimitCalculator().currentBlobGasLimit()).thenReturn(MAX_BLOB_GAS);
when(blockSelectionContext.gasCalculator().blobGasCost(anyLong()))
.thenAnswer(iom -> BLOB_GAS_PER_BLOB * iom.getArgument(0, Long.class));
selectorsStateManager = new SelectorsStateManager();
selector = new BlobSizeTransactionSelector(blockSelectionContext, selectorsStateManager);
}
@Test
void notBlobTransactionsAreSelectedWithoutAnyCheck() {
final var selector = new BlobSizeTransactionSelector(blockSelectionContext);
final var nonBlobTx = createEIP1559PendingTransaction();
void notBlobTransactionsAreAlwaysSelected() {
// this tx fills all the available blob space
final var firstBlobTx = createBlobPendingTransaction(MAX_BLOBS);
final var txEvaluationContext =
new TransactionEvaluationContext(
blockSelectionContext.pendingBlockHeader(), nonBlobTx, null, null, null);
blockSelectionContext.pendingBlockHeader(), firstBlobTx, null, null, null);
selectorsStateManager.blockSelectionStarted();
evaluateAndAssertSelected(txEvaluationContext);
final var result =
selector.evaluateTransactionPreProcessing(txEvaluationContext, selectionResults);
assertThat(result).isEqualTo(TransactionSelectionResult.SELECTED);
verifyNoInteractions(selectionResults);
// this non blob tx is selected regardless the blob space is already filled
final var nonBlobTx = createEIP1559PendingTransaction();
final var nonBlobTxEvaluationContext =
new TransactionEvaluationContext(
blockSelectionContext.pendingBlockHeader(), nonBlobTx, null, null, null);
selectorsStateManager.blockSelectionStarted();
evaluateAndAssertSelected(nonBlobTxEvaluationContext);
}
@Test
void firstBlobTransactionIsSelected() {
when(blockSelectionContext.gasLimitCalculator().currentBlobGasLimit()).thenReturn(MAX_BLOB_GAS);
when(blockSelectionContext.gasCalculator().blobGasCost(anyLong()))
.thenAnswer(iom -> BLOB_GAS_PER_BLOB * iom.getArgument(0, Long.class));
final var selector = new BlobSizeTransactionSelector(blockSelectionContext);
final var firstBlobTx = createBlobPendingTransaction(MAX_BLOBS);
final var txEvaluationContext =
new TransactionEvaluationContext(
blockSelectionContext.pendingBlockHeader(), firstBlobTx, null, null, null);
when(selectionResults.getCumulativeBlobGasUsed()).thenReturn(0L);
final var result =
selector.evaluateTransactionPreProcessing(txEvaluationContext, selectionResults);
assertThat(result).isEqualTo(TransactionSelectionResult.SELECTED);
verify(selectionResults).getCumulativeBlobGasUsed();
selectorsStateManager.blockSelectionStarted();
evaluateAndAssertSelected(txEvaluationContext);
}
@Test
void returnsBlobsFullWhenMaxNumberOfBlobsAlreadyPresent() {
when(blockSelectionContext.gasLimitCalculator().currentBlobGasLimit()).thenReturn(MAX_BLOB_GAS);
final var selector = new BlobSizeTransactionSelector(blockSelectionContext);
final var firstBlobTx = createBlobPendingTransaction(1);
final var txEvaluationContext =
final var blobTx1 = createBlobPendingTransaction(MAX_BLOBS);
final var txEvaluationContext1 =
new TransactionEvaluationContext(
blockSelectionContext.pendingBlockHeader(), firstBlobTx, null, null, null);
blockSelectionContext.pendingBlockHeader(), blobTx1, null, null, null);
when(selectionResults.getCumulativeBlobGasUsed()).thenReturn(MAX_BLOB_GAS);
selectorsStateManager.blockSelectionStarted();
evaluateAndAssertSelected(txEvaluationContext1);
final var result =
selector.evaluateTransactionPreProcessing(txEvaluationContext, selectionResults);
assertThat(result).isEqualTo(TransactionSelectionResult.BLOBS_FULL);
verify(selectionResults).getCumulativeBlobGasUsed();
final var blobTx2 = createBlobPendingTransaction(1);
final var txEvaluationContext2 =
new TransactionEvaluationContext(
blockSelectionContext.pendingBlockHeader(), blobTx2, null, null, null);
selectorsStateManager.blockSelectionStarted();
evaluateAndAssertNotSelected(txEvaluationContext2, BLOBS_FULL);
}
@Test
void returnsTooLargeForRemainingBlobGas() {
when(blockSelectionContext.gasLimitCalculator().currentBlobGasLimit()).thenReturn(MAX_BLOB_GAS);
when(blockSelectionContext.gasCalculator().blobGasCost(anyLong()))
.thenAnswer(iom -> BLOB_GAS_PER_BLOB * iom.getArgument(0, Long.class));
final var selector = new BlobSizeTransactionSelector(blockSelectionContext);
final var firstBlobTx = createBlobPendingTransaction(MAX_BLOBS);
final var txEvaluationContext =
// first tx only fill the space for one blob leaving space max MAX_BLOB_GAS-1 blobs to be added
// later
final var blobTx1 = createBlobPendingTransaction(1);
final var txEvaluationContext1 =
new TransactionEvaluationContext(
blockSelectionContext.pendingBlockHeader(), firstBlobTx, null, null, null);
blockSelectionContext.pendingBlockHeader(), blobTx1, null, null, null);
selectorsStateManager.blockSelectionStarted();
evaluateAndAssertSelected(txEvaluationContext1);
when(selectionResults.getCumulativeBlobGasUsed()).thenReturn(MAX_BLOB_GAS - 1);
final var blobTx2 = createBlobPendingTransaction(MAX_BLOBS);
final var txEvaluationContext2 =
new TransactionEvaluationContext(
blockSelectionContext.pendingBlockHeader(), blobTx2, null, null, null);
selectorsStateManager.blockSelectionStarted();
evaluateAndAssertNotSelected(txEvaluationContext2, TX_TOO_LARGE_FOR_REMAINING_BLOB_GAS);
}
final var result =
selector.evaluateTransactionPreProcessing(txEvaluationContext, selectionResults);
assertThat(result).isEqualTo(TransactionSelectionResult.TX_TOO_LARGE_FOR_REMAINING_BLOB_GAS);
verify(selectionResults).getCumulativeBlobGasUsed();
private void evaluateAndAssertSelected(final TransactionEvaluationContext txEvaluationContext) {
assertThat(selector.evaluateTransactionPreProcessing(txEvaluationContext)).isEqualTo(SELECTED);
assertThat(
selector.evaluateTransactionPostProcessing(
txEvaluationContext, transactionProcessingResult))
.isEqualTo(SELECTED);
}
private void evaluateAndAssertNotSelected(
final TransactionEvaluationContext txEvaluationContext,
final TransactionSelectionResult preProcessedResult) {
assertThat(selector.evaluateTransactionPreProcessing(txEvaluationContext))
.isEqualTo(preProcessedResult);
}
private PendingTransaction createEIP1559PendingTransaction() {

View File

@@ -0,0 +1,272 @@
/*
* 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.blockcreation.txselection.selectors;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.BLOCK_FULL;
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.BLOCK_OCCUPANCY_ABOVE_THRESHOLD;
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.SELECTED;
import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.TX_TOO_LARGE_FOR_REMAINING_GAS;
import static org.mockito.Answers.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.TransactionType;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.blockcreation.txselection.BlockSelectionContext;
import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionEvaluationContext;
import org.hyperledger.besu.ethereum.core.MiningConfiguration;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransaction;
import org.hyperledger.besu.ethereum.processing.TransactionProcessingResult;
import org.hyperledger.besu.plugin.data.TransactionSelectionResult;
import org.hyperledger.besu.plugin.services.txselection.SelectorsStateManager;
import java.util.Optional;
import java.util.stream.IntStream;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class BlockSizeTransactionSelectorTest {
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
private static final KeyPair KEYS = SIGNATURE_ALGORITHM.get().generateKeyPair();
private static final long TRANSFER_GAS_LIMIT = 21_000L;
private static final long BLOCK_GAS_LIMIT = 1_000_000L;
@Mock(answer = RETURNS_DEEP_STUBS)
BlockSelectionContext blockSelectionContext;
SelectorsStateManager selectorsStateManager;
BlockSizeTransactionSelector selector;
MiningConfiguration miningConfiguration;
@BeforeEach
void setup() {
miningConfiguration = MiningConfiguration.newDefault();
when(blockSelectionContext.pendingBlockHeader().getGasLimit()).thenReturn(BLOCK_GAS_LIMIT);
when(blockSelectionContext.miningConfiguration()).thenReturn(miningConfiguration);
selectorsStateManager = new SelectorsStateManager();
selector = new BlockSizeTransactionSelector(blockSelectionContext, selectorsStateManager);
}
@Test
void singleTransactionBelowBlockGasLimitIsSelected() {
final var tx = createPendingTransaction(TRANSFER_GAS_LIMIT);
final var txEvaluationContext =
new TransactionEvaluationContext(
blockSelectionContext.pendingBlockHeader(), tx, null, null, null);
selectorsStateManager.blockSelectionStarted();
evaluateAndAssertSelected(txEvaluationContext, remainingGas(0));
assertThat(selector.getWorkingState()).isEqualTo(TRANSFER_GAS_LIMIT);
}
@Test
void singleTransactionAboveBlockGasLimitIsNotSelected() {
final var tx = createPendingTransaction(BLOCK_GAS_LIMIT + 1);
final var txEvaluationContext =
new TransactionEvaluationContext(
blockSelectionContext.pendingBlockHeader(), tx, null, null, null);
selectorsStateManager.blockSelectionStarted();
evaluateAndAssertNotSelected(txEvaluationContext, TX_TOO_LARGE_FOR_REMAINING_GAS);
assertThat(selector.getWorkingState()).isEqualTo(0);
}
@Test
void correctlyCumulatesOnlyTheEffectiveGasUsedAfterProcessing() {
final var tx = createPendingTransaction(TRANSFER_GAS_LIMIT * 2);
final long remainingGas = 100;
final var txEvaluationContext =
new TransactionEvaluationContext(
blockSelectionContext.pendingBlockHeader(), tx, null, null, null);
selectorsStateManager.blockSelectionStarted();
evaluateAndAssertSelected(txEvaluationContext, remainingGas(remainingGas));
assertThat(selector.getWorkingState()).isEqualTo(TRANSFER_GAS_LIMIT * 2 - remainingGas);
}
@Test
void moreTransactionsBelowBlockGasLimitAreSelected() {
selectorsStateManager.blockSelectionStarted();
final int txCount = 10;
IntStream.range(0, txCount)
.forEach(
unused -> {
final var tx = createPendingTransaction(TRANSFER_GAS_LIMIT);
final var txEvaluationContext =
new TransactionEvaluationContext(
blockSelectionContext.pendingBlockHeader(), tx, null, null, null);
evaluateAndAssertSelected(txEvaluationContext, remainingGas(0));
});
assertThat(selector.getWorkingState()).isEqualTo(TRANSFER_GAS_LIMIT * txCount);
}
@Test
void moreTransactionsThanBlockCanFitOnlySomeAreSelected() {
selectorsStateManager.blockSelectionStarted();
final int txCount = 10;
IntStream.range(0, txCount)
.forEach(
unused -> {
final var tx = createPendingTransaction(TRANSFER_GAS_LIMIT);
final var txEvaluationContext =
new TransactionEvaluationContext(
blockSelectionContext.pendingBlockHeader(), tx, null, null, null);
evaluateAndAssertSelected(txEvaluationContext, remainingGas(0));
});
assertThat(selector.getWorkingState()).isEqualTo(TRANSFER_GAS_LIMIT * txCount);
// last tx is too big for the remaining gas
final long tooBigGasLimit = BLOCK_GAS_LIMIT - (TRANSFER_GAS_LIMIT * txCount) + 1;
final var bigTxEvaluationContext =
new TransactionEvaluationContext(
blockSelectionContext.pendingBlockHeader(),
createPendingTransaction(tooBigGasLimit),
null,
null,
null);
evaluateAndAssertNotSelected(bigTxEvaluationContext, TX_TOO_LARGE_FOR_REMAINING_GAS);
assertThat(selector.getWorkingState()).isEqualTo(TRANSFER_GAS_LIMIT * txCount);
}
@Test
void identifyWhenBlockOccupancyIsAboveThreshold() {
selectorsStateManager.blockSelectionStarted();
// create 2 txs with a gas limit just above the min block occupancy ratio
// so the first is accepted while the second not
final long justAboveOccupancyRatioGasLimit =
(long) (BLOCK_GAS_LIMIT * miningConfiguration.getMinBlockOccupancyRatio()) + 100;
final var tx1 = createPendingTransaction(justAboveOccupancyRatioGasLimit);
final var txEvaluationContext1 =
new TransactionEvaluationContext(
blockSelectionContext.pendingBlockHeader(), tx1, null, null, null);
evaluateAndAssertSelected(txEvaluationContext1, remainingGas(0));
assertThat(selector.getWorkingState()).isEqualTo(justAboveOccupancyRatioGasLimit);
final var tx2 = createPendingTransaction(justAboveOccupancyRatioGasLimit);
final var txEvaluationContext2 =
new TransactionEvaluationContext(
blockSelectionContext.pendingBlockHeader(), tx2, null, null, null);
evaluateAndAssertNotSelected(txEvaluationContext2, BLOCK_OCCUPANCY_ABOVE_THRESHOLD);
assertThat(selector.getWorkingState()).isEqualTo(justAboveOccupancyRatioGasLimit);
}
@Test
void identifyWhenBlockIsFull() {
when(blockSelectionContext.gasCalculator().getMinimumTransactionCost())
.thenReturn(TRANSFER_GAS_LIMIT);
selectorsStateManager.blockSelectionStarted();
// allow to completely fill the block
miningConfiguration.setMinBlockOccupancyRatio(1.0);
// create 2 txs, where the first fill the block leaving less gas than the min required by a
// transfer
final long fillBlockGasLimit = BLOCK_GAS_LIMIT - TRANSFER_GAS_LIMIT + 1;
final var tx1 = createPendingTransaction(fillBlockGasLimit);
final var txEvaluationContext1 =
new TransactionEvaluationContext(
blockSelectionContext.pendingBlockHeader(), tx1, null, null, null);
evaluateAndAssertSelected(txEvaluationContext1, remainingGas(0));
assertThat(selector.getWorkingState()).isEqualTo(fillBlockGasLimit);
final var tx2 = createPendingTransaction(TRANSFER_GAS_LIMIT);
final var txEvaluationContext2 =
new TransactionEvaluationContext(
blockSelectionContext.pendingBlockHeader(), tx2, null, null, null);
evaluateAndAssertNotSelected(txEvaluationContext2, BLOCK_FULL);
assertThat(selector.getWorkingState()).isEqualTo(fillBlockGasLimit);
}
private void evaluateAndAssertSelected(
final TransactionEvaluationContext txEvaluationContext,
final TransactionProcessingResult transactionProcessingResult) {
assertThat(selector.evaluateTransactionPreProcessing(txEvaluationContext)).isEqualTo(SELECTED);
assertThat(
selector.evaluateTransactionPostProcessing(
txEvaluationContext, transactionProcessingResult))
.isEqualTo(SELECTED);
}
private void evaluateAndAssertNotSelected(
final TransactionEvaluationContext txEvaluationContext,
final TransactionSelectionResult preProcessedResult) {
assertThat(selector.evaluateTransactionPreProcessing(txEvaluationContext))
.isEqualTo(preProcessedResult);
}
private PendingTransaction createPendingTransaction(final long gasLimit) {
return PendingTransaction.newPendingTransaction(
createTransaction(TransactionType.EIP1559, gasLimit), false, false);
}
private Transaction createTransaction(final TransactionType type, final long gasLimit) {
var tx =
new TransactionTestFixture()
.to(Optional.of(Address.fromHexString("0x634316eA0EE79c701c6F67C53A4C54cBAfd2316d")))
.nonce(0)
.gasLimit(gasLimit)
.type(type)
.maxFeePerGas(Optional.of(Wei.of(1000)))
.maxPriorityFeePerGas(Optional.of(Wei.of(100)));
return tx.createTransaction(KEYS);
}
private TransactionProcessingResult remainingGas(final long remainingGas) {
final var txProcessingResult = mock(TransactionProcessingResult.class);
when(txProcessingResult.getGasRemaining()).thenReturn(remainingGas);
return txProcessingResult;
}
}

View File

@@ -12,7 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.blockcreation;
package org.hyperledger.besu.ethereum.blockcreation.txselection.selectors;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -22,8 +22,6 @@ import static org.mockito.Mockito.when;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.blockcreation.txselection.BlockSelectionContext;
import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionEvaluationContext;
import org.hyperledger.besu.ethereum.blockcreation.txselection.selectors.AbstractTransactionSelector;
import org.hyperledger.besu.ethereum.blockcreation.txselection.selectors.MinPriorityFeePerGasTransactionSelector;
import org.hyperledger.besu.ethereum.core.MiningConfiguration;
import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader;
import org.hyperledger.besu.ethereum.core.Transaction;
@@ -85,8 +83,7 @@ public class MinPriorityFeePerGasTransactionSelectorTest {
private void assertSelectionResult(
final TransactionEvaluationContext evaluationContext,
final TransactionSelectionResult expectedResult) {
var actualResult =
transactionSelector.evaluateTransactionPreProcessing(evaluationContext, null);
var actualResult = transactionSelector.evaluateTransactionPreProcessing(evaluationContext);
assertThat(actualResult).isEqualTo(expectedResult);
}

View File

@@ -16,9 +16,12 @@ package org.hyperledger.besu.ethereum.core;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.plugin.data.ProcessableBlockHeader;
import org.hyperledger.besu.plugin.services.TransactionSelectionService;
import org.hyperledger.besu.plugin.services.txselection.BlockTransactionSelectionService;
import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelector;
import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelectorFactory;
import org.hyperledger.besu.plugin.services.txselection.SelectorsStateManager;
import org.hyperledger.besu.util.number.PositiveNumber;
import java.time.Duration;
@@ -168,10 +171,16 @@ public abstract class MiningConfiguration {
public TransactionSelectionService getTransactionSelectionService() {
return new TransactionSelectionService() {
@Override
public PluginTransactionSelector createPluginTransactionSelector() {
public PluginTransactionSelector createPluginTransactionSelector(
final SelectorsStateManager selectorsStateManager) {
return PluginTransactionSelector.ACCEPT_ALL;
}
@Override
public void selectPendingTransactions(
final BlockTransactionSelectionService selectionService,
final ProcessableBlockHeader pendingBlockHeader) {}
@Override
public void registerPluginTransactionSelectorFactory(
final PluginTransactionSelectorFactory transactionSelectorFactory) {}

View File

@@ -1,5 +1,5 @@
/*
* Copyright ConsenSys AG.
* 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
@@ -18,6 +18,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider.createInMemoryBlockchain;
import static org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider.createInMemoryWorldStateArchive;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.ethereum.ConsensusContext;
import org.hyperledger.besu.ethereum.ProtocolContext;
@@ -49,31 +50,28 @@ import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Stream;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.Mockito;
public class DetermineCommonAncestorTaskParameterizedTest {
public abstract class AbstractDetermineCommonAncestorTaskParameterizedTest {
private final ProtocolSchedule protocolSchedule = ProtocolScheduleFixture.MAINNET;
private static final BlockDataGenerator blockDataGenerator = new BlockDataGenerator();
private final MetricsSystem metricsSystem = new NoOpMetricsSystem();
private static Block genesisBlock;
private static MutableBlockchain localBlockchain;
private static final int chainHeight = 50;
protected static final int chainHeight = 50;
private MutableBlockchain remoteBlockchain;
private PeerTaskExecutor peerTaskExecutor;
@@ -98,19 +96,7 @@ public class DetermineCommonAncestorTaskParameterizedTest {
@BeforeEach
public void setup() {
remoteBlockchain = createInMemoryBlockchain(genesisBlock);
peerTaskExecutor = Mockito.mock(PeerTaskExecutor.class);
}
public static Stream<Arguments> parameters() throws IOException {
final int[] requestSizes = {5, 12, chainHeight, chainHeight * 2};
final Stream.Builder<Arguments> builder = Stream.builder();
for (final int requestSize : requestSizes) {
for (int i = 0; i <= chainHeight; i++) {
builder.add(Arguments.of(requestSize, i, true));
builder.add(Arguments.of(requestSize, i, false));
}
}
return builder.build();
peerTaskExecutor = mock(PeerTaskExecutor.class);
}
@ParameterizedTest(name = "requestSize={0}, commonAncestor={1}, isPeerTaskSystemEnabled={2}")
@@ -187,10 +173,8 @@ public class DetermineCommonAncestorTaskParameterizedTest {
.build(),
metricsSystem);
Mockito.when(
peerTaskExecutor.executeAgainstPeer(
Mockito.any(GetHeadersFromPeerTask.class),
Mockito.eq(respondingEthPeer.getEthPeer())))
when(peerTaskExecutor.executeAgainstPeer(
Mockito.any(GetHeadersFromPeerTask.class), Mockito.eq(respondingEthPeer.getEthPeer())))
.thenAnswer(
(invocationOnMock) -> {
GetHeadersFromPeerTask getHeadersTask =
@@ -205,7 +189,7 @@ public class DetermineCommonAncestorTaskParameterizedTest {
headers.add(remoteBlockchain.getBlockHeader(i).get());
}
return new PeerTaskExecutorResult<List<BlockHeader>>(
return new PeerTaskExecutorResult<>(
Optional.of(headers),
PeerTaskExecutorResponseCode.SUCCESS,
Optional.of(respondingEthPeer.getEthPeer()));

View File

@@ -0,0 +1,34 @@
/*
* 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.eth.sync.tasks;
import java.util.stream.Stream;
import org.junit.jupiter.params.provider.Arguments;
public class DetermineCommonAncestorTaskParameterizedTest1
extends AbstractDetermineCommonAncestorTaskParameterizedTest {
public static Stream<Arguments> parameters() {
final int[] requestSizes = {5, 12, chainHeight, chainHeight * 2};
final Stream.Builder<Arguments> builder = Stream.builder();
for (final int requestSize : requestSizes) {
for (int i = 0; i <= chainHeight; i++) {
builder.add(Arguments.of(requestSize, i, true));
}
}
return builder.build();
}
}

View File

@@ -0,0 +1,34 @@
/*
* 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.eth.sync.tasks;
import java.util.stream.Stream;
import org.junit.jupiter.params.provider.Arguments;
public class DetermineCommonAncestorTaskParameterizedTest2
extends AbstractDetermineCommonAncestorTaskParameterizedTest {
public static Stream<Arguments> parameters() {
final int[] requestSizes = {5, 12, chainHeight, chainHeight * 2};
final Stream.Builder<Arguments> builder = Stream.builder();
for (final int requestSize : requestSizes) {
for (int i = 0; i <= chainHeight; i++) {
builder.add(Arguments.of(requestSize, i, false));
}
}
return builder.build();
}
}

View File

@@ -9,6 +9,6 @@
"stdout": [
{"pc":0,"op":96,"gas":"0x2540be400","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"},
{"pc":2,"op":239,"gas":"0x2540be3fd","gasCost":"0x0","memSize":0,"stack":["0x0"],"depth":1,"refund":0,"opName":"INVALID","error":"Invalid opcode: 0xef"},
{"stateRoot":"0xfc9dc1be50c1b0a497afa545d770cc7064f0d71efbc4338f002dc2e086965d98","output":"0x","gasUsed":"0x2540be400","pass":false,"fork":"Cancun"}
{"stateRoot":"0xfc9dc1be50c1b0a497afa545d770cc7064f0d71efbc4338f002dc2e086965d98","output":"0x","gasUsed":"0x2540be400","pass":false,"fork":"Prague"}
]
}

View File

@@ -70,6 +70,6 @@
{"pc":24,"op":243,"gas":"0x45","gasCost":"0x0","memSize":96,"stack":["0x1","0x40"],"depth":2,"refund":0,"opName":"RETURN"},
{"pc":22,"op":96,"gas":"0x71","gasCost":"0x3","memSize":96,"stack":["0x1"],"depth":1,"refund":0,"opName":"PUSH1"},
{"pc":24,"op":243,"gas":"0x6e","gasCost":"0x0","memSize":96,"stack":["0x1","0x40"],"depth":1,"refund":0,"opName":"RETURN"},
{"stateRoot":"0xcb5e8e232189003640b6f131ea2c09b1791ffd2e8357f64610f638e9a11ab2d2","output":"0x40","gasUsed":"0x619e","pass":true,"fork":"Cancun"}
{"stateRoot":"0xcb5e8e232189003640b6f131ea2c09b1791ffd2e8357f64610f638e9a11ab2d2","output":"0x40","gasUsed":"0x619e","pass":true,"fork":"Prague"}
]
}

View File

@@ -13,6 +13,6 @@
{"pc":8,"op":96,"gas":"0x2540be3f4","gasCost":"0x3","memSize":32,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"},
{"pc":10,"op":96,"gas":"0x2540be3f1","gasCost":"0x3","memSize":32,"stack":["0x4"],"depth":1,"refund":0,"opName":"PUSH1"},
{"pc":12,"op":253,"gas":"0x2540be3ee","gasCost":"0x0","memSize":32,"stack":["0x4","0x1c"],"depth":1,"refund":0,"opName":"REVERT","error":"0x4e6f7065"},
{"stateRoot":"0x405bbd98da2aca6dff77f79e0b270270c48d6a3e07b76db675b20e454b50bbcb","output":"0x4e6f7065","gasUsed":"0x12","pass":true,"fork":"Cancun"}
{"stateRoot":"0x405bbd98da2aca6dff77f79e0b270270c48d6a3e07b76db675b20e454b50bbcb","output":"0x4e6f7065","gasUsed":"0x12","pass":true,"fork":"Prague"}
]
}

View File

@@ -67,6 +67,6 @@
{"pc":24,"op":243,"gas":"0x45","gasCost":"0x0","memSize":96,"stack":["0x1","0x40"],"depth":2,"refund":0,"opName":"RETURN"},
{"pc":22,"op":96,"gas":"0x71","gasCost":"0x3","memSize":96,"stack":["0x1"],"depth":1,"refund":0,"opName":"PUSH1"},
{"pc":24,"op":243,"gas":"0x6e","gasCost":"0x0","memSize":96,"stack":["0x1","0x40"],"depth":1,"refund":0,"opName":"RETURN"},
{"stateRoot":"0xcb5e8e232189003640b6f131ea2c09b1791ffd2e8357f64610f638e9a11ab2d2","output":"0x40","gasUsed":"0x619e","pass":true,"fork":"Cancun"}
{"stateRoot":"0xcb5e8e232189003640b6f131ea2c09b1791ffd2e8357f64610f638e9a11ab2d2","output":"0x40","gasUsed":"0x619e","pass":true,"fork":"Prague"}
]
}

View File

@@ -49,7 +49,7 @@ class EVMExecutorTest {
@Test
void currentEVM() {
var subject = EVMExecutor.evm();
assertThat(subject.getEVMVersion()).isEqualTo(EvmSpecVersion.CANCUN);
assertThat(subject.getEVMVersion()).isEqualTo(EvmSpecVersion.PRAGUE);
}
@ParameterizedTest

View File

@@ -1903,6 +1903,14 @@
<sha256 value="63bc9f25de4e53d6b7fee665fa5e4c73236a4015af3d388db99038b84f79fef5" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-all" version="4.1.118.Final">
<artifact name="netty-all-4.1.118.Final.jar">
<sha256 value="44790974f057722b9d9d19dcb7afb91504eeb46d81fa2494db4f0fe01c5b7242" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-all-4.1.118.Final.pom">
<sha256 value="634075f04ebe719a681fc7a653e43900b0ff20104d6705d5852c1a6ec8b2cc83" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-bom" version="4.1.111.Final">
<artifact name="netty-bom-4.1.111.Final.pom">
<sha256 value="2f113902364208bbb32d551e062b05ab219692fbc12bd48de1d83c7576e5b470" origin="Generated by Gradle"/>
@@ -1913,6 +1921,11 @@
<sha256 value="25dc8bb8337dfc186c49fc8cf4f6a5b6c7cf4146362f5f440dacadcd0df9761b" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-bom" version="4.1.118.Final">
<artifact name="netty-bom-4.1.118.Final.pom">
<sha256 value="900f78fd6eb8c6a67b4c9a39d9e5cd96f9eca270b12d7090bc5057efe7e8286a" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-buffer" version="4.1.111.Final">
<artifact name="netty-buffer-4.1.111.Final.pom">
<sha256 value="6afc7e751e0a90d2cb1168f6a4854d80bf3ba888104afab303233cd31684f4b4" origin="Generated by Gradle"/>
@@ -1926,6 +1939,14 @@
<sha256 value="94a3581ea9e4ffc385bb237960f68ef1e84089f164eae8629e8fd66d8aaf0139" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-buffer" version="4.1.118.Final">
<artifact name="netty-buffer-4.1.118.Final.jar">
<sha256 value="0eea4e8666a9636a28722661d8ba5fa8564477e75fec6dd2ff3e324e361f8b3c" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-buffer-4.1.118.Final.pom">
<sha256 value="95cbe6e3e341ee49bcffd3257cf78fa5785e465b77a734bcfc2f93509d43fe64" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-codec" version="4.1.115.Final">
<artifact name="netty-codec-4.1.115.Final.jar">
<sha256 value="cd189afb70ec6eacfcdfdd3a5f472b4e705a5c91d5bd3ef0386421f2ae15ec77" origin="Generated by Gradle"/>
@@ -1934,6 +1955,14 @@
<sha256 value="ac166597e81179ca8300276605408910cc030efec12236ce9c38fefc16801aa0" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-codec" version="4.1.118.Final">
<artifact name="netty-codec-4.1.118.Final.jar">
<sha256 value="4abd215fd1ed7ce86509d169cc9cbede5042176c265a79b3b70602b017226c3f" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-codec-4.1.118.Final.pom">
<sha256 value="0f1d81390dca4588d87d3fe15d8c52f26bb2ea6d80c90ab93a97e64dfd7a1cdd" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-codec-dns" version="4.1.115.Final">
<artifact name="netty-codec-dns-4.1.115.Final.jar">
<sha256 value="23dd6806bcc326855f13e69838c6411d0490e6b1aeb12e217a19a3dd6ad3f10d" origin="Generated by Gradle"/>
@@ -1942,6 +1971,14 @@
<sha256 value="73976d702597a4d7597f82634fd17d1161a486972c7306b7e7cccb582533ea4e" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-codec-dns" version="4.1.118.Final">
<artifact name="netty-codec-dns-4.1.118.Final.jar">
<sha256 value="e115e42ca1e3cc8d85e3a632d8faa102d18c0ebc1fa4511af30bec79f8c147d4" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-codec-dns-4.1.118.Final.pom">
<sha256 value="dc1e18117cf0d84983aef3774b15bf8eff9a56fae98ce98a4cedb69cc07e471a" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-codec-haproxy" version="4.1.115.Final">
<artifact name="netty-codec-haproxy-4.1.115.Final.jar">
<sha256 value="7514f1d10dda3af9468b57bbb0f63fe0b8abb55dbb8190bede95319f37228322" origin="Generated by Gradle"/>
@@ -1950,6 +1987,14 @@
<sha256 value="83e802589a080559e692e554a382119d9f88e5145b09fa4cc130a2655c44f8b4" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-codec-haproxy" version="4.1.118.Final">
<artifact name="netty-codec-haproxy-4.1.118.Final.jar">
<sha256 value="442adc9857f129389cb7fc43c8d602dfb60305c75f14338d60e9f24646b307fe" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-codec-haproxy-4.1.118.Final.pom">
<sha256 value="53f8a7ffd904cc9b55100535c3ecf729b59f42725b70d2644ae66ced218cfa08" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-codec-http" version="4.1.111.Final">
<artifact name="netty-codec-http-4.1.111.Final.pom">
<sha256 value="80372e3e6e61ba8b3abe623964c986376568b6bbec705adb29d2a959cd7b9f8c" origin="Generated by Gradle"/>
@@ -1963,6 +2008,14 @@
<sha256 value="fa7064f72ee52e136c9b6bfb5a1a0d510dcb73092d71941a39514c322a59de40" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-codec-http" version="4.1.118.Final">
<artifact name="netty-codec-http-4.1.118.Final.jar">
<sha256 value="09822d785e9a794838031ddd5346cf419b30c036a981c2e277a062bea884174b" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-codec-http-4.1.118.Final.pom">
<sha256 value="efa0a65fa1bccbd98dd14def99fda66dae999f6efc4b6418803bb50c6713c716" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-codec-http2" version="4.1.111.Final">
<artifact name="netty-codec-http2-4.1.111.Final.pom">
<sha256 value="c7f0325aa4c75c9eae1e388306dc7d10426c6a2a6c8f712f876f58a4bc157ede" origin="Generated by Gradle"/>
@@ -1976,6 +2029,14 @@
<sha256 value="88747b3e38e21cc1f42eaa44d301423482c0fc26c467b3f78fb9edfbde93a3e1" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-codec-http2" version="4.1.118.Final">
<artifact name="netty-codec-http2-4.1.118.Final.jar">
<sha256 value="68da0b1a34dceb00a6f9f6f788fb2f6b7b9e4adba8c70658ac2bd7eb898b97ae" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-codec-http2-4.1.118.Final.pom">
<sha256 value="4224460f69f6f67982c624a0d503c786a2450111b31ab15c075e0b28c94e11fe" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-codec-memcache" version="4.1.115.Final">
<artifact name="netty-codec-memcache-4.1.115.Final.jar">
<sha256 value="c44561d5cdb0e28e63b00dccb51c8f293c8b23012e0299042804e054e2b05fab" origin="Generated by Gradle"/>
@@ -1984,6 +2045,14 @@
<sha256 value="cfcc9845b932975dfc13be8c19260fda48d0646003de0e017964bdcde24d37c6" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-codec-memcache" version="4.1.118.Final">
<artifact name="netty-codec-memcache-4.1.118.Final.jar">
<sha256 value="e808b9fb1e0c165517782fa7efa97b25b69eb29029f8c39cd934c4a740aaf4c6" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-codec-memcache-4.1.118.Final.pom">
<sha256 value="e5842c75785fe96c7290783ea72593c3d0775362adb0dbefe6acc06e4fbaac52" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-codec-mqtt" version="4.1.115.Final">
<artifact name="netty-codec-mqtt-4.1.115.Final.jar">
<sha256 value="54202f894eea7f235b8f9032b32a09396bbf92a68cb32a486981ced96eb85222" origin="Generated by Gradle"/>
@@ -1992,6 +2061,14 @@
<sha256 value="dc966366eac6d123a56357a34495400435ed2c48f9bab5b363d648ddb525abba" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-codec-mqtt" version="4.1.118.Final">
<artifact name="netty-codec-mqtt-4.1.118.Final.jar">
<sha256 value="11055eaff1fca2a4df3f97b8a5a86732366ca99ff823ceef1c1a4bc4ddcb3b20" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-codec-mqtt-4.1.118.Final.pom">
<sha256 value="6daa144a9c589a058249c7f2f035b3f72710e6cb2357ef49ed5bcf86789be71e" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-codec-redis" version="4.1.115.Final">
<artifact name="netty-codec-redis-4.1.115.Final.jar">
<sha256 value="eddc930ecb65055a202b4d959f7b6636085a468d45daf283725037d466fa4de1" origin="Generated by Gradle"/>
@@ -2000,6 +2077,14 @@
<sha256 value="3afc90b1246694810d9da24e60a057cc2db5e44f20d820dfee67357d621a2975" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-codec-redis" version="4.1.118.Final">
<artifact name="netty-codec-redis-4.1.118.Final.jar">
<sha256 value="1a516f6c038976cfd8132d10ff01075d184eb94b168f95db36b7aef283fc2337" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-codec-redis-4.1.118.Final.pom">
<sha256 value="991cd92af74549a4292c34583826717a41c30b10575e90d8cb0c52ca680f77aa" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-codec-smtp" version="4.1.115.Final">
<artifact name="netty-codec-smtp-4.1.115.Final.jar">
<sha256 value="caa622c91fae41a0396b576c3803955371ae9ccde3ba6bf46117f4b641d0f737" origin="Generated by Gradle"/>
@@ -2008,6 +2093,14 @@
<sha256 value="f249c4744b45bea1147755c57a98d093b65925d684a249e8ec06d7bb360f687d" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-codec-smtp" version="4.1.118.Final">
<artifact name="netty-codec-smtp-4.1.118.Final.jar">
<sha256 value="ea97d0622440bcdf20278252d8c276430e17749204e169cd3ee60199e6d3ba3c" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-codec-smtp-4.1.118.Final.pom">
<sha256 value="6d015f95098fc3478b02350257f4944b880d066cc248be817e630f8bf28ef3ee" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-codec-socks" version="4.1.115.Final">
<artifact name="netty-codec-socks-4.1.115.Final.jar">
<sha256 value="e9b1cc744dc6195894450b1fd4d271a821ab167fe21ae3c459b27cdadc70e81f" origin="Generated by Gradle"/>
@@ -2016,6 +2109,14 @@
<sha256 value="08b3c1acc77abdbadeef08c8cdf080e1bcceffe5f84751f60d89fc0bcbaaa2fc" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-codec-socks" version="4.1.118.Final">
<artifact name="netty-codec-socks-4.1.118.Final.jar">
<sha256 value="094465e3cfb3aef0fca38ed82b801f53a6c8be7ae1f83ab0c1b2e8ece2586840" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-codec-socks-4.1.118.Final.pom">
<sha256 value="9382fa2a4497e4de147b3aa8c193f5b7cabef56671cce8e12bc593cc7a84d293" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-codec-stomp" version="4.1.115.Final">
<artifact name="netty-codec-stomp-4.1.115.Final.jar">
<sha256 value="9470312b5e263595a4d0a6a3cb3e64e5d11e87285fbfc267b845118d1c26fc7a" origin="Generated by Gradle"/>
@@ -2024,6 +2125,14 @@
<sha256 value="394fd6bdf7145aa180548d46f3297ed6a9e6a4a073fe0f743ca4df8217b1990a" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-codec-stomp" version="4.1.118.Final">
<artifact name="netty-codec-stomp-4.1.118.Final.jar">
<sha256 value="a9a6c2ed43cc1f0769f9e130f9df5c76be9fd9294e4611227341d8b28689ec57" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-codec-stomp-4.1.118.Final.pom">
<sha256 value="32ca71a69d0ffe32f26f4c2f5a29a1d54b4821548a8135e84588f7ce7051b75e" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-codec-xml" version="4.1.115.Final">
<artifact name="netty-codec-xml-4.1.115.Final.jar">
<sha256 value="e1c5f043f027ca5f853076df16c233665011be60a81f5a6f7336093790a9dced" origin="Generated by Gradle"/>
@@ -2032,6 +2141,14 @@
<sha256 value="aa98be6f2de38ece5a34492c1ae1c22d6783aa88cf74f1b1eb3d46add5bf514b" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-codec-xml" version="4.1.118.Final">
<artifact name="netty-codec-xml-4.1.118.Final.jar">
<sha256 value="47ac0297e67213a4dd24ef4e81f170a87787df92ac6f2a2a9da051f272a5a482" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-codec-xml-4.1.118.Final.pom">
<sha256 value="2461f368ad805297376f6ffbaa06a0335f6babb387494f684664d82e6b029c45" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-common" version="4.1.111.Final">
<artifact name="netty-common-4.1.111.Final.pom">
<sha256 value="b9c78337616bf8d54d5515bab6cba1df2db46fdb01b412434cc7fbafe78345c5" origin="Generated by Gradle"/>
@@ -2045,6 +2162,14 @@
<sha256 value="6286361f5dd6a7b26c42909242bb8629e3aeec255ad0d3ff131bcb5152c31b21" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-common" version="4.1.118.Final">
<artifact name="netty-common-4.1.118.Final.jar">
<sha256 value="65cce901ecf0f9d6591cc7750772614ab401a84415dc9aec9da4d046f0f9a77c" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-common-4.1.118.Final.pom">
<sha256 value="34290415e3f7961b196f7637c82a617d1038112291b31cbbc47200d1da326e82" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-handler" version="4.1.111.Final">
<artifact name="netty-handler-4.1.111.Final.pom">
<sha256 value="f575d446fa5cd546be4fce0bbf34c144041761b89da77971e30bf83d42905d1b" origin="Generated by Gradle"/>
@@ -2058,6 +2183,14 @@
<sha256 value="8c28025a352fc03846ce04960ab9843418bc6c82cbe5459e8060a4f3459efe90" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-handler" version="4.1.118.Final">
<artifact name="netty-handler-4.1.118.Final.jar">
<sha256 value="26e3f8a5e859fd62cf3c13dc6d75e4e18879f000a5d0ad7f58f8679675d23dae" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-handler-4.1.118.Final.pom">
<sha256 value="64b4b591ac4df4e9683a3fa17f73defb761af072c56dc2de4033e180c56b1049" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-handler-proxy" version="4.1.111.Final">
<artifact name="netty-handler-proxy-4.1.111.Final.pom">
<sha256 value="8577aa45f16f435b12420a37b8c8d731df11d4dcb7e132c00fc588946274ed89" origin="Generated by Gradle"/>
@@ -2071,6 +2204,14 @@
<sha256 value="f9936b2da7adcecef5c4ffad772067b7de5d0e359b83e6fd39b4a49d11706a10" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-handler-proxy" version="4.1.118.Final">
<artifact name="netty-handler-proxy-4.1.118.Final.jar">
<sha256 value="fef926126f44c668968dd3e2389c2552981d452e6dfc23b1f9bd03db92c21f96" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-handler-proxy-4.1.118.Final.pom">
<sha256 value="65c5c63a7bd6449a6d71e03c609d81a7a671e339abee54418477a3b388a19ef5" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-handler-ssl-ocsp" version="4.1.115.Final">
<artifact name="netty-handler-ssl-ocsp-4.1.115.Final.jar">
<sha256 value="d958f728bffe9a2f23f32a70917a2842feb4fac59baec581f89411c734b59cfe" origin="Generated by Gradle"/>
@@ -2079,6 +2220,14 @@
<sha256 value="a4e8fdffeb1006d7ebb0c4b5cf70b4e7a81a533c783721b0d18d3c4403ffe883" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-handler-ssl-ocsp" version="4.1.118.Final">
<artifact name="netty-handler-ssl-ocsp-4.1.118.Final.jar">
<sha256 value="c311f4f3389432544bf103162b40736d9b67001dbae1cb92d67adcb56f50f1c6" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-handler-ssl-ocsp-4.1.118.Final.pom">
<sha256 value="d6a1ea624d03d5817a38adc238cd2a6cc13ffae3a40b3d1f0a91d0ce6bc2a7a7" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-parent" version="4.1.111.Final">
<artifact name="netty-parent-4.1.111.Final.pom">
<sha256 value="96b055ae7af7f1e32db3d7261918d900fba15e1e589ccd682f9cfd5744249a51" origin="Generated by Gradle"/>
@@ -2089,6 +2238,11 @@
<sha256 value="d832942b8e2a71c2ccfdd0247a8b840105ce40aaeec4b8de36358f28d93941e3" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-parent" version="4.1.118.Final">
<artifact name="netty-parent-4.1.118.Final.pom">
<sha256 value="d966daebad7c721dfbf8fecd94f07b126f6fcb2d1f85b9c87999fd999ebc68c8" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-resolver" version="4.1.111.Final">
<artifact name="netty-resolver-4.1.111.Final.pom">
<sha256 value="d7524053459d425c9cbbbf93f0793504c4d0148c75fb49daab3e14d4e4d38ef0" origin="Generated by Gradle"/>
@@ -2102,6 +2256,14 @@
<sha256 value="53a491cfc876083ebd67e9c315c18c2348bb28f5366ca5d178bd8e8611f0ab4e" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-resolver" version="4.1.118.Final">
<artifact name="netty-resolver-4.1.118.Final.jar">
<sha256 value="3170c225972c18b6850d28add60db15bb28d83c4e3d5b686ca220e0bd7273c8a" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-resolver-4.1.118.Final.pom">
<sha256 value="872dcbd88229c2e9180a3caa75584c42ced88a516c49a68dd896372164b2a94f" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-resolver-dns" version="4.1.111.Final">
<artifact name="netty-resolver-dns-4.1.111.Final.pom">
<sha256 value="9d4607157d264015d8e8ae825d49fb8d8bf48fe227de99d82fb5570ac519c70a" origin="Generated by Gradle"/>
@@ -2115,6 +2277,14 @@
<sha256 value="8d1e3143825e0244e1dd614b2340deb00f4ee07ef615faa855bd195100776789" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-resolver-dns" version="4.1.118.Final">
<artifact name="netty-resolver-dns-4.1.118.Final.jar">
<sha256 value="c0e0fdaffaba849e3145b2b96288fc8fc6f3b2a623cf72aaba708288348e4938" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-resolver-dns-4.1.118.Final.pom">
<sha256 value="d6a6c36e94772f70b86aab4cada5f8c56aedf924d0006735da42b35d5ef9c6f7" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-resolver-dns-classes-macos" version="4.1.115.Final">
<artifact name="netty-resolver-dns-classes-macos-4.1.115.Final.jar">
<sha256 value="5fbac6346b3fc2fbdce7f7f24bb96a1b3190aa14d50021303a0a4437f3d373bc" origin="Generated by Gradle"/>
@@ -2123,6 +2293,14 @@
<sha256 value="221aabb31073eea980d9de5a00a5b74ddaa99cccae638ad485dce8897d880c72" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-resolver-dns-classes-macos" version="4.1.118.Final">
<artifact name="netty-resolver-dns-classes-macos-4.1.118.Final.jar">
<sha256 value="fb51c17c4eaba431da57dcf185d9a02c767dbc2925f281e19a4f14ba1b0ae5ed" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-resolver-dns-classes-macos-4.1.118.Final.pom">
<sha256 value="f88c40964c5732808d3ceff427cb9ced7a2f2d77f260c353df3d97f2f0d70371" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-resolver-dns-native-macos" version="4.1.115.Final">
<artifact name="netty-resolver-dns-native-macos-4.1.115.Final-osx-aarch_64.jar">
<sha256 value="a386a63e9c3d5a66130d1eebe9818820ca04d79769ab18b65d934e6e3a74e4e5" origin="Generated by Gradle"/>
@@ -2134,6 +2312,17 @@
<sha256 value="f037922e4384b71959204e880e0564e448afd8d2066fc46f9caee528cda0ba44" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-resolver-dns-native-macos" version="4.1.118.Final">
<artifact name="netty-resolver-dns-native-macos-4.1.118.Final-osx-aarch_64.jar">
<sha256 value="e1e95fe184946a0e14171c494e54456b9160f9f9f2a07e5db30426ed9f9b76d8" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-resolver-dns-native-macos-4.1.118.Final-osx-x86_64.jar">
<sha256 value="0bb6cd68b24b844eae224a1846909adbdffa7f78407bb588bf77f01f334b5bb5" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-resolver-dns-native-macos-4.1.118.Final.pom">
<sha256 value="62bd872c2982af7de2438ebdbd7eff1e5ee6aa475e4c683d52990eebfc05bad4" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-tcnative-boringssl-static" version="2.0.65.Final">
<artifact name="netty-tcnative-boringssl-static-2.0.65.Final.pom">
<sha256 value="ba89f115a8c30bdbcb6dba36849a047da803b496f3e856e4365d53df1403932d" origin="Generated by Gradle"/>
@@ -2147,6 +2336,14 @@
<sha256 value="d55ff689622b75dfeca232da2a98cefa1773b7717a318be18113ebf718d232f8" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-tcnative-boringssl-static" version="2.0.70.Final">
<artifact name="netty-tcnative-boringssl-static-2.0.70.Final.jar">
<sha256 value="3f7b4c3a51737965cd5b53777782c125784420458d96513cfac7412e4d1fa0c3" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-tcnative-boringssl-static-2.0.70.Final.pom">
<sha256 value="df443ea183f355f099a796a7ee067a566c7e3e94e71dd9c9691f9bbafc2b3535" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-tcnative-classes" version="2.0.69.Final">
<artifact name="netty-tcnative-classes-2.0.69.Final.jar">
<sha256 value="0bbc2848cb099eb3f6f4ec36501b3600e1828457cda41d5330687f601ee04bef" origin="Generated by Gradle"/>
@@ -2155,11 +2352,24 @@
<sha256 value="59da0f949e2bee89f5711eb3e0c6fd8cf7914e6b70c007f36b9eb0688243015c" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-tcnative-classes" version="2.0.70.Final">
<artifact name="netty-tcnative-classes-2.0.70.Final.jar">
<sha256 value="a79c1579313d4ad48a3ecc1d01a25da06d22d6449c3bcc369c2318749bcf55bc" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-tcnative-classes-2.0.70.Final.pom">
<sha256 value="411b46884bf6d25eba1f64535391ec5b1087e918e0e53d224e4220a04fb32675" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-tcnative-parent" version="2.0.69.Final">
<artifact name="netty-tcnative-parent-2.0.69.Final.pom">
<sha256 value="2953027196637610c894f32bda705093cd8768315c9f1a573951a75f17869afc" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-tcnative-parent" version="2.0.70.Final">
<artifact name="netty-tcnative-parent-2.0.70.Final.pom">
<sha256 value="8075a5e549677011a07175d9b8da963a58b884b32e8e101b75e78152173fdc35" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-transport" version="4.1.111.Final">
<artifact name="netty-transport-4.1.111.Final.pom">
<sha256 value="8662bd5d7f62d372548f40ad896b87ea55c2b1d179029ef985837dce43a52269" origin="Generated by Gradle"/>
@@ -2173,6 +2383,14 @@
<sha256 value="9677471c5409adced1f6cdb65a3ad8015e2e970990aba497a4eeca18abc363f9" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-transport" version="4.1.118.Final">
<artifact name="netty-transport-4.1.118.Final.jar">
<sha256 value="ab3751e717daef9c8d91e4d74728a48730bd8530b72e2466b222b2ea3fb07db9" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-transport-4.1.118.Final.pom">
<sha256 value="621f4c5672204a7b4372397b0bf8d7e446cd9f660affc0d0339c059c500a7032" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-transport-classes-epoll" version="4.1.115.Final">
<artifact name="netty-transport-classes-epoll-4.1.115.Final.jar">
<sha256 value="40aa67b4463cca0ab346e393c87f6c37e8954d18ec8b78567d95b55aa1f2b3aa" origin="Generated by Gradle"/>
@@ -2181,6 +2399,14 @@
<sha256 value="e57cc702f24b733565741ce3a69d89a2b5f5dcbda34c52b779fd02ba68c40987" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-transport-classes-epoll" version="4.1.118.Final">
<artifact name="netty-transport-classes-epoll-4.1.118.Final.jar">
<sha256 value="bd86e6d41e1f6053f9577931655236259778ab045646e1e6ab04150f070864f3" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-transport-classes-epoll-4.1.118.Final.pom">
<sha256 value="f962b4579a4be0cd4bba23ea89ae3b84e0c9b8d16486e4c1d311346eb6fc731f" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-transport-classes-kqueue" version="4.1.115.Final">
<artifact name="netty-transport-classes-kqueue-4.1.115.Final.jar">
<sha256 value="77d3a0930f86870b48e668635e214e0c81205f90f2012dc70c8417b59a839d04" origin="Generated by Gradle"/>
@@ -2189,6 +2415,14 @@
<sha256 value="6e3d4260612e3e5af0fcd9042d20a9249f7f643c282906c34b2a568f7dea5e69" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-transport-classes-kqueue" version="4.1.118.Final">
<artifact name="netty-transport-classes-kqueue-4.1.118.Final.jar">
<sha256 value="9ac68d26efb78d74a24eeec8309d60bc80265655b60c44920dcac0828db4e8e2" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-transport-classes-kqueue-4.1.118.Final.pom">
<sha256 value="28c71e4396343f25438d619d2953f066d0cf0f5971aea957fab1e318a0135290" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-transport-native-epoll" version="4.1.111.Final">
<artifact name="netty-transport-native-epoll-4.1.111.Final.pom">
<sha256 value="c14b3e677409871ae1cb80436dd06945591ae201ce3cc96db6a52957c7b9e574" origin="Generated by Gradle"/>
@@ -2211,6 +2445,23 @@
<sha256 value="0f2e546db040b7cbebd911dbdef0f897d7bf36b9cae9a53e047a49cb2c497b0c" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-transport-native-epoll" version="4.1.118.Final">
<artifact name="netty-transport-native-epoll-4.1.118.Final-linux-aarch_64.jar">
<sha256 value="53b3bec1d019bd3db6eeed30c6a770b49052ddd8fa1566796ae759e62b572425" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-transport-native-epoll-4.1.118.Final-linux-riscv64.jar">
<sha256 value="e7b7a5403a580be2f38c99c2559e551ebb563fdddd89d7f6e142fb1b475ba95d" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-transport-native-epoll-4.1.118.Final-linux-x86_64.jar">
<sha256 value="c95f66b9ab3f7fb6e666a81a5d0120e0c6acddf8fdf440e2ba212bfaf76a7c73" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-transport-native-epoll-4.1.118.Final.jar">
<sha256 value="892c85d067ecf495808b7090d8b62afa0248404b4e07cde88287ea116dfa6fba" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-transport-native-epoll-4.1.118.Final.pom">
<sha256 value="cab7bfb6c06d1f88199dc10f8681d92def080d116b7669fc596a750a37f40e50" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-transport-native-kqueue" version="4.1.111.Final">
<artifact name="netty-transport-native-kqueue-4.1.111.Final.pom">
<sha256 value="d1bf448ced9703e41e63f539f8523246f2a163434ddf4330d213262e731d5707" origin="Generated by Gradle"/>
@@ -2230,6 +2481,20 @@
<sha256 value="765e74114e7381fdcdd1b189d695b624113590f471980fd96745f1f2950014c5" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-transport-native-kqueue" version="4.1.118.Final">
<artifact name="netty-transport-native-kqueue-4.1.118.Final-osx-aarch_64.jar">
<sha256 value="599f32b702fb515f6fe15064250f525ad7eee13676dd3bcfc4c42704a17c7c7e" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-transport-native-kqueue-4.1.118.Final-osx-x86_64.jar">
<sha256 value="dc0a100920afadd39c252ba682ea62bfdb139945eaa9efdb6472f20b6bae4ef2" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-transport-native-kqueue-4.1.118.Final.jar">
<sha256 value="d7684931403b08e20a7926c171e294deccadcebdc500a90e68d04f978cddae41" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-transport-native-kqueue-4.1.118.Final.pom">
<sha256 value="9d9bb0f52879c53907b0a60898f6e71e7997169cbfa84307154b503c12209146" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-transport-native-unix-common" version="4.1.111.Final">
<artifact name="netty-transport-native-unix-common-4.1.111.Final.pom">
<sha256 value="2ea70a1864ab38fcec2bdc28c69ee9756dbad58a4626c664554d3c860d540981" origin="Generated by Gradle"/>
@@ -2243,6 +2508,14 @@
<sha256 value="96075271c578faadffec6f133991ea30884a69a41e07245a5ff8d5e7e5dd9f07" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-transport-native-unix-common" version="4.1.118.Final">
<artifact name="netty-transport-native-unix-common-4.1.118.Final.jar">
<sha256 value="69b16793d7b41ea76a762bd2bd144fc4f7c39c156a7a59ebf69baeb560fb10b7" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-transport-native-unix-common-4.1.118.Final.pom">
<sha256 value="c37dfa448fda576e35d2a788a346a9a4518198acc53759922584abb86d21dfdb" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-transport-rxtx" version="4.1.115.Final">
<artifact name="netty-transport-rxtx-4.1.115.Final.jar">
<sha256 value="33e80b3c468be74f448416d59edbf4618ea4917d697a3cbc4a53e13516bfd5ed" origin="Generated by Gradle"/>
@@ -2251,6 +2524,14 @@
<sha256 value="38713e766cdf792a6d309adaba05193419a1ee8f1aea63a68cdabfe736bf5acd" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-transport-rxtx" version="4.1.118.Final">
<artifact name="netty-transport-rxtx-4.1.118.Final.jar">
<sha256 value="e5cdde9a3b70ea9cca796872c86598bff60883332151794b3900d533e1316739" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-transport-rxtx-4.1.118.Final.pom">
<sha256 value="054eac7bbcb2cf239b39662b0dbd62766882d81edcee946c802b57db37ab0b4b" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-transport-sctp" version="4.1.115.Final">
<artifact name="netty-transport-sctp-4.1.115.Final.jar">
<sha256 value="016e8596b4b2357c2671a8691cafaaab7650689d01715cda2a0b85631f22f3c6" origin="Generated by Gradle"/>
@@ -2259,6 +2540,14 @@
<sha256 value="844fdde08ab3dd22f5cac9e6f211f29a75caa8d80d986e5f5d459f95eab06d26" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-transport-sctp" version="4.1.118.Final">
<artifact name="netty-transport-sctp-4.1.118.Final.jar">
<sha256 value="1f9f992bb1681637932dbd0449dc931088ea8bb25f968c07524ef0f193ea6c52" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-transport-sctp-4.1.118.Final.pom">
<sha256 value="831a411a7aca8d3b75b1dcca93196fe718cdd12ea79b4c84d139b2739624ebb9" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-transport-udt" version="4.1.115.Final">
<artifact name="netty-transport-udt-4.1.115.Final.jar">
<sha256 value="c505809dbb2d1b159eb1732dfa10c68f05da41ea9a479416690ff9a76908e4fb" origin="Generated by Gradle"/>
@@ -2267,6 +2556,14 @@
<sha256 value="71338f622075315979a0709b9b5952aa8fdb019b75500b5437abdefbd9e0e6f5" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.netty" name="netty-transport-udt" version="4.1.118.Final">
<artifact name="netty-transport-udt-4.1.118.Final.jar">
<sha256 value="f8ca98ebc019f4fed190c959abfd8d32996250c1a4f97f4fd88c99ab97759d1b" origin="Generated by Gradle"/>
</artifact>
<artifact name="netty-transport-udt-4.1.118.Final.pom">
<sha256 value="c6a6f04a27746cb1c6b5b6de29529b569418c928e9e2ba1b24a5314a6ab8d10c" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.opencensus" name="opencensus-api" version="0.31.1">
<artifact name="opencensus-api-0.31.1.jar">
<sha256 value="f1474d47f4b6b001558ad27b952e35eda5cc7146788877fc52938c6eba24b382" origin="Generated by Gradle"/>
@@ -6721,15 +7018,15 @@
<sha256 value="f85d31049d34a23b7ae5418dce2d5a18a132aa7739c762b11551325f3706c089" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="tech.pegasys.discovery" name="discovery" version="24.9.1">
<artifact name="discovery-24.9.1.jar">
<sha256 value="3ec1210c89a08d990767b0c2087afe3b0554c52171459926166f88118afe6b45" origin="Generated by Gradle"/>
<component group="tech.pegasys.discovery" name="discovery" version="25.2.0">
<artifact name="discovery-25.2.0.jar">
<sha256 value="262b6ee8cbe3a7e2146d27fc183610698c61c0317c93134120b9be655121a189" origin="Generated by Gradle"/>
</artifact>
<artifact name="discovery-24.9.1.module">
<sha256 value="b5be44855f5e2eead37b8120447cf3431e976aa79df1182e12380a51a7c77cf5" origin="Generated by Gradle"/>
<artifact name="discovery-25.2.0.module">
<sha256 value="0bd6dfcfea3ac83b81bcf813cecf826fa703f92e7cbeb9f31be4434296018993" origin="Generated by Gradle"/>
</artifact>
<artifact name="discovery-24.9.1.pom">
<sha256 value="54fc2d14a65ab0e25ec43df22f119e2840e628fdf8726f85d05929feccc68bd1" origin="Generated by Gradle"/>
<artifact name="discovery-25.2.0.pom">
<sha256 value="68c3d3764c71d443241eecc990a3dfc470b592604e9e2d53c3473631ab9b141d" origin="Generated by Gradle"/>
</artifact>
</component>
</components>

View File

@@ -28,7 +28,7 @@ javaPlatform {
dependencies {
api platform('com.fasterxml.jackson:jackson-bom:2.18.0')
api platform('io.grpc:grpc-bom:1.68.0')
api platform('io.netty:netty-bom:4.1.115.Final')
api platform('io.netty:netty-bom:4.1.118.Final')
api platform('io.opentelemetry:opentelemetry-bom:1.43.0')
api platform('io.prometheus:prometheus-metrics-bom:1.3.4')
api platform('io.vertx:vertx-stack-depchain:4.5.10')
@@ -96,6 +96,8 @@ dependencies {
api 'info.picocli:picocli-codegen:4.7.6'
api 'io.kubernetes:client-java:21.0.1-legacy'
// Temporarily locking in to avoid https://github.com/netplex/json-smart-v2/issues/240
api 'net.minidev:json-smart:2.4.2'
api 'io.opentelemetry.instrumentation:opentelemetry-okhttp-3.0:2.9.0-alpha'
api 'io.opentelemetry.proto:opentelemetry-proto:1.3.2-alpha'
@@ -178,7 +180,7 @@ dependencies {
api 'tech.pegasys:jc-kzg-4844:1.0.0'
api 'tech.pegasys.discovery:discovery:24.9.1'
api 'tech.pegasys.discovery:discovery:25.2.0'
}
}

View File

@@ -71,7 +71,7 @@ Calculated : ${currentHash}
tasks.register('checkAPIChanges', FileStateChecker) {
description = "Checks that the API for the Plugin-API project does not change without deliberate thought"
files = sourceSets.main.allJava.files
knownHash = 'I2IrN2aLU610031Vw8xNr3hcT8/wb25pDrclwZUggE4='
knownHash = 'D2ZMRGb2HS/9FgDE2mcizdxTQhFsGD4LS9lgngG/TnU='
}
check.dependsOn('checkAPIChanges')

View File

@@ -15,8 +15,11 @@
package org.hyperledger.besu.plugin.services;
import org.hyperledger.besu.plugin.Unstable;
import org.hyperledger.besu.plugin.data.ProcessableBlockHeader;
import org.hyperledger.besu.plugin.services.txselection.BlockTransactionSelectionService;
import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelector;
import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelectorFactory;
import org.hyperledger.besu.plugin.services.txselection.SelectorsStateManager;
/** Transaction selection service interface */
@Unstable
@@ -25,9 +28,23 @@ public interface TransactionSelectionService extends BesuService {
/**
* Create a transaction selector plugin
*
* @param selectorsStateManager the selectors state manager
* @return the transaction selector plugin
*/
PluginTransactionSelector createPluginTransactionSelector();
PluginTransactionSelector createPluginTransactionSelector(
SelectorsStateManager selectorsStateManager);
/**
* Called during the block creation to allow plugins to propose their own pending transactions for
* block inclusion
*
* @param blockTransactionSelectionService the service used by the plugin to evaluate pending
* transactions and commit or rollback changes
* @param pendingBlockHeader the header of the block being created
*/
void selectPendingTransactions(
BlockTransactionSelectionService blockTransactionSelectionService,
final ProcessableBlockHeader pendingBlockHeader);
/**
* Registers the transaction selector factory with the service

View File

@@ -0,0 +1,71 @@
/*
* 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.plugin.services.txselection;
/**
* This class represents an abstract plugin transaction selector which provides methods to manage
* the selector state.
*
* @param <S> The type of the state used by the selector
*/
public abstract class AbstractStatefulPluginTransactionSelector<S>
implements PluginTransactionSelector {
private final SelectorsStateManager selectorsStateManager;
/**
* Initialize the plugin state to an initial value
*
* @param selectorsStateManager the selectors state manager
* @param initialState the initial value of the state
* @param stateDuplicator the function that duplicates the state
*/
public AbstractStatefulPluginTransactionSelector(
final SelectorsStateManager selectorsStateManager,
final S initialState,
final SelectorsStateManager.StateDuplicator<S> stateDuplicator) {
this.selectorsStateManager = selectorsStateManager;
selectorsStateManager.createSelectorState(this, initialState, stateDuplicator);
}
/**
* Get the working state for this selector. A working state contains changes that have not yet
* committed
*
* @return the working state of this selector
*/
protected S getWorkingState() {
return selectorsStateManager.getSelectorWorkingState(this);
}
/**
* Set the working state for this selector. A working state contains changes that have not yet
* commited
*
* @param newState the new working state of this selector
*/
protected void setWorkingState(final S newState) {
selectorsStateManager.setSelectorWorkingState(this, newState);
}
/**
* Get the commited state for this selector. A commited state contains changes that have been
* commited
*
* @return the commited state of this selector
*/
protected S getCommitedState() {
return selectorsStateManager.getSelectorCommittedState(this);
}
}

View File

@@ -0,0 +1,57 @@
/*
* 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.plugin.services.txselection;
import org.hyperledger.besu.datatypes.PendingTransaction;
import org.hyperledger.besu.plugin.data.ProcessableBlockHeader;
import org.hyperledger.besu.plugin.data.TransactionSelectionResult;
/**
* A service that can be used by plugins to include their pending transactions during block
* creation. Proposed pending transactions need to be first evaluated and based on the result of the
* evaluation of one or more pending transactions the plugin can apply its logic to commit or not
* the inclusion of the evaluated in the block.
*
* <p>The process of including plugin proposed pending transactions starts when {@link
* PluginTransactionSelectorFactory#selectPendingTransactions(BlockTransactionSelectionService,
* ProcessableBlockHeader)} is called.
*/
public interface BlockTransactionSelectionService {
/**
* Evaluate a plugin proposed pending transaction for block inclusion
*
* @param pendingTransaction the pending transaction proposed by the plugin
* @return the result of the evaluation
*/
TransactionSelectionResult evaluatePendingTransaction(PendingTransaction pendingTransaction);
/**
* Commit the changes applied by all the evaluated pending transactions since the previous commit.
* As part of this {@link PluginTransactionSelector} state of commited.
*
* @return false only if a timeout occurred during the selection of the pending transaction,
* meaning that the pending transaction is not included in the current block
*/
boolean commit();
/**
* Rollback the changes applied by all the evaluated pending transactions since the previous
* commit.
*
* <p>As part of this {@link PluginTransactionSelector} state of rolled back.
*/
void rollback();
}

View File

@@ -23,7 +23,7 @@ import org.hyperledger.besu.plugin.services.tracer.BlockAwareOperationTracer;
/** Interface for the transaction selector */
@Unstable
public interface PluginTransactionSelector {
public interface PluginTransactionSelector extends TransactionSelector {
/** Plugin transaction selector that unconditionally select every transaction */
PluginTransactionSelector ACCEPT_ALL =
new PluginTransactionSelector() {

View File

@@ -15,15 +15,39 @@
package org.hyperledger.besu.plugin.services.txselection;
import org.hyperledger.besu.plugin.Unstable;
import org.hyperledger.besu.plugin.data.ProcessableBlockHeader;
/** Interface for a factory that creates transaction selectors */
/**
* Interface for a factory that creates transaction selector and propose pending transaction for
* block inclusion
*/
@Unstable
public interface PluginTransactionSelectorFactory {
/**
* A default factory that does not propose any pending transactions and does not apply any filter
*/
PluginTransactionSelectorFactory NO_OP_FACTORY = new PluginTransactionSelectorFactory() {};
/**
* Create a transaction selector
* Create a plugin transaction selector, that can be used during block creation to apply custom
* filters to proposed pending transactions
*
* @param selectorsStateManager the selectors state manager
* @return the transaction selector
*/
PluginTransactionSelector create();
default PluginTransactionSelector create(final SelectorsStateManager selectorsStateManager) {
return PluginTransactionSelector.ACCEPT_ALL;
}
/**
* Called during the block creation to allow plugins to propose their own pending transactions for
* block inclusion
*
* @param blockTransactionSelectionService the service used by the plugin to evaluate pending
* transactions and commit or rollback changes
* @param pendingBlockHeader the header of the block being created
*/
default void selectPendingTransactions(
final BlockTransactionSelectionService blockTransactionSelectionService,
final ProcessableBlockHeader pendingBlockHeader) {}
}

View File

@@ -0,0 +1,222 @@
/*
* 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.plugin.services.txselection;
import static com.google.common.base.Preconditions.checkState;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
/**
* Manages the state of transaction selectors (including the plugin transaction selector {@link
* PluginTransactionSelector}) during the block creation process. Some selectors have a state, for
* example the amount of gas used by selected pending transactions so far, and changes made to the
* state must be only commited after the evaluated pending transaction has been definitely selected
* for inclusion, until that point it will be always possible to rollback the changes to the state
* and return the previous commited state.
*/
@SuppressWarnings("rawtypes")
public class SelectorsStateManager {
private final List<Map<TransactionSelector, SelectorState<?, ? extends StateDuplicator<?>>>>
uncommitedStates = new ArrayList<>();
private Map<TransactionSelector, SelectorState<?, ? extends StateDuplicator<?>>> committedState =
new HashMap<>();
private volatile boolean blockSelectionStarted = false;
/** Create an empty selectors state manager, here to make javadoc linter happy. */
public SelectorsStateManager() {}
/**
* Create, initialize and track the state for a selector.
*
* <p>Call to this method must be performed before the block selection is stated with {@link
* #blockSelectionStarted()}, otherwise it fails.
*
* @param selector the selector
* @param initialState the initial value of the state
* @param duplicator the state duplicator
* @param <S> the type of the selector state
* @param <D> the type of the state duplicator
*/
public <S, D extends StateDuplicator<S>> void createSelectorState(
final TransactionSelector selector, final S initialState, final D duplicator) {
checkState(
!blockSelectionStarted, "Cannot create selector state after block selection is started");
committedState.put(selector, new SelectorState<>(initialState, duplicator));
}
/**
* Called at the start of block selection, when the initialization is done, to prepare a new
* working state based on the initial state.
*
* <p>After this method is called, it is not possible to call anymore {@link
* #createSelectorState(TransactionSelector, Object, StateDuplicator)}
*/
public void blockSelectionStarted() {
blockSelectionStarted = true;
uncommitedStates.add(duplicateLastState());
}
private Map<TransactionSelector, SelectorState<?, ? extends StateDuplicator<?>>>
duplicateLastState() {
return getLast().entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().duplicated()));
}
/**
* Get the working state for the specified selector
*
* @param selector the selector
* @return the working state of the selector
* @param <T> the type of the selector state
*/
@SuppressWarnings("unchecked")
public <T> T getSelectorWorkingState(final TransactionSelector selector) {
return (T) uncommitedStates.getLast().get(selector).get();
}
/**
* set the working state for the specified selector
*
* @param selector the selector
* @param newState the new state
* @param <T> the type of the selector state
*/
@SuppressWarnings("unchecked")
public <T> void setSelectorWorkingState(final TransactionSelector selector, final T newState) {
((SelectorState<T, StateDuplicator<T>>) uncommitedStates.getLast().get(selector)).set(newState);
}
/**
* Get the commited state for the specified selector
*
* @param selector the selector
* @return the commited state of the selector
* @param <T> the type of the selector state
*/
@SuppressWarnings("unchecked")
public <T> T getSelectorCommittedState(final TransactionSelector selector) {
return (T) committedState.get(selector).get();
}
/**
* Commit the current working state and prepare a new working state based on the just commited
* state
*/
public void commit() {
committedState = getLast();
uncommitedStates.clear();
uncommitedStates.add(duplicateLastState());
}
/**
* Discards the current working state and prepare a new working state based on the just commited
* state
*/
public void rollback() {
uncommitedStates.clear();
uncommitedStates.add(duplicateLastState());
}
private Map<TransactionSelector, SelectorState<?, ? extends StateDuplicator<?>>> getLast() {
if (uncommitedStates.isEmpty()) {
return committedState;
}
return uncommitedStates.getLast();
}
/**
* A function that create a duplicate of the input object. The duplication must be a deep copy.
*
* @param <T> the type of the object
*/
@FunctionalInterface
public interface StateDuplicator<T> extends UnaryOperator<T> {
/**
* Duplicate the input objet
*
* @param t the input object to duplicate
* @return a deep copy of the input object
*/
T duplicate(T t);
@Override
default T apply(final T t) {
return duplicate(t);
}
/**
* Utility to duplicate a long
*
* @param l a long
* @return a copy of the long
*/
static long duplicateLong(final long l) {
return l;
}
}
/**
* A selector state object is one that is able to return, update and duplicate the state it
* contains
*
* @param <S> the type of the state
*/
private static class SelectorState<S, D extends StateDuplicator<S>> {
private final D duplicator;
private S state;
/**
* Create a selector state with the initial value
*
* @param initialState the initial initialState
*/
public SelectorState(final S initialState, final D duplicator) {
this.state = initialState;
this.duplicator = duplicator;
}
/**
* The method that concrete classes must implement to create a deep copy of the state
*
* @return a new selector state with a deep copy of the state
*/
private SelectorState<S, D> duplicated() {
return new SelectorState<>(duplicator.duplicate(state), duplicator);
}
/**
* Get the current state
*
* @return the current state
*/
public S get() {
return state;
}
/**
* Replace the current state with the new one
*
* @param newState the new state
*/
public void set(final S newState) {
this.state = newState;
}
}
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright contributors to Hyperledger 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.plugin.services.txselection;
import org.hyperledger.besu.plugin.Unstable;
/** Interface for the transaction selector */
@Unstable
public interface TransactionSelector {}

View File

@@ -0,0 +1,120 @@
/*
* Copyright contributors to Hyperledger 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.util.log4j.plugin;
import java.util.Arrays;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.Logger;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
import org.apache.logging.log4j.core.filter.AbstractFilter;
import org.apache.logging.log4j.message.Message;
/** Matches a text in the stack trace */
@Plugin(
name = "StackTraceMatchFilter",
category = "Core",
elementType = "filter",
printObject = true)
public class StackTraceMatchFilter extends AbstractFilter {
private final String text;
private StackTraceMatchFilter(final String text, final Result onMatch, final Result onMismatch) {
super(onMatch, onMismatch);
this.text = text;
}
@Override
public Result filter(
final Logger logger,
final Level level,
final Marker marker,
final Object msg,
final Throwable t) {
return filter(t);
}
@Override
public Result filter(
final Logger logger,
final Level level,
final Marker marker,
final Message msg,
final Throwable t) {
return filter(t);
}
@Override
public Result filter(final LogEvent event) {
return filter(event.getThrown());
}
private Result filter(final Throwable t) {
if (t != null) {
return Arrays.stream(t.getStackTrace())
.map(StackTraceElement::getClassName)
.anyMatch(cn -> cn.contains(text))
? onMatch
: onMismatch;
}
return Result.NEUTRAL;
}
@Override
public String toString() {
return text;
}
/**
* Create a new builder
*
* @return a new builder
*/
@PluginBuilderFactory
public static StackTraceMatchFilter.Builder newBuilder() {
return new StackTraceMatchFilter.Builder();
}
/** Builder for StackTraceMatchFilter */
public static class Builder extends AbstractFilterBuilder<StackTraceMatchFilter.Builder>
implements org.apache.logging.log4j.core.util.Builder<StackTraceMatchFilter> {
@PluginBuilderAttribute private String text = "";
/** Default constructor */
public Builder() {
// here to make javadoc happy
}
/**
* Set the string to match in the stack trace
*
* @param text the match string
* @return this builder
*/
public StackTraceMatchFilter.Builder setMatchString(final String text) {
this.text = text;
return this;
}
@Override
public StackTraceMatchFilter build() {
return new StackTraceMatchFilter(this.text, this.getOnMatch(), this.getOnMismatch());
}
}
}