mirror of
https://github.com/vacp2p/linea-besu.git
synced 2026-01-08 23:17:54 -05:00
Merge branch 'main' into zkbesu
This commit is contained in:
27
.github/workflows/stale-issues.yml
vendored
Normal file
27
.github/workflows/stale-issues.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: Close inactive issues
|
||||
on:
|
||||
schedule:
|
||||
- cron: "30 1 * * *"
|
||||
|
||||
jobs:
|
||||
close-issues:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/stale@v5
|
||||
with:
|
||||
days-before-issue-stale: 180
|
||||
days-before-issue-close: 14
|
||||
stale-issue-label: "stale"
|
||||
stale-issue-message: "This issue is stale because it has been open for 6 months with no activity."
|
||||
close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
|
||||
days-before-pr-stale: 30
|
||||
days-before-pr-close: 14
|
||||
stale-pr-message: "This pr is stale because it has been open for 30 days with no activity."
|
||||
close-pr-message: "This pr was closed because it has been inactive for 14 days since being marked as stale."
|
||||
remove-issue-stale-when-updated: true
|
||||
remove-pr-stale-when-updated: true
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
### Additions and Improvements
|
||||
- Fine tune already seen txs tracker when a tx is removed from the pool [#7755](https://github.com/hyperledger/besu/pull/7755)
|
||||
- Support for enabling and configuring TLS/mTLS in WebSocket service. [#7854](https://github.com/hyperledger/besu/pull/7854)
|
||||
- Create and publish Besu BOM (Bill of Materials) [#7615](https://github.com/hyperledger/besu/pull/7615)
|
||||
- Update Java dependencies [#7786](https://github.com/hyperledger/besu/pull/7786)
|
||||
- Add a method to get all the transaction in the pool, to the `TransactionPoolService`, to easily access the transaction pool content from plugins [#7813](https://github.com/hyperledger/besu/pull/7813)
|
||||
|
||||
@@ -51,7 +51,7 @@ public class TestMetricsPlugin implements BesuPlugin {
|
||||
.createGauge(
|
||||
TestMetricCategory.TEST_METRIC_CATEGORY,
|
||||
"test_metric",
|
||||
"Returns 1 on succes",
|
||||
"Returns 1 on success",
|
||||
() -> 1.0);
|
||||
}
|
||||
|
||||
|
||||
@@ -120,6 +120,71 @@ public class RpcWebsocketOptions {
|
||||
arity = "1")
|
||||
private final File rpcWsAuthenticationPublicKeyFile = null;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {"--rpc-ws-ssl-enabled"},
|
||||
description = "Enable SSL/TLS for the WebSocket RPC service")
|
||||
private final Boolean isRpcWsSslEnabled = false;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {"--rpc-ws-ssl-keystore-file"},
|
||||
paramLabel = DefaultCommandValues.MANDATORY_FILE_FORMAT_HELP,
|
||||
description = "Path to the keystore file for the WebSocket RPC service")
|
||||
private String rpcWsKeyStoreFile = null;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {"--rpc-ws-ssl-keystore-password"},
|
||||
paramLabel = "<PASSWORD>",
|
||||
description = "Password for the WebSocket RPC keystore file")
|
||||
private String rpcWsKeyStorePassword = null;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {"--rpc-ws-ssl-key-file"},
|
||||
paramLabel = DefaultCommandValues.MANDATORY_FILE_FORMAT_HELP,
|
||||
description = "Path to the PEM key file for the WebSocket RPC service")
|
||||
private String rpcWsKeyFile = null;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {"--rpc-ws-ssl-cert-file"},
|
||||
paramLabel = DefaultCommandValues.MANDATORY_FILE_FORMAT_HELP,
|
||||
description = "Path to the PEM cert file for the WebSocket RPC service")
|
||||
private String rpcWsCertFile = null;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {"--rpc-ws-ssl-keystore-type"},
|
||||
paramLabel = "<TYPE>",
|
||||
description = "Type of the WebSocket RPC keystore (JKS, PKCS12, PEM)")
|
||||
private String rpcWsKeyStoreType = null;
|
||||
|
||||
// For client authentication (mTLS)
|
||||
@CommandLine.Option(
|
||||
names = {"--rpc-ws-ssl-client-auth-enabled"},
|
||||
description = "Enable client authentication for the WebSocket RPC service")
|
||||
private final Boolean isRpcWsClientAuthEnabled = false;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {"--rpc-ws-ssl-truststore-file"},
|
||||
paramLabel = DefaultCommandValues.MANDATORY_FILE_FORMAT_HELP,
|
||||
description = "Path to the truststore file for the WebSocket RPC service")
|
||||
private String rpcWsTrustStoreFile = null;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {"--rpc-ws-ssl-truststore-password"},
|
||||
paramLabel = "<PASSWORD>",
|
||||
description = "Password for the WebSocket RPC truststore file")
|
||||
private String rpcWsTrustStorePassword = null;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {"--rpc-ws-ssl-trustcert-file"},
|
||||
paramLabel = DefaultCommandValues.MANDATORY_FILE_FORMAT_HELP,
|
||||
description = "Path to the PEM trustcert file for the WebSocket RPC service")
|
||||
private String rpcWsTrustCertFile = null;
|
||||
|
||||
@CommandLine.Option(
|
||||
names = {"--rpc-ws-ssl-truststore-type"},
|
||||
paramLabel = "<TYPE>",
|
||||
description = "Type of the truststore (JKS, PKCS12, PEM)")
|
||||
private String rpcWsTrustStoreType = null;
|
||||
|
||||
/** Default Constructor. */
|
||||
public RpcWebsocketOptions() {}
|
||||
|
||||
@@ -184,7 +249,61 @@ public class RpcWebsocketOptions {
|
||||
"--rpc-ws-authentication-enabled",
|
||||
"--rpc-ws-authentication-credentials-file",
|
||||
"--rpc-ws-authentication-public-key-file",
|
||||
"--rpc-ws-authentication-jwt-algorithm"));
|
||||
"--rpc-ws-authentication-jwt-algorithm",
|
||||
"--rpc-ws-ssl-enabled"));
|
||||
|
||||
CommandLineUtils.checkOptionDependencies(
|
||||
logger,
|
||||
commandLine,
|
||||
"--rpc-ws-ssl-enabled",
|
||||
!isRpcWsSslEnabled,
|
||||
List.of(
|
||||
"--rpc-ws-ssl-keystore-file",
|
||||
"--rpc-ws-ssl-keystore-type",
|
||||
"--rpc-ws-ssl-client-auth-enabled"));
|
||||
|
||||
CommandLineUtils.checkOptionDependencies(
|
||||
logger,
|
||||
commandLine,
|
||||
"--rpc-ws-ssl-client-auth-enabled",
|
||||
!isRpcWsClientAuthEnabled,
|
||||
List.of(
|
||||
"--rpc-ws-ssl-truststore-file",
|
||||
"--rpc-ws-ssl-truststore-type",
|
||||
"--rpc-ws-ssl-trustcert-file"));
|
||||
|
||||
if (isRpcWsSslEnabled) {
|
||||
if ("PEM".equalsIgnoreCase(rpcWsKeyStoreType)) {
|
||||
CommandLineUtils.checkOptionDependencies(
|
||||
logger,
|
||||
commandLine,
|
||||
"--rpc-ws-ssl-key-file",
|
||||
rpcWsKeyFile == null,
|
||||
List.of("--rpc-ws-ssl-cert-file"));
|
||||
CommandLineUtils.checkOptionDependencies(
|
||||
logger,
|
||||
commandLine,
|
||||
"--rpc-ws-ssl-cert-file",
|
||||
rpcWsCertFile == null,
|
||||
List.of("--rpc-ws-ssl-key-file"));
|
||||
} else {
|
||||
CommandLineUtils.checkOptionDependencies(
|
||||
logger,
|
||||
commandLine,
|
||||
"--rpc-ws-ssl-keystore-file",
|
||||
rpcWsKeyStoreFile == null,
|
||||
List.of("--rpc-ws-ssl-keystore-password"));
|
||||
}
|
||||
}
|
||||
|
||||
if (isRpcWsClientAuthEnabled && !"PEM".equalsIgnoreCase(rpcWsTrustStoreType)) {
|
||||
CommandLineUtils.checkOptionDependencies(
|
||||
logger,
|
||||
commandLine,
|
||||
"--rpc-ws-ssl-truststore-file",
|
||||
rpcWsTrustStoreFile == null,
|
||||
List.of("--rpc-ws-ssl-truststore-password"));
|
||||
}
|
||||
|
||||
if (isRpcWsAuthenticationEnabled) {
|
||||
CommandLineUtils.checkOptionDependencies(
|
||||
@@ -222,6 +341,18 @@ public class RpcWebsocketOptions {
|
||||
webSocketConfiguration.setAuthenticationPublicKeyFile(rpcWsAuthenticationPublicKeyFile);
|
||||
webSocketConfiguration.setAuthenticationAlgorithm(rpcWebsocketsAuthenticationAlgorithm);
|
||||
webSocketConfiguration.setTimeoutSec(wsTimoutSec);
|
||||
webSocketConfiguration.setSslEnabled(isRpcWsSslEnabled);
|
||||
webSocketConfiguration.setKeyStorePath(rpcWsKeyStoreFile);
|
||||
webSocketConfiguration.setKeyStorePassword(rpcWsKeyStorePassword);
|
||||
webSocketConfiguration.setKeyStoreType(rpcWsKeyStoreType);
|
||||
webSocketConfiguration.setClientAuthEnabled(isRpcWsClientAuthEnabled);
|
||||
webSocketConfiguration.setTrustStorePath(rpcWsTrustStoreFile);
|
||||
webSocketConfiguration.setTrustStorePassword(rpcWsTrustStorePassword);
|
||||
webSocketConfiguration.setTrustStoreType(rpcWsTrustStoreType);
|
||||
webSocketConfiguration.setKeyPath(rpcWsKeyFile);
|
||||
webSocketConfiguration.setCertPath(rpcWsCertFile);
|
||||
webSocketConfiguration.setTrustCertPath(rpcWsTrustCertFile);
|
||||
|
||||
return webSocketConfiguration;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.Difficulty;
|
||||
import org.hyperledger.besu.ethereum.core.MiningConfiguration;
|
||||
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
|
||||
import org.hyperledger.besu.ethereum.core.Synchronizer;
|
||||
import org.hyperledger.besu.ethereum.eth.EthProtocol;
|
||||
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
|
||||
import org.hyperledger.besu.ethereum.eth.SnapProtocol;
|
||||
@@ -93,6 +94,7 @@ import org.hyperledger.besu.ethereum.trie.forest.ForestWorldStateArchive;
|
||||
import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration;
|
||||
import org.hyperledger.besu.ethereum.worldstate.DiffBasedSubStorageConfiguration;
|
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
|
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive.WorldStateHealer;
|
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateKeyValueStorage;
|
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStatePreimageStorage;
|
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateStorageCoordinator;
|
||||
@@ -113,6 +115,7 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@@ -589,9 +592,14 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
|
||||
.map(BesuComponent::getCachedMerkleTrieLoader)
|
||||
.orElseGet(() -> new BonsaiCachedMerkleTrieLoader(metricsSystem));
|
||||
|
||||
final var worldStateHealerSupplier = new AtomicReference<WorldStateHealer>();
|
||||
|
||||
final WorldStateArchive worldStateArchive =
|
||||
createWorldStateArchive(
|
||||
worldStateStorageCoordinator, blockchain, bonsaiCachedMerkleTrieLoader);
|
||||
worldStateStorageCoordinator,
|
||||
blockchain,
|
||||
bonsaiCachedMerkleTrieLoader,
|
||||
worldStateHealerSupplier::get);
|
||||
|
||||
if (maybeStoredGenesisBlockHash.isEmpty()) {
|
||||
genesisState.writeStateTo(worldStateArchive.getMutable());
|
||||
@@ -713,6 +721,8 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
|
||||
ethProtocolManager,
|
||||
pivotBlockSelector);
|
||||
|
||||
worldStateHealerSupplier.set(synchronizer::healWorldState);
|
||||
|
||||
ethPeers.setTrailingPeerRequirementsSupplier(synchronizer::calculateTrailingPeerRequirements);
|
||||
|
||||
if (syncConfig.getSyncMode() == SyncMode.SNAP
|
||||
@@ -723,11 +733,9 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
|
||||
ethPeers.snapServerPeersNeeded(false);
|
||||
}
|
||||
|
||||
protocolContext.setSynchronizer(synchronizer);
|
||||
|
||||
final Optional<SnapProtocolManager> maybeSnapProtocolManager =
|
||||
createSnapProtocolManager(
|
||||
protocolContext, worldStateStorageCoordinator, ethPeers, snapMessages);
|
||||
protocolContext, worldStateStorageCoordinator, ethPeers, snapMessages, synchronizer);
|
||||
|
||||
final MiningCoordinator miningCoordinator =
|
||||
createMiningCoordinator(
|
||||
@@ -1088,20 +1096,23 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
|
||||
final ProtocolContext protocolContext,
|
||||
final WorldStateStorageCoordinator worldStateStorageCoordinator,
|
||||
final EthPeers ethPeers,
|
||||
final EthMessages snapMessages) {
|
||||
final EthMessages snapMessages,
|
||||
final Synchronizer synchronizer) {
|
||||
return Optional.of(
|
||||
new SnapProtocolManager(
|
||||
worldStateStorageCoordinator,
|
||||
syncConfig.getSnapSyncConfiguration(),
|
||||
ethPeers,
|
||||
snapMessages,
|
||||
protocolContext));
|
||||
protocolContext,
|
||||
synchronizer));
|
||||
}
|
||||
|
||||
WorldStateArchive createWorldStateArchive(
|
||||
final WorldStateStorageCoordinator worldStateStorageCoordinator,
|
||||
final Blockchain blockchain,
|
||||
final BonsaiCachedMerkleTrieLoader bonsaiCachedMerkleTrieLoader) {
|
||||
final BonsaiCachedMerkleTrieLoader bonsaiCachedMerkleTrieLoader,
|
||||
final Supplier<WorldStateHealer> worldStateHealerSupplier) {
|
||||
return switch (dataStorageConfiguration.getDataStorageFormat()) {
|
||||
case BONSAI -> {
|
||||
final BonsaiWorldStateKeyValueStorage worldStateKeyValueStorage =
|
||||
@@ -1116,7 +1127,8 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
|
||||
.getMaxLayersToLoad()),
|
||||
bonsaiCachedMerkleTrieLoader,
|
||||
besuComponent.map(BesuComponent::getBesuPluginContext).orElse(null),
|
||||
evmConfiguration);
|
||||
evmConfiguration,
|
||||
worldStateHealerSupplier);
|
||||
}
|
||||
case FOREST -> {
|
||||
final WorldStatePreimageStorage preimageStorage =
|
||||
|
||||
@@ -120,6 +120,19 @@ rpc-ws-max-frame-size=65535
|
||||
rpc-ws-authentication-enabled=false
|
||||
rpc-ws-authentication-credentials-file="none"
|
||||
rpc-ws-authentication-jwt-public-key-file="none"
|
||||
rpc-ws-ssl-enabled=false
|
||||
rpc-ws-ssl-keystore-file="none.pfx"
|
||||
rpc-ws-ssl-keystore-password="none.passwd"
|
||||
rpc-ws-ssl-keystore-type="none"
|
||||
rpc-ws-ssl-client-auth-enabled=false
|
||||
rpc-ws-ssl-truststore-file="none.pfx"
|
||||
rpc-ws-ssl-truststore-password="none.passwd"
|
||||
rpc-ws-ssl-truststore-type="none"
|
||||
rpc-ws-ssl-key-file="none.pfx"
|
||||
rpc-ws-ssl-cert-file="none.pfx"
|
||||
rpc-ws-ssl-trustcert-file="none.pfx"
|
||||
|
||||
|
||||
|
||||
# API
|
||||
api-gas-price-blocks=100
|
||||
|
||||
@@ -59,7 +59,6 @@ import org.hyperledger.besu.ethereum.core.ImmutableMiningConfiguration;
|
||||
import org.hyperledger.besu.ethereum.core.ImmutableMiningConfiguration.MutableInitValues;
|
||||
import org.hyperledger.besu.ethereum.core.ImmutableMiningConfiguration.Unstable;
|
||||
import org.hyperledger.besu.ethereum.core.MiningConfiguration;
|
||||
import org.hyperledger.besu.ethereum.core.Synchronizer;
|
||||
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
|
||||
@@ -190,7 +189,6 @@ public class MergeCoordinatorTest implements MergeGenesisConfigHelper {
|
||||
|
||||
protocolContext =
|
||||
new ProtocolContext(blockchain, worldStateArchive, mergeContext, badBlockManager);
|
||||
protocolContext.setSynchronizer(mock(Synchronizer.class));
|
||||
var mutable = worldStateArchive.getMutable();
|
||||
genesisState.writeStateTo(mutable);
|
||||
mutable.persist(null);
|
||||
|
||||
@@ -16,7 +16,7 @@ implementations of Besu might track gas refunds separately.
|
||||
|
||||
### Returned Memory from Calls
|
||||
|
||||
In the `vmTrace` `ope.ex.mem` fields Besu only reports actual data returned
|
||||
In the `vmTrace` `op.ex.mem` fields Besu only reports actual data returned
|
||||
from a `RETURN` opcode. Other implementations return the contents of the
|
||||
reserved output space for the call operations. Note two major differences:
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
|
||||
@@ -49,6 +50,21 @@ public class WebSocketConfiguration {
|
||||
private int maxActiveConnections;
|
||||
private int maxFrameSize;
|
||||
|
||||
private boolean isSslEnabled = false;
|
||||
private Optional<String> keyStorePath = Optional.empty();
|
||||
private Optional<String> keyStorePassword = Optional.empty();
|
||||
private Optional<String> keyStoreType = Optional.of("JKS"); // Default to JKS
|
||||
|
||||
private boolean clientAuthEnabled = false;
|
||||
private Optional<String> trustStorePath = Optional.empty();
|
||||
private Optional<String> trustStorePassword = Optional.empty();
|
||||
private Optional<String> trustStoreType = Optional.of("JKS"); // Default to JKS
|
||||
|
||||
// For PEM format
|
||||
private Optional<String> keyPath = Optional.empty();
|
||||
private Optional<String> certPath = Optional.empty();
|
||||
private Optional<String> trustCertPath = Optional.empty();
|
||||
|
||||
public static WebSocketConfiguration createDefault() {
|
||||
final WebSocketConfiguration config = new WebSocketConfiguration();
|
||||
config.setEnabled(false);
|
||||
@@ -159,6 +175,102 @@ public class WebSocketConfiguration {
|
||||
this.timeoutSec = timeoutSec;
|
||||
}
|
||||
|
||||
public boolean isSslEnabled() {
|
||||
return isSslEnabled;
|
||||
}
|
||||
|
||||
public void setSslEnabled(final boolean isSslEnabled) {
|
||||
this.isSslEnabled = isSslEnabled;
|
||||
}
|
||||
|
||||
public Optional<String> getKeyStorePath() {
|
||||
return keyStorePath;
|
||||
}
|
||||
|
||||
public void setKeyStorePath(final String keyStorePath) {
|
||||
this.keyStorePath = Optional.ofNullable(keyStorePath);
|
||||
}
|
||||
|
||||
public Optional<String> getKeyStorePassword() {
|
||||
return keyStorePassword;
|
||||
}
|
||||
|
||||
public void setKeyStorePassword(final String keyStorePassword) {
|
||||
this.keyStorePassword = Optional.ofNullable(keyStorePassword);
|
||||
}
|
||||
|
||||
// Keystore Type
|
||||
public Optional<String> getKeyStoreType() {
|
||||
return keyStoreType;
|
||||
}
|
||||
|
||||
public void setKeyStoreType(final String keyStoreType) {
|
||||
this.keyStoreType = Optional.ofNullable(keyStoreType);
|
||||
}
|
||||
|
||||
// Key Path (for PEM)
|
||||
public Optional<String> getKeyPath() {
|
||||
return keyPath;
|
||||
}
|
||||
|
||||
public void setKeyPath(final String keyPath) {
|
||||
this.keyPath = Optional.ofNullable(keyPath);
|
||||
}
|
||||
|
||||
// Cert Path (for PEM)
|
||||
public Optional<String> getCertPath() {
|
||||
return certPath;
|
||||
}
|
||||
|
||||
public void setCertPath(final String certPath) {
|
||||
this.certPath = Optional.ofNullable(certPath);
|
||||
}
|
||||
|
||||
// Client Authentication Enabled
|
||||
public boolean isClientAuthEnabled() {
|
||||
return clientAuthEnabled;
|
||||
}
|
||||
|
||||
public void setClientAuthEnabled(final boolean clientAuthEnabled) {
|
||||
this.clientAuthEnabled = clientAuthEnabled;
|
||||
}
|
||||
|
||||
// Truststore Path
|
||||
public Optional<String> getTrustStorePath() {
|
||||
return trustStorePath;
|
||||
}
|
||||
|
||||
public void setTrustStorePath(final String trustStorePath) {
|
||||
this.trustStorePath = Optional.ofNullable(trustStorePath);
|
||||
}
|
||||
|
||||
// Truststore Password
|
||||
public Optional<String> getTrustStorePassword() {
|
||||
return trustStorePassword;
|
||||
}
|
||||
|
||||
public void setTrustStorePassword(final String trustStorePassword) {
|
||||
this.trustStorePassword = Optional.ofNullable(trustStorePassword);
|
||||
}
|
||||
|
||||
// Truststore Type
|
||||
public Optional<String> getTrustStoreType() {
|
||||
return trustStoreType;
|
||||
}
|
||||
|
||||
public void setTrustStoreType(final String trustStoreType) {
|
||||
this.trustStoreType = Optional.ofNullable(trustStoreType);
|
||||
}
|
||||
|
||||
// Trust Cert Path (for PEM)
|
||||
public Optional<String> getTrustCertPath() {
|
||||
return trustCertPath;
|
||||
}
|
||||
|
||||
public void setTrustCertPath(final String trustCertPath) {
|
||||
this.trustCertPath = Optional.ofNullable(trustCertPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.hyperledger.besu.metrics.BesuMetricCategory;
|
||||
import org.hyperledger.besu.plugin.services.MetricsSystem;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -34,6 +35,7 @@ import io.vertx.core.AsyncResult;
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.core.Vertx;
|
||||
import io.vertx.core.buffer.Buffer;
|
||||
import io.vertx.core.http.ClientAuth;
|
||||
import io.vertx.core.http.HttpConnection;
|
||||
import io.vertx.core.http.HttpServer;
|
||||
import io.vertx.core.http.HttpServerOptions;
|
||||
@@ -41,6 +43,9 @@ import io.vertx.core.http.HttpServerRequest;
|
||||
import io.vertx.core.http.HttpServerResponse;
|
||||
import io.vertx.core.http.ServerWebSocket;
|
||||
import io.vertx.core.net.HostAndPort;
|
||||
import io.vertx.core.net.JksOptions;
|
||||
import io.vertx.core.net.PemKeyCertOptions;
|
||||
import io.vertx.core.net.PemTrustOptions;
|
||||
import io.vertx.core.net.SocketAddress;
|
||||
import io.vertx.ext.web.Router;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
@@ -103,18 +108,65 @@ public class WebSocketService {
|
||||
"Starting Websocket service on {}:{}", configuration.getHost(), configuration.getPort());
|
||||
|
||||
final CompletableFuture<?> resultFuture = new CompletableFuture<>();
|
||||
HttpServerOptions serverOptions =
|
||||
new HttpServerOptions()
|
||||
.setHost(configuration.getHost())
|
||||
.setPort(configuration.getPort())
|
||||
.setHandle100ContinueAutomatically(true)
|
||||
.setCompressionSupported(true)
|
||||
.addWebSocketSubProtocol("undefined")
|
||||
.setMaxWebSocketFrameSize(configuration.getMaxFrameSize())
|
||||
.setMaxWebSocketMessageSize(configuration.getMaxFrameSize() * 4)
|
||||
.setRegisterWebSocketWriteHandlers(true);
|
||||
|
||||
// Check if SSL/TLS is enabled in the configuration
|
||||
if (configuration.isSslEnabled()) {
|
||||
serverOptions.setSsl(true);
|
||||
|
||||
String keystorePath = configuration.getKeyStorePath().orElse(null);
|
||||
String keystorePassword = configuration.getKeyStorePassword().orElse(null);
|
||||
String keyPath = configuration.getKeyPath().orElse(null);
|
||||
String certPath = configuration.getCertPath().orElse(null);
|
||||
|
||||
String keystoreType = configuration.getKeyStoreType().orElse("JKS");
|
||||
|
||||
switch (keystoreType.toUpperCase(Locale.getDefault())) {
|
||||
case "PEM":
|
||||
serverOptions.setKeyCertOptions(
|
||||
new PemKeyCertOptions().setKeyPath(keyPath).setCertPath(certPath));
|
||||
break;
|
||||
case "JKS":
|
||||
default:
|
||||
serverOptions.setKeyCertOptions(
|
||||
new JksOptions().setPath(keystorePath).setPassword(keystorePassword));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Set up truststore for client authentication (mTLS)
|
||||
if (configuration.isClientAuthEnabled()) {
|
||||
serverOptions.setClientAuth(ClientAuth.REQUIRED);
|
||||
|
||||
String truststorePath = configuration.getTrustStorePath().orElse(null);
|
||||
String truststorePassword = configuration.getTrustStorePassword().orElse("");
|
||||
String truststoreType = configuration.getTrustStoreType().orElse("JKS");
|
||||
String trustCertPath = configuration.getTrustCertPath().orElse(null);
|
||||
|
||||
switch (truststoreType.toUpperCase(Locale.getDefault())) {
|
||||
case "PEM":
|
||||
serverOptions.setTrustOptions(new PemTrustOptions().addCertPath(trustCertPath));
|
||||
break;
|
||||
case "JKS":
|
||||
default:
|
||||
serverOptions.setTrustOptions(
|
||||
new JksOptions().setPath(truststorePath).setPassword(truststorePassword));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
httpServer =
|
||||
vertx
|
||||
.createHttpServer(
|
||||
new HttpServerOptions()
|
||||
.setHost(configuration.getHost())
|
||||
.setPort(configuration.getPort())
|
||||
.setHandle100ContinueAutomatically(true)
|
||||
.setCompressionSupported(true)
|
||||
.addWebSocketSubProtocol("undefined")
|
||||
.setMaxWebSocketFrameSize(configuration.getMaxFrameSize())
|
||||
.setMaxWebSocketMessageSize(configuration.getMaxFrameSize() * 4)
|
||||
.setRegisterWebSocketWriteHandlers(true))
|
||||
.createHttpServer(serverOptions)
|
||||
.webSocketHandler(websocketHandler())
|
||||
.connectionHandler(connectionHandler())
|
||||
.requestHandler(httpHandler())
|
||||
|
||||
@@ -0,0 +1,444 @@
|
||||
/*
|
||||
* 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.jsonrpc.websocket;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
import org.hyperledger.besu.ethereum.api.handlers.TimeoutOptions;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.execution.BaseJsonRpcProcessor;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.execution.JsonRpcExecutor;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.JsonRpcMethod;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.methods.WebSocketMethodsFactory;
|
||||
import org.hyperledger.besu.ethereum.api.jsonrpc.websocket.subscription.SubscriptionManager;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
|
||||
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.security.KeyStore;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import io.netty.handler.ssl.util.SelfSignedCertificate;
|
||||
import io.vertx.core.Vertx;
|
||||
import io.vertx.core.http.WebSocketClient;
|
||||
import io.vertx.core.http.WebSocketClientOptions;
|
||||
import io.vertx.core.net.JksOptions;
|
||||
import io.vertx.core.net.PemTrustOptions;
|
||||
import io.vertx.junit5.VertxExtension;
|
||||
import io.vertx.junit5.VertxTestContext;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
||||
@ExtendWith(VertxExtension.class)
|
||||
public class WebSocketServiceTLSTest {
|
||||
|
||||
private Vertx vertx;
|
||||
private WebSocketConfiguration config;
|
||||
private WebSocketMessageHandler webSocketMessageHandlerSpy;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
vertx = Vertx.vertx();
|
||||
config = WebSocketConfiguration.createDefault();
|
||||
Map<String, JsonRpcMethod> websocketMethods;
|
||||
config.setPort(0); // Use ephemeral port
|
||||
config.setHost("localhost");
|
||||
websocketMethods =
|
||||
new WebSocketMethodsFactory(
|
||||
new SubscriptionManager(new NoOpMetricsSystem()), new HashMap<>())
|
||||
.methods();
|
||||
webSocketMessageHandlerSpy =
|
||||
spy(
|
||||
new WebSocketMessageHandler(
|
||||
vertx,
|
||||
new JsonRpcExecutor(new BaseJsonRpcProcessor(), websocketMethods),
|
||||
mock(EthScheduler.class),
|
||||
TimeoutOptions.defaultOptions().getTimeoutSeconds()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAcceptSecureWebSocketConnection(final VertxTestContext testContext)
|
||||
throws Throwable {
|
||||
// Generate a self-signed certificate
|
||||
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||
|
||||
// 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, null);
|
||||
keyStore.setKeyEntry(
|
||||
"alias",
|
||||
ssc.key(),
|
||||
"password".toCharArray(),
|
||||
new java.security.cert.Certificate[] {ssc.cert()});
|
||||
|
||||
// Save the keystore to the temporary file
|
||||
try (FileOutputStream fos = new FileOutputStream(keystoreFile)) {
|
||||
keyStore.store(fos, "password".toCharArray());
|
||||
}
|
||||
|
||||
// Configure WebSocket with SSL enabled
|
||||
config.setSslEnabled(true);
|
||||
config.setKeyStorePath(keystoreFile.getAbsolutePath());
|
||||
config.setKeyStorePassword("password");
|
||||
config.setKeyStoreType("JKS");
|
||||
|
||||
// Create and start WebSocketService
|
||||
WebSocketService webSocketService =
|
||||
new WebSocketService(vertx, config, webSocketMessageHandlerSpy, new NoOpMetricsSystem());
|
||||
webSocketService.start().join();
|
||||
|
||||
// Get the actual port
|
||||
int port = webSocketService.socketAddress().getPort();
|
||||
|
||||
// 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());
|
||||
}
|
||||
|
||||
// Configure the HTTP client with the truststore
|
||||
WebSocketClientOptions clientOptions =
|
||||
new WebSocketClientOptions()
|
||||
.setSsl(true)
|
||||
.setTrustOptions(
|
||||
new JksOptions().setPath(truststoreFile.getAbsolutePath()).setPassword("password"))
|
||||
.setVerifyHost(true);
|
||||
|
||||
WebSocketClient webSocketClient = vertx.createWebSocketClient(clientOptions);
|
||||
webSocketClient
|
||||
.connect(port, "localhost", "/")
|
||||
.onSuccess(
|
||||
ws -> {
|
||||
assertThat(ws.isSsl()).isTrue();
|
||||
ws.close();
|
||||
testContext.completeNow();
|
||||
})
|
||||
.onFailure(testContext::failNow);
|
||||
|
||||
assertThat(testContext.awaitCompletion(5, TimeUnit.SECONDS)).isTrue();
|
||||
if (testContext.failed()) {
|
||||
throw testContext.causeOfFailure();
|
||||
}
|
||||
|
||||
// Stop the WebSocketService after the test
|
||||
webSocketService.stop().join();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAcceptSecureWebSocketConnectionPEM(final VertxTestContext testContext)
|
||||
throws Throwable {
|
||||
// Generate a self-signed certificate
|
||||
SelfSignedCertificate ssc = new SelfSignedCertificate();
|
||||
|
||||
// Create temporary PEM files for the certificate and key
|
||||
File certFile = File.createTempFile("cert", ".pem");
|
||||
certFile.deleteOnExit();
|
||||
File keyFile = File.createTempFile("key", ".pem");
|
||||
keyFile.deleteOnExit();
|
||||
|
||||
// Write the certificate and key to the PEM files
|
||||
try (FileOutputStream certOut = new FileOutputStream(certFile);
|
||||
FileOutputStream keyOut = new FileOutputStream(keyFile)) {
|
||||
certOut.write("-----BEGIN CERTIFICATE-----\n".getBytes(UTF_8));
|
||||
certOut.write(
|
||||
Base64.getMimeEncoder(64, "\n".getBytes(UTF_8)).encode(ssc.cert().getEncoded()));
|
||||
certOut.write("\n-----END CERTIFICATE-----\n".getBytes(UTF_8));
|
||||
|
||||
keyOut.write("-----BEGIN PRIVATE KEY-----\n".getBytes(UTF_8));
|
||||
keyOut.write(Base64.getMimeEncoder(64, "\n".getBytes(UTF_8)).encode(ssc.key().getEncoded()));
|
||||
keyOut.write("\n-----END PRIVATE KEY-----\n".getBytes(UTF_8));
|
||||
}
|
||||
|
||||
// Configure WebSocket with SSL enabled using PEM files
|
||||
config.setSslEnabled(true);
|
||||
config.setKeyPath(keyFile.getAbsolutePath());
|
||||
config.setCertPath(certFile.getAbsolutePath());
|
||||
config.setKeyStoreType("PEM");
|
||||
|
||||
// Create and start WebSocketService
|
||||
WebSocketService webSocketService =
|
||||
new WebSocketService(vertx, config, webSocketMessageHandlerSpy, new NoOpMetricsSystem());
|
||||
webSocketService.start().join();
|
||||
|
||||
// Get the actual port
|
||||
int port = webSocketService.socketAddress().getPort();
|
||||
|
||||
// Create a temporary PEM file for the trust store
|
||||
File trustCertFile = File.createTempFile("trust-cert", ".pem");
|
||||
trustCertFile.deleteOnExit();
|
||||
|
||||
// Write the server's certificate to the PEM file
|
||||
try (FileOutputStream trustCertOut = new FileOutputStream(trustCertFile)) {
|
||||
trustCertOut.write("-----BEGIN CERTIFICATE-----\n".getBytes(UTF_8));
|
||||
trustCertOut.write(
|
||||
Base64.getMimeEncoder(64, "\n".getBytes(UTF_8)).encode(ssc.cert().getEncoded()));
|
||||
trustCertOut.write("\n-----END CERTIFICATE-----\n".getBytes(UTF_8));
|
||||
}
|
||||
|
||||
// Configure the HTTP client with the trust store using PEM files
|
||||
WebSocketClientOptions clientOptions =
|
||||
new WebSocketClientOptions()
|
||||
.setSsl(true)
|
||||
.setTrustOptions(new PemTrustOptions().addCertPath(trustCertFile.getAbsolutePath()))
|
||||
.setVerifyHost(true);
|
||||
|
||||
WebSocketClient webSocketClient = vertx.createWebSocketClient(clientOptions);
|
||||
webSocketClient
|
||||
.connect(port, "localhost", "/")
|
||||
.onSuccess(
|
||||
ws -> {
|
||||
assertThat(ws.isSsl()).isTrue();
|
||||
ws.close();
|
||||
testContext.completeNow();
|
||||
})
|
||||
.onFailure(testContext::failNow);
|
||||
|
||||
assertThat(testContext.awaitCompletion(5, TimeUnit.SECONDS)).isTrue();
|
||||
if (testContext.failed()) {
|
||||
throw testContext.causeOfFailure();
|
||||
}
|
||||
|
||||
// Stop the WebSocketService after the test
|
||||
webSocketService.stop().join();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldFailConnectionWithWrongCertificateInTrustStore(
|
||||
final VertxTestContext testContext) throws Throwable {
|
||||
// Generate a self-signed certificate for the server
|
||||
SelfSignedCertificate serverCert = new SelfSignedCertificate();
|
||||
|
||||
// Create a temporary keystore file for the server
|
||||
File keystoreFile = File.createTempFile("keystore", ".p12");
|
||||
keystoreFile.deleteOnExit();
|
||||
|
||||
// Create a PKCS12 keystore and load the server's self-signed certificate
|
||||
KeyStore keyStore = KeyStore.getInstance("PKCS12");
|
||||
keyStore.load(null, null);
|
||||
keyStore.setKeyEntry(
|
||||
"alias",
|
||||
serverCert.key(),
|
||||
"password".toCharArray(),
|
||||
new java.security.cert.Certificate[] {serverCert.cert()});
|
||||
|
||||
// Save the keystore to the temporary file
|
||||
try (FileOutputStream fos = new FileOutputStream(keystoreFile)) {
|
||||
keyStore.store(fos, "password".toCharArray());
|
||||
}
|
||||
|
||||
// Configure WebSocket with SSL enabled
|
||||
config.setSslEnabled(true);
|
||||
config.setKeyStorePath(keystoreFile.getAbsolutePath());
|
||||
config.setKeyStorePassword("password");
|
||||
config.setKeyStoreType("PKCS12");
|
||||
|
||||
// Create and start WebSocketService
|
||||
WebSocketService webSocketService =
|
||||
new WebSocketService(vertx, config, webSocketMessageHandlerSpy, new NoOpMetricsSystem());
|
||||
webSocketService.start().join();
|
||||
|
||||
// Get the actual port
|
||||
int port = webSocketService.socketAddress().getPort();
|
||||
|
||||
// Generate a different self-signed certificate for the trust store
|
||||
SelfSignedCertificate wrongCert = new SelfSignedCertificate();
|
||||
|
||||
// Create a temporary truststore file
|
||||
File truststoreFile = File.createTempFile("truststore", ".p12");
|
||||
truststoreFile.deleteOnExit();
|
||||
|
||||
// Create a PKCS12 truststore and load the wrong certificate
|
||||
KeyStore trustStore = KeyStore.getInstance("PKCS12");
|
||||
trustStore.load(null, null);
|
||||
trustStore.setCertificateEntry("alias", wrongCert.cert());
|
||||
|
||||
// Save the truststore to the temporary file
|
||||
try (FileOutputStream fos = new FileOutputStream(truststoreFile)) {
|
||||
trustStore.store(fos, "password".toCharArray());
|
||||
}
|
||||
|
||||
// Configure the HTTP client with the truststore containing the wrong certificate
|
||||
WebSocketClientOptions clientOptions =
|
||||
new WebSocketClientOptions()
|
||||
.setSsl(true)
|
||||
.setTrustOptions(
|
||||
new JksOptions().setPath(truststoreFile.getAbsolutePath()).setPassword("password"))
|
||||
.setVerifyHost(true);
|
||||
|
||||
WebSocketClient webSocketClient = vertx.createWebSocketClient(clientOptions);
|
||||
webSocketClient
|
||||
.connect(port, "localhost", "/")
|
||||
.onSuccess(
|
||||
ws -> {
|
||||
testContext.failNow(new AssertionError("Connection should have been rejected"));
|
||||
})
|
||||
.onFailure(
|
||||
throwable -> {
|
||||
assertThat(throwable).isInstanceOf(Exception.class);
|
||||
testContext.completeNow();
|
||||
});
|
||||
|
||||
assertThat(testContext.awaitCompletion(5, TimeUnit.SECONDS)).isTrue();
|
||||
if (testContext.failed()) {
|
||||
throw testContext.causeOfFailure();
|
||||
}
|
||||
|
||||
// Stop the WebSocketService after the test
|
||||
webSocketService.stop().join();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAuthenticateClient(final VertxTestContext testContext) throws Throwable {
|
||||
// Generate a self-signed certificate for the server
|
||||
SelfSignedCertificate serverCert = new SelfSignedCertificate();
|
||||
|
||||
// Generate a self-signed certificate for the client
|
||||
SelfSignedCertificate clientCert = new SelfSignedCertificate();
|
||||
|
||||
// Create a temporary keystore file for the server
|
||||
File serverKeystoreFile = File.createTempFile("keystore", ".p12");
|
||||
serverKeystoreFile.deleteOnExit();
|
||||
|
||||
// Create a temporary truststore file for the server
|
||||
File serverTruststoreFile = File.createTempFile("truststore", ".p12");
|
||||
serverTruststoreFile.deleteOnExit();
|
||||
|
||||
// Create a temporary keystore file for the client
|
||||
File clientKeystoreFile = File.createTempFile("client-keystore", ".p12");
|
||||
clientKeystoreFile.deleteOnExit();
|
||||
|
||||
// Create a temporary truststore file for the client
|
||||
File clientTruststoreFile = File.createTempFile("truststore", ".p12");
|
||||
clientTruststoreFile.deleteOnExit();
|
||||
|
||||
// Create a PKCS12 keystore and load the server's self-signed certificate
|
||||
KeyStore serverKeyStore = KeyStore.getInstance("PKCS12");
|
||||
serverKeyStore.load(null, null);
|
||||
serverKeyStore.setKeyEntry(
|
||||
"alias",
|
||||
serverCert.key(),
|
||||
"password".toCharArray(),
|
||||
new java.security.cert.Certificate[] {serverCert.cert()});
|
||||
|
||||
// Save the keystore to the temporary file
|
||||
try (FileOutputStream fos = new FileOutputStream(serverKeystoreFile)) {
|
||||
serverKeyStore.store(fos, "password".toCharArray());
|
||||
}
|
||||
|
||||
// Create a PKCS12 truststore and load the client's self-signed certificate
|
||||
KeyStore serverTrustStore = KeyStore.getInstance("PKCS12");
|
||||
serverTrustStore.load(null, null);
|
||||
serverTrustStore.setCertificateEntry("alias", clientCert.cert());
|
||||
|
||||
// Save the truststore to the temporary file
|
||||
try (FileOutputStream fos = new FileOutputStream(serverTruststoreFile)) {
|
||||
serverTrustStore.store(fos, "password".toCharArray());
|
||||
}
|
||||
|
||||
// Create a PKCS12 keystore and load the client's self-signed certificate
|
||||
KeyStore clientKeyStore = KeyStore.getInstance("PKCS12");
|
||||
clientKeyStore.load(null, null);
|
||||
clientKeyStore.setKeyEntry(
|
||||
"alias",
|
||||
clientCert.key(),
|
||||
"password".toCharArray(),
|
||||
new java.security.cert.Certificate[] {clientCert.cert()});
|
||||
|
||||
// Save the client keystore to the temporary file
|
||||
try (FileOutputStream fos = new FileOutputStream(clientKeystoreFile)) {
|
||||
clientKeyStore.store(fos, "password".toCharArray());
|
||||
}
|
||||
|
||||
// Create a PKCS12 truststore and load the server's self-signed certificate
|
||||
KeyStore clientTrustStore = KeyStore.getInstance("PKCS12");
|
||||
clientTrustStore.load(null, null);
|
||||
clientTrustStore.setCertificateEntry("alias", serverCert.cert());
|
||||
|
||||
// Save the truststore to the temporary file
|
||||
try (FileOutputStream fos = new FileOutputStream(clientTruststoreFile)) {
|
||||
clientTrustStore.store(fos, "password".toCharArray());
|
||||
}
|
||||
|
||||
// Configure WebSocket with SSL and client authentication enabled
|
||||
config.setSslEnabled(true);
|
||||
config.setKeyStorePath(serverKeystoreFile.getAbsolutePath());
|
||||
config.setKeyStorePassword("password");
|
||||
config.setKeyStoreType("PKCS12");
|
||||
config.setClientAuthEnabled(true);
|
||||
config.setTrustStorePath(serverTruststoreFile.getAbsolutePath());
|
||||
config.setTrustStorePassword("password");
|
||||
config.setTrustStoreType("PKCS12");
|
||||
|
||||
// Create and start WebSocketService
|
||||
WebSocketService webSocketService =
|
||||
new WebSocketService(vertx, config, webSocketMessageHandlerSpy, new NoOpMetricsSystem());
|
||||
webSocketService.start().join();
|
||||
|
||||
// Get the actual port
|
||||
int port = webSocketService.socketAddress().getPort();
|
||||
|
||||
// Configure the HTTP client with the client certificate
|
||||
WebSocketClientOptions clientOptions =
|
||||
new WebSocketClientOptions()
|
||||
.setSsl(true)
|
||||
.setKeyStoreOptions(
|
||||
new JksOptions()
|
||||
.setPath(clientKeystoreFile.getAbsolutePath())
|
||||
.setPassword("password"))
|
||||
.setTrustOptions(
|
||||
new JksOptions()
|
||||
.setPath(clientTruststoreFile.getAbsolutePath())
|
||||
.setPassword("password"))
|
||||
.setVerifyHost(true);
|
||||
|
||||
WebSocketClient webSocketClient = vertx.createWebSocketClient(clientOptions);
|
||||
webSocketClient
|
||||
.connect(port, "localhost", "/")
|
||||
.onSuccess(
|
||||
ws -> {
|
||||
assertThat(ws.isSsl()).isTrue();
|
||||
ws.close();
|
||||
testContext.completeNow();
|
||||
})
|
||||
.onFailure(testContext::failNow);
|
||||
|
||||
assertThat(testContext.awaitCompletion(5, TimeUnit.SECONDS)).isTrue();
|
||||
if (testContext.failed()) {
|
||||
throw testContext.causeOfFailure();
|
||||
}
|
||||
|
||||
// Stop the WebSocketService after the test
|
||||
webSocketService.stop().join();
|
||||
}
|
||||
}
|
||||
@@ -181,7 +181,7 @@ public class MainnetBlockValidator implements BlockValidator {
|
||||
Optional.of(new BlockProcessingOutputs(worldState, receipts, maybeRequests)));
|
||||
}
|
||||
} catch (MerkleTrieException ex) {
|
||||
context.getSynchronizer().healWorldState(ex.getMaybeAddress(), ex.getLocation());
|
||||
context.getWorldStateArchive().heal(ex.getMaybeAddress(), ex.getLocation());
|
||||
return new BlockProcessingResult(Optional.empty(), ex);
|
||||
} catch (StorageException ex) {
|
||||
var retval = new BlockProcessingResult(Optional.empty(), ex);
|
||||
|
||||
@@ -16,7 +16,6 @@ package org.hyperledger.besu.ethereum;
|
||||
|
||||
import org.hyperledger.besu.ethereum.chain.BadBlockManager;
|
||||
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
|
||||
import org.hyperledger.besu.ethereum.core.Synchronizer;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
|
||||
|
||||
@@ -32,7 +31,6 @@ public class ProtocolContext {
|
||||
private final WorldStateArchive worldStateArchive;
|
||||
private final BadBlockManager badBlockManager;
|
||||
private final ConsensusContext consensusContext;
|
||||
private Synchronizer synchronizer;
|
||||
|
||||
/**
|
||||
* Constructs a new ProtocolContext with the given blockchain, world state archive, consensus
|
||||
@@ -78,24 +76,6 @@ public class ProtocolContext {
|
||||
badBlockManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the synchronizer of the protocol context.
|
||||
*
|
||||
* @return the synchronizer of the protocol context
|
||||
*/
|
||||
public Synchronizer getSynchronizer() {
|
||||
return synchronizer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the synchronizer of the protocol context.
|
||||
*
|
||||
* @param synchronizer the synchronizer to set
|
||||
*/
|
||||
public void setSynchronizer(final Synchronizer synchronizer) {
|
||||
this.synchronizer = synchronizer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the blockchain of the protocol context.
|
||||
*
|
||||
|
||||
@@ -34,6 +34,7 @@ import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
@@ -44,6 +45,7 @@ public class BonsaiWorldStateProvider extends DiffBasedWorldStateProvider {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(BonsaiWorldStateProvider.class);
|
||||
private final BonsaiCachedMerkleTrieLoader bonsaiCachedMerkleTrieLoader;
|
||||
private final Supplier<WorldStateHealer> worldStateHealerSupplier;
|
||||
|
||||
public BonsaiWorldStateProvider(
|
||||
final BonsaiWorldStateKeyValueStorage worldStateKeyValueStorage,
|
||||
@@ -51,9 +53,11 @@ public class BonsaiWorldStateProvider extends DiffBasedWorldStateProvider {
|
||||
final Optional<Long> maxLayersToLoad,
|
||||
final BonsaiCachedMerkleTrieLoader bonsaiCachedMerkleTrieLoader,
|
||||
final BesuContext pluginContext,
|
||||
final EvmConfiguration evmConfiguration) {
|
||||
final EvmConfiguration evmConfiguration,
|
||||
final Supplier<WorldStateHealer> worldStateHealerSupplier) {
|
||||
super(worldStateKeyValueStorage, blockchain, maxLayersToLoad, pluginContext);
|
||||
this.bonsaiCachedMerkleTrieLoader = bonsaiCachedMerkleTrieLoader;
|
||||
this.worldStateHealerSupplier = worldStateHealerSupplier;
|
||||
provideCachedWorldStorageManager(
|
||||
new BonsaiCachedWorldStorageManager(
|
||||
this, worldStateKeyValueStorage, this::cloneBonsaiWorldStateConfig));
|
||||
@@ -69,9 +73,11 @@ public class BonsaiWorldStateProvider extends DiffBasedWorldStateProvider {
|
||||
final BonsaiWorldStateKeyValueStorage worldStateKeyValueStorage,
|
||||
final Blockchain blockchain,
|
||||
final BonsaiCachedMerkleTrieLoader bonsaiCachedMerkleTrieLoader,
|
||||
final EvmConfiguration evmConfiguration) {
|
||||
final EvmConfiguration evmConfiguration,
|
||||
final Supplier<WorldStateHealer> worldStateHealerSupplier) {
|
||||
super(worldStateKeyValueStorage, blockchain, trieLogManager);
|
||||
this.bonsaiCachedMerkleTrieLoader = bonsaiCachedMerkleTrieLoader;
|
||||
this.worldStateHealerSupplier = worldStateHealerSupplier;
|
||||
provideCachedWorldStorageManager(bonsaiCachedWorldStorageManager);
|
||||
loadPersistedState(
|
||||
new BonsaiWorldState(
|
||||
@@ -151,4 +157,9 @@ public class BonsaiWorldStateProvider extends DiffBasedWorldStateProvider {
|
||||
private DiffBasedWorldStateConfig cloneBonsaiWorldStateConfig() {
|
||||
return new DiffBasedWorldStateConfig(defaultWorldStateConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void heal(final Optional<Address> maybeAccountToRepair, final Bytes location) {
|
||||
worldStateHealerSupplier.get().heal(maybeAccountToRepair, location);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,6 +112,11 @@ public class ForestWorldStateArchive implements WorldStateArchive {
|
||||
blockHeader.getStateRoot(), accountAddress, accountStorageKeys));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void heal(final Optional<Address> maybeAccountToRepair, final Bytes location) {
|
||||
// no heal needed for Forest
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// no op
|
||||
|
||||
@@ -64,4 +64,24 @@ public interface WorldStateArchive extends Closeable {
|
||||
final Address accountAddress,
|
||||
final List<UInt256> accountStorageKeys,
|
||||
final Function<Optional<WorldStateProof>, ? extends Optional<U>> mapper);
|
||||
|
||||
/**
|
||||
* Heal the world state to fix inconsistency
|
||||
*
|
||||
* @param maybeAccountToRepair the optional account to repair
|
||||
* @param location the location of the inconsistency
|
||||
*/
|
||||
void heal(Optional<Address> maybeAccountToRepair, Bytes location);
|
||||
|
||||
/** A world state healer */
|
||||
@FunctionalInterface
|
||||
interface WorldStateHealer {
|
||||
/**
|
||||
* Heal the world state to fix inconsistency
|
||||
*
|
||||
* @param maybeAccountToRepair the optional account to repair
|
||||
* @param location the location of the inconsistency
|
||||
*/
|
||||
void heal(Optional<Address> maybeAccountToRepair, Bytes location);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,7 +194,6 @@ public class BlockchainSetupUtil {
|
||||
|
||||
genesisState.writeStateTo(worldArchive.getMutable());
|
||||
final ProtocolContext protocolContext = protocolContextProvider.get(blockchain, worldArchive);
|
||||
protocolContext.setSynchronizer(new DummySynchronizer());
|
||||
|
||||
final Path blocksPath = Path.of(chainResources.getBlocksURL().toURI());
|
||||
final List<Block> blocks = new ArrayList<>();
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.core;
|
||||
|
||||
import static org.hyperledger.besu.ethereum.core.WorldStateHealerHelper.throwingWorldStateHealerSupplier;
|
||||
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.chain.DefaultBlockchain;
|
||||
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
|
||||
@@ -105,7 +107,8 @@ public class InMemoryKeyValueStorageProvider extends KeyValueStorageProvider {
|
||||
Optional.empty(),
|
||||
bonsaiCachedMerkleTrieLoader,
|
||||
null,
|
||||
evmConfiguration);
|
||||
evmConfiguration,
|
||||
throwingWorldStateHealerSupplier());
|
||||
}
|
||||
|
||||
public static MutableWorldState createInMemoryWorldState() {
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.core;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Address;
|
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive.WorldStateHealer;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
public class WorldStateHealerHelper {
|
||||
|
||||
public static WorldStateHealer throwingHealer(
|
||||
final Optional<Address> maybeAccountToRepair, final Bytes location) {
|
||||
throw new RuntimeException(
|
||||
"World state needs to be healed: "
|
||||
+ maybeAccountToRepair.map(address -> "account to repair: " + address).orElse("")
|
||||
+ " location: "
|
||||
+ location.toHexString());
|
||||
}
|
||||
|
||||
public static Supplier<WorldStateHealer> throwingWorldStateHealerSupplier() {
|
||||
return () -> WorldStateHealerHelper::throwingHealer;
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,6 @@ import org.hyperledger.besu.ethereum.core.Block;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
|
||||
import org.hyperledger.besu.ethereum.core.MutableWorldState;
|
||||
import org.hyperledger.besu.ethereum.core.Synchronizer;
|
||||
import org.hyperledger.besu.ethereum.mainnet.BlockBodyValidator;
|
||||
import org.hyperledger.besu.ethereum.mainnet.BlockHeaderValidator;
|
||||
import org.hyperledger.besu.ethereum.mainnet.BlockProcessor;
|
||||
@@ -91,7 +90,6 @@ public class MainnetBlockValidatorTest {
|
||||
|
||||
when(protocolContext.getBlockchain()).thenReturn(blockchain);
|
||||
when(protocolContext.getWorldStateArchive()).thenReturn(worldStateArchive);
|
||||
when(protocolContext.getSynchronizer()).thenReturn(mock(Synchronizer.class));
|
||||
when(worldStateArchive.getMutable(any(BlockHeader.class), anyBoolean()))
|
||||
.thenReturn(Optional.of(worldState));
|
||||
when(worldStateArchive.getMutable(any(Hash.class), any(Hash.class)))
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package org.hyperledger.besu.ethereum.trie.diffbased.bonsai;
|
||||
|
||||
import static org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider.createInMemoryBlockchain;
|
||||
import static org.hyperledger.besu.ethereum.core.WorldStateHealerHelper.throwingWorldStateHealerSupplier;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -167,7 +168,8 @@ public abstract class AbstractIsolationTests {
|
||||
Optional.of(16L),
|
||||
new BonsaiCachedMerkleTrieLoader(new NoOpMetricsSystem()),
|
||||
null,
|
||||
EvmConfiguration.DEFAULT);
|
||||
EvmConfiguration.DEFAULT,
|
||||
throwingWorldStateHealerSupplier());
|
||||
var ws = archive.getMutable();
|
||||
genesisState.writeStateTo(ws);
|
||||
protocolContext = new ProtocolContext(blockchain, archive, null, new BadBlockManager());
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package org.hyperledger.besu.ethereum.trie.diffbased.bonsai;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hyperledger.besu.ethereum.core.WorldStateHealerHelper.throwingWorldStateHealerSupplier;
|
||||
import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.BLOCKCHAIN;
|
||||
import static org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier.TRIE_BRANCH_STORAGE;
|
||||
import static org.hyperledger.besu.ethereum.trie.diffbased.common.storage.DiffBasedWorldStateKeyValueStorage.WORLD_BLOCK_HASH_KEY;
|
||||
@@ -111,7 +112,8 @@ class BonsaiWorldStateProviderTest {
|
||||
DataStorageConfiguration.DEFAULT_BONSAI_CONFIG),
|
||||
blockchain,
|
||||
new BonsaiCachedMerkleTrieLoader(new NoOpMetricsSystem()),
|
||||
EvmConfiguration.DEFAULT);
|
||||
EvmConfiguration.DEFAULT,
|
||||
throwingWorldStateHealerSupplier());
|
||||
|
||||
assertThat(bonsaiWorldStateArchive.getMutable(chainHead, true))
|
||||
.containsInstanceOf(BonsaiWorldState.class);
|
||||
@@ -129,7 +131,8 @@ class BonsaiWorldStateProviderTest {
|
||||
Optional.of(512L),
|
||||
new BonsaiCachedMerkleTrieLoader(new NoOpMetricsSystem()),
|
||||
null,
|
||||
EvmConfiguration.DEFAULT);
|
||||
EvmConfiguration.DEFAULT,
|
||||
throwingWorldStateHealerSupplier());
|
||||
final BlockHeader blockHeader = blockBuilder.number(0).buildHeader();
|
||||
final BlockHeader chainHead = blockBuilder.number(512).buildHeader();
|
||||
when(blockchain.getChainHeadHeader()).thenReturn(chainHead);
|
||||
@@ -150,7 +153,8 @@ class BonsaiWorldStateProviderTest {
|
||||
DataStorageConfiguration.DEFAULT_BONSAI_CONFIG),
|
||||
blockchain,
|
||||
new BonsaiCachedMerkleTrieLoader(new NoOpMetricsSystem()),
|
||||
EvmConfiguration.DEFAULT);
|
||||
EvmConfiguration.DEFAULT,
|
||||
throwingWorldStateHealerSupplier());
|
||||
final BlockHeader blockHeader = blockBuilder.number(0).buildHeader();
|
||||
final BlockHeader chainHead = blockBuilder.number(511).buildHeader();
|
||||
final BonsaiWorldState mockWorldState = mock(BonsaiWorldState.class);
|
||||
@@ -185,7 +189,8 @@ class BonsaiWorldStateProviderTest {
|
||||
worldStateKeyValueStorage,
|
||||
blockchain,
|
||||
new BonsaiCachedMerkleTrieLoader(new NoOpMetricsSystem()),
|
||||
EvmConfiguration.DEFAULT));
|
||||
EvmConfiguration.DEFAULT,
|
||||
throwingWorldStateHealerSupplier()));
|
||||
final BlockHeader blockHeader = blockBuilder.number(0).buildHeader();
|
||||
|
||||
when(blockchain.getBlockHeader(blockHeader.getHash())).thenReturn(Optional.of(blockHeader));
|
||||
@@ -214,7 +219,8 @@ class BonsaiWorldStateProviderTest {
|
||||
worldStateKeyValueStorage,
|
||||
blockchain,
|
||||
new BonsaiCachedMerkleTrieLoader(new NoOpMetricsSystem()),
|
||||
EvmConfiguration.DEFAULT));
|
||||
EvmConfiguration.DEFAULT,
|
||||
throwingWorldStateHealerSupplier()));
|
||||
|
||||
final BlockHeader blockHeader = blockBuilder.number(0).buildHeader();
|
||||
|
||||
@@ -254,7 +260,8 @@ class BonsaiWorldStateProviderTest {
|
||||
worldStateKeyValueStorage,
|
||||
blockchain,
|
||||
new BonsaiCachedMerkleTrieLoader(new NoOpMetricsSystem()),
|
||||
EvmConfiguration.DEFAULT));
|
||||
EvmConfiguration.DEFAULT,
|
||||
throwingWorldStateHealerSupplier()));
|
||||
|
||||
// initial persisted state hash key
|
||||
when(blockchain.getBlockHeader(Hash.ZERO)).thenReturn(Optional.of(blockHeaderChainA));
|
||||
@@ -297,7 +304,8 @@ class BonsaiWorldStateProviderTest {
|
||||
DataStorageConfiguration.DEFAULT_BONSAI_CONFIG),
|
||||
blockchain,
|
||||
new BonsaiCachedMerkleTrieLoader(new NoOpMetricsSystem()),
|
||||
EvmConfiguration.DEFAULT));
|
||||
EvmConfiguration.DEFAULT,
|
||||
throwingWorldStateHealerSupplier()));
|
||||
|
||||
// initial persisted state hash key
|
||||
when(blockchain.getBlockHeader(Hash.ZERO)).thenReturn(Optional.of(blockHeaderChainA));
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package org.hyperledger.besu.ethereum.eth.manager.snap;
|
||||
|
||||
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
import org.hyperledger.besu.ethereum.core.Synchronizer;
|
||||
import org.hyperledger.besu.ethereum.eth.SnapProtocol;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthMessages;
|
||||
@@ -53,11 +54,13 @@ public class SnapProtocolManager implements ProtocolManager {
|
||||
final SnapSyncConfiguration snapConfig,
|
||||
final EthPeers ethPeers,
|
||||
final EthMessages snapMessages,
|
||||
final ProtocolContext protocolContext) {
|
||||
final ProtocolContext protocolContext,
|
||||
final Synchronizer synchronizer) {
|
||||
this.ethPeers = ethPeers;
|
||||
this.snapMessages = snapMessages;
|
||||
this.supportedCapabilities = calculateCapabilities();
|
||||
new SnapServer(snapConfig, snapMessages, worldStateStorageCoordinator, protocolContext);
|
||||
new SnapServer(
|
||||
snapConfig, snapMessages, worldStateStorageCoordinator, protocolContext, synchronizer);
|
||||
}
|
||||
|
||||
private List<Capability> calculateCapabilities() {
|
||||
|
||||
@@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.eth.manager.snap;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
import org.hyperledger.besu.ethereum.core.Synchronizer;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthMessages;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.snap.AccountRangeMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.snap.ByteCodesMessage;
|
||||
@@ -98,7 +99,8 @@ class SnapServer implements BesuEvents.InitialSyncCompletionListener {
|
||||
final SnapSyncConfiguration snapConfig,
|
||||
final EthMessages snapMessages,
|
||||
final WorldStateStorageCoordinator worldStateStorageCoordinator,
|
||||
final ProtocolContext protocolContext) {
|
||||
final ProtocolContext protocolContext,
|
||||
final Synchronizer synchronizer) {
|
||||
this.snapServerEnabled =
|
||||
Optional.ofNullable(snapConfig)
|
||||
.map(SnapSyncConfiguration::isSnapServerEnabled)
|
||||
@@ -110,7 +112,7 @@ class SnapServer implements BesuEvents.InitialSyncCompletionListener {
|
||||
|
||||
// subscribe to initial sync completed events to start/stop snap server,
|
||||
// not saving the listenerId since we never need to unsubscribe.
|
||||
protocolContext.getSynchronizer().subscribeInitialSync(this);
|
||||
synchronizer.subscribeInitialSync(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -37,7 +37,8 @@ import com.google.common.annotations.VisibleForTesting;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class TransactionBroadcaster implements TransactionBatchAddedListener {
|
||||
public class TransactionBroadcaster
|
||||
implements TransactionBatchAddedListener, PendingTransactionDroppedListener {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TransactionBroadcaster.class);
|
||||
|
||||
private static final EnumSet<TransactionType> ANNOUNCE_HASH_ONLY_TX_TYPES = EnumSet.of(BLOB);
|
||||
@@ -219,4 +220,9 @@ public class TransactionBroadcaster implements TransactionBatchAddedListener {
|
||||
destinationList.add(sourceList.remove(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransactionDropped(final Transaction transaction, final RemovalReason reason) {
|
||||
transactionTracker.onTransactionDropped(transaction, reason);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,6 +139,7 @@ public class TransactionPool implements BlockAddedObserver {
|
||||
subscribePendingTransactions(this::mapBlobsOnTransactionAdded);
|
||||
subscribeDroppedTransactions(
|
||||
(transaction, reason) -> unmapBlobsOnTransactionDropped(transaction));
|
||||
subscribeDroppedTransactions(transactionBroadcaster);
|
||||
}
|
||||
|
||||
private void initLogForReplay() {
|
||||
|
||||
@@ -15,10 +15,12 @@
|
||||
package org.hyperledger.besu.ethereum.eth.manager.snap;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import org.hyperledger.besu.datatypes.Hash;
|
||||
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
|
||||
import org.hyperledger.besu.ethereum.core.Synchronizer;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthMessages;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.snap.AccountRangeMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.snap.GetAccountRangeMessage;
|
||||
@@ -70,7 +72,8 @@ public class SnapServerGetAccountRangeTest {
|
||||
snapSyncConfiguration,
|
||||
new EthMessages(),
|
||||
worldStateStorageCoordinator,
|
||||
protocolContext)
|
||||
protocolContext,
|
||||
mock(Synchronizer.class))
|
||||
.start();
|
||||
initAccounts();
|
||||
}
|
||||
|
||||
@@ -264,7 +264,7 @@ public abstract class RocksDBColumnarKeyValueStorageTest extends AbstractKeyValu
|
||||
List.of());
|
||||
store.close();
|
||||
|
||||
// Create new db without ignoring experimental colum family will add column to db
|
||||
// Create new db without ignoring experimental column family will add column to db
|
||||
store =
|
||||
createSegmentedStore(
|
||||
testPath,
|
||||
|
||||
@@ -25,7 +25,7 @@ public interface FuzzTarget {
|
||||
/**
|
||||
* The target to fuzz
|
||||
*
|
||||
* @param data data proviced by the fuzzer
|
||||
* @param data data provided by the fuzzer
|
||||
*/
|
||||
void fuzz(byte[] data);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user