Merge branch 'main' into zkbesu

This commit is contained in:
Fabio Di Fabio
2024-11-12 12:11:24 +01:00
29 changed files with 936 additions and 66 deletions

27
.github/workflows/stale-issues.yml vendored Normal file
View 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 }}

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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 =

View File

@@ -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

View File

@@ -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);

View File

@@ -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:

View File

@@ -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) {

View File

@@ -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())

View File

@@ -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();
}
}

View File

@@ -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);

View File

@@ -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.
*

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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<>();

View File

@@ -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() {

View File

@@ -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;
}
}

View File

@@ -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)))

View File

@@ -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());

View File

@@ -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));

View File

@@ -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() {

View File

@@ -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);
}
/**

View File

@@ -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);
}
}

View File

@@ -139,6 +139,7 @@ public class TransactionPool implements BlockAddedObserver {
subscribePendingTransactions(this::mapBlobsOnTransactionAdded);
subscribeDroppedTransactions(
(transaction, reason) -> unmapBlobsOnTransactionDropped(transaction));
subscribeDroppedTransactions(transactionBroadcaster);
}
private void initLogForReplay() {

View File

@@ -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();
}

View File

@@ -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,

View File

@@ -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);
}