mirror of
https://github.com/vacp2p/status-linea-besu.git
synced 2026-01-09 13:58:02 -05:00
Merge branch 'main' into zkbesu
# Conflicts: # .github/workflows/pre-review.yml # .github/workflows/reference-tests.yml
This commit is contained in:
13
CHANGELOG.md
13
CHANGELOG.md
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(
|
||||
HttpServerOptions options =
|
||||
new HttpServerOptions()
|
||||
.setHost(config.getHost())
|
||||
.setPort(config.getPort())
|
||||
.setHandle100ContinueAutomatically(true)
|
||||
.setCompressionSupported(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
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
return handleTransactionNotSelected(
|
||||
evaluationContext, postProcessingSelectionResult, txWorldStateUpdater);
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
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();
|
||||
|
||||
// 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;
|
||||
final long blobGasUsed =
|
||||
blockSelectionContext.gasCalculator().blobGasCost(transaction.getBlobCount());
|
||||
|
||||
final boolean tooLate;
|
||||
|
||||
// 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);
|
||||
|
||||
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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,8 +649,7 @@ public abstract class AbstractBlockTransactionSelectorTest {
|
||||
final Transaction notSelectedInvalid = createTransaction(2, Wei.of(10), 21_000, SENDER2);
|
||||
ensureTransactionIsValid(notSelectedInvalid, 21_000, 0);
|
||||
|
||||
final PluginTransactionSelectorFactory transactionSelectorFactory =
|
||||
() ->
|
||||
final PluginTransactionSelector pluginTransactionSelector =
|
||||
new PluginTransactionSelector() {
|
||||
@Override
|
||||
public TransactionSelectionResult evaluateTransactionPreProcessing(
|
||||
@@ -670,11 +670,20 @@ public abstract class AbstractBlockTransactionSelectorTest {
|
||||
@Override
|
||||
public TransactionSelectionResult evaluateTransactionPostProcessing(
|
||||
final TransactionEvaluationContext evaluationContext,
|
||||
final org.hyperledger.besu.plugin.data.TransactionProcessingResult
|
||||
processingResult) {
|
||||
final org.hyperledger.besu.plugin.data.TransactionProcessingResult processingResult) {
|
||||
return SELECTED;
|
||||
}
|
||||
};
|
||||
|
||||
final PluginTransactionSelectorFactory transactionSelectorFactory =
|
||||
new PluginTransactionSelectorFactory() {
|
||||
@Override
|
||||
public PluginTransactionSelector create(
|
||||
final SelectorsStateManager selectorsStateManager) {
|
||||
return pluginTransactionSelector;
|
||||
}
|
||||
};
|
||||
|
||||
transactionSelectionService.registerPluginTransactionSelectorFactory(
|
||||
transactionSelectorFactory);
|
||||
|
||||
@@ -717,11 +726,7 @@ 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 PluginTransactionSelectorFactory transactionSelectorFactory =
|
||||
() ->
|
||||
final PluginTransactionSelector pluginTransactionSelector =
|
||||
new PluginTransactionSelector() {
|
||||
@Override
|
||||
public TransactionSelectionResult evaluateTransactionPreProcessing(
|
||||
@@ -732,8 +737,7 @@ public abstract class AbstractBlockTransactionSelectorTest {
|
||||
@Override
|
||||
public TransactionSelectionResult evaluateTransactionPostProcessing(
|
||||
final TransactionEvaluationContext evaluationContext,
|
||||
final org.hyperledger.besu.plugin.data.TransactionProcessingResult
|
||||
processingResult) {
|
||||
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;
|
||||
@@ -741,6 +745,16 @@ public abstract class AbstractBlockTransactionSelectorTest {
|
||||
return SELECTED;
|
||||
}
|
||||
};
|
||||
|
||||
final PluginTransactionSelectorFactory transactionSelectorFactory =
|
||||
new PluginTransactionSelectorFactory() {
|
||||
@Override
|
||||
public PluginTransactionSelector create(
|
||||
final SelectorsStateManager selectorsStateManager) {
|
||||
return pluginTransactionSelector;
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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()));
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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"}
|
||||
]
|
||||
}
|
||||
@@ -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"}
|
||||
]
|
||||
}
|
||||
@@ -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"}
|
||||
]
|
||||
}
|
||||
@@ -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"}
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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')
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
@@ -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) {}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 {}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user