PKI Block Creation cli configuration (#2619)

* Added CLI options for PKI Block Creation
* BesuCommand changes for PKI Block Creation

Signed-off-by: Lucas Saldanha <lucascrsaldanha@gmail.com>
pull/2623/head
Lucas Saldanha 3 years ago committed by GitHub
parent b9e1ce05c0
commit 047b680ddc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      besu/build.gradle
  2. 21
      besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
  3. 144
      besu/src/main/java/org/hyperledger/besu/cli/options/unstable/PkiBlockCreationOptions.java
  4. 9
      besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java
  5. 13
      besu/src/main/java/org/hyperledger/besu/controller/QbftBesuControllerBuilder.java
  6. 71
      besu/src/test/java/org/hyperledger/besu/cli/BesuCommandTest.java
  7. 23
      besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java
  8. 46
      consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/pki/PkiBlockCreationConfiguration.java
  9. 91
      consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/pki/PkiBlockCreationConfigurationProvider.java
  10. 11
      consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/pki/PkiQbftContext.java
  11. 71
      consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/pki/PkiBlockCreationConfigurationProviderTest.java
  12. 29
      pki/src/main/java/org/hyperledger/besu/pki/config/PkiKeyStoreConfiguration.java

@ -90,4 +90,5 @@ dependencies {
testImplementation 'org.assertj:assertj-core' testImplementation 'org.assertj:assertj-core'
testImplementation 'org.awaitility:awaitility' testImplementation 'org.awaitility:awaitility'
testImplementation 'org.mockito:mockito-core' testImplementation 'org.mockito:mockito-core'
testImplementation 'commons-io:commons-io'
} }

@ -59,6 +59,7 @@ import org.hyperledger.besu.cli.options.unstable.MiningOptions;
import org.hyperledger.besu.cli.options.unstable.NatOptions; import org.hyperledger.besu.cli.options.unstable.NatOptions;
import org.hyperledger.besu.cli.options.unstable.NativeLibraryOptions; import org.hyperledger.besu.cli.options.unstable.NativeLibraryOptions;
import org.hyperledger.besu.cli.options.unstable.NetworkingOptions; import org.hyperledger.besu.cli.options.unstable.NetworkingOptions;
import org.hyperledger.besu.cli.options.unstable.PkiBlockCreationOptions;
import org.hyperledger.besu.cli.options.unstable.PrivacyPluginOptions; import org.hyperledger.besu.cli.options.unstable.PrivacyPluginOptions;
import org.hyperledger.besu.cli.options.unstable.RPCOptions; import org.hyperledger.besu.cli.options.unstable.RPCOptions;
import org.hyperledger.besu.cli.options.unstable.SynchronizerOptions; import org.hyperledger.besu.cli.options.unstable.SynchronizerOptions;
@ -78,6 +79,8 @@ import org.hyperledger.besu.cli.util.VersionProvider;
import org.hyperledger.besu.config.GenesisConfigFile; import org.hyperledger.besu.config.GenesisConfigFile;
import org.hyperledger.besu.config.GenesisConfigOptions; import org.hyperledger.besu.config.GenesisConfigOptions;
import org.hyperledger.besu.config.GoQuorumOptions; import org.hyperledger.besu.config.GoQuorumOptions;
import org.hyperledger.besu.consensus.qbft.pki.PkiBlockCreationConfiguration;
import org.hyperledger.besu.consensus.qbft.pki.PkiBlockCreationConfigurationProvider;
import org.hyperledger.besu.controller.BesuController; import org.hyperledger.besu.controller.BesuController;
import org.hyperledger.besu.controller.BesuControllerBuilder; import org.hyperledger.besu.controller.BesuControllerBuilder;
import org.hyperledger.besu.controller.TargetingGasLimitCalculator; import org.hyperledger.besu.controller.TargetingGasLimitCalculator;
@ -286,6 +289,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
new PreSynchronizationTaskRunner(); new PreSynchronizationTaskRunner();
private final Set<Integer> allocatedPorts = new HashSet<>(); private final Set<Integer> allocatedPorts = new HashSet<>();
private final PkiBlockCreationConfigurationProvider pkiBlockCreationConfigProvider;
// CLI options defined by user at runtime. // CLI options defined by user at runtime.
// Options parsing is done with CLI library Picocli https://picocli.info/ // Options parsing is done with CLI library Picocli https://picocli.info/
@ -1097,6 +1101,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
@Mixin private P2PTLSConfigOptions p2pTLSConfigOptions; @Mixin private P2PTLSConfigOptions p2pTLSConfigOptions;
@Mixin private PkiBlockCreationOptions pkiBlockCreationOptions;
private EthNetworkConfig ethNetworkConfig; private EthNetworkConfig ethNetworkConfig;
private JsonRpcConfiguration jsonRpcConfiguration; private JsonRpcConfiguration jsonRpcConfiguration;
private GraphQLConfiguration graphQLConfiguration; private GraphQLConfiguration graphQLConfiguration;
@ -1136,7 +1142,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
new StorageServiceImpl(), new StorageServiceImpl(),
new SecurityModuleServiceImpl(), new SecurityModuleServiceImpl(),
new PermissioningServiceImpl(), new PermissioningServiceImpl(),
new PrivacyPluginServiceImpl()); new PrivacyPluginServiceImpl(),
new PkiBlockCreationConfigurationProvider());
} }
@VisibleForTesting @VisibleForTesting
@ -1152,7 +1159,8 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
final StorageServiceImpl storageService, final StorageServiceImpl storageService,
final SecurityModuleServiceImpl securityModuleService, final SecurityModuleServiceImpl securityModuleService,
final PermissioningServiceImpl permissioningService, final PermissioningServiceImpl permissioningService,
final PrivacyPluginServiceImpl privacyPluginPluginService) { final PrivacyPluginServiceImpl privacyPluginPluginService,
final PkiBlockCreationConfigurationProvider pkiBlockCreationConfigProvider) {
this.logger = logger; this.logger = logger;
this.rlpBlockImporter = rlpBlockImporter; this.rlpBlockImporter = rlpBlockImporter;
this.rlpBlockExporterFactory = rlpBlockExporterFactory; this.rlpBlockExporterFactory = rlpBlockExporterFactory;
@ -1167,6 +1175,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
this.privacyPluginPluginService = privacyPluginPluginService; this.privacyPluginPluginService = privacyPluginPluginService;
pluginCommonConfiguration = new BesuCommandConfigurationService(); pluginCommonConfiguration = new BesuCommandConfigurationService();
besuPluginContext.addService(BesuConfiguration.class, pluginCommonConfiguration); besuPluginContext.addService(BesuConfiguration.class, pluginCommonConfiguration);
this.pkiBlockCreationConfigProvider = pkiBlockCreationConfigProvider;
} }
public void parse( public void parse(
@ -1441,6 +1450,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
validateNetStatsParams(); validateNetStatsParams();
validateDnsOptionsParams(); validateDnsOptionsParams();
p2pTLSConfigOptions.checkP2PTLSOptionsDependencies(logger, commandLine); p2pTLSConfigOptions.checkP2PTLSOptionsDependencies(logger, commandLine);
pkiBlockCreationOptions.checkPkiBlockCreationOptionsDependencies(logger, commandLine);
} }
@SuppressWarnings("ConstantConditions") @SuppressWarnings("ConstantConditions")
@ -1711,6 +1721,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
.metricsSystem(metricsSystem.get()) .metricsSystem(metricsSystem.get())
.messagePermissioningProviders(permissioningService.getMessagePermissioningProviders()) .messagePermissioningProviders(permissioningService.getMessagePermissioningProviders())
.privacyParameters(privacyParameters(storageProvider)) .privacyParameters(privacyParameters(storageProvider))
.pkiBlockCreationConfiguration(maybePkiBlockCreationConfiguration())
.clock(Clock.systemUTC()) .clock(Clock.systemUTC())
.isRevertReasonEnabled(isRevertReasonEnabled) .isRevertReasonEnabled(isRevertReasonEnabled)
.storageProvider(storageProvider) .storageProvider(storageProvider)
@ -2269,6 +2280,12 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
return this.keyValueStorageProvider; return this.keyValueStorageProvider;
} }
private Optional<PkiBlockCreationConfiguration> maybePkiBlockCreationConfiguration() {
return pkiBlockCreationOptions
.asDomainConfig(commandLine)
.map(pkiBlockCreationConfigProvider::load);
}
private SynchronizerConfiguration buildSyncConfig() { private SynchronizerConfiguration buildSyncConfig() {
return unstableSynchronizerOptions return unstableSynchronizerOptions
.toDomainObject() .toDomainObject()

@ -0,0 +1,144 @@
/*
* Copyright ConsenSys AG.
*
* 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.cli.options.unstable;
import static java.util.Arrays.asList;
import static org.hyperledger.besu.cli.DefaultCommandValues.MANDATORY_FILE_FORMAT_HELP;
import org.hyperledger.besu.cli.util.CommandLineUtils;
import org.hyperledger.besu.ethereum.api.tls.FileBasedPasswordProvider;
import org.hyperledger.besu.pki.config.PkiKeyStoreConfiguration;
import java.nio.file.Path;
import java.util.Optional;
import org.apache.logging.log4j.Logger;
import picocli.CommandLine;
import picocli.CommandLine.Option;
import picocli.CommandLine.ParameterException;
public class PkiBlockCreationOptions {
@Option(
names = {"--Xpki-block-creation-enabled"},
hidden = true,
description = "Enable PKI integration (default: ${DEFAULT-VALUE})")
Boolean enabled = false;
@Option(
names = {"--Xpki-block-creation-keystore-type"},
hidden = true,
paramLabel = "<NAME>",
description = "PKI service keystore type. Required if PKI Block Creation is enabled.")
@SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"})
String keyStoreType = PkiKeyStoreConfiguration.DEFAULT_KEYSTORE_TYPE;
@Option(
names = {"--Xpki-block-creation-keystore-file"},
hidden = true,
paramLabel = MANDATORY_FILE_FORMAT_HELP,
description = "Keystore containing key/certificate for PKI Block Creation.")
Path keyStoreFile = null;
@Option(
names = {"--Xpki-block-creation-keystore-password-file"},
hidden = true,
paramLabel = MANDATORY_FILE_FORMAT_HELP,
description =
"File containing password to unlock keystore for PKI Integration. Required if PKI Block Creation is enabled.")
Path keyStorePasswordFile = null;
@Option(
names = {"--Xpki-block-creation-keystore-certificate-alias"},
hidden = true,
paramLabel = "<NAME>",
description =
"Alias of the certificate that will be included in the blocks proposed by this validator.")
@SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"})
String certificateAlias = PkiKeyStoreConfiguration.DEFAULT_CERTIFICATE_ALIAS;
@Option(
names = {"--Xpki-block-creation-truststore-type"},
hidden = true,
paramLabel = "<NAME>",
description = "PKI Integration truststore type.")
@SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"})
String trustStoreType = PkiKeyStoreConfiguration.DEFAULT_KEYSTORE_TYPE;
@Option(
names = {"--Xpki-block-creation-truststore-file"},
hidden = true,
paramLabel = MANDATORY_FILE_FORMAT_HELP,
description = "Truststore containing trusted certificates for PKI Block Creation.")
Path trustStoreFile = null;
@Option(
names = {"--Xpki-block-creation-truststore-password-file"},
hidden = true,
paramLabel = MANDATORY_FILE_FORMAT_HELP,
description = "File containing password to unlock truststore for PKI Block Creation.")
Path trustStorePasswordFile = null;
@Option(
names = {"--Xpki-block-creation-crl-file"},
hidden = true,
paramLabel = MANDATORY_FILE_FORMAT_HELP,
description = "File with all CRLs for PKI Block Creation.")
Path crlFile = null;
public Optional<PkiKeyStoreConfiguration> asDomainConfig(final CommandLine commandLine) {
if (!enabled) {
return Optional.empty();
}
if (keyStoreFile == null) {
throw new ParameterException(
commandLine, "KeyStore file is required when PKI Block Creation is enabled");
}
if (keyStorePasswordFile == null) {
throw new ParameterException(
commandLine,
"File containing password to unlock keystore is required when PKI Block Creation is enabled");
}
return Optional.of(
new PkiKeyStoreConfiguration.Builder()
.withKeyStoreType(keyStoreType)
.withKeyStorePath(keyStoreFile)
.withKeyStorePasswordSupplier(new FileBasedPasswordProvider(keyStorePasswordFile))
.withCertificateAlias(certificateAlias)
.withTrustStoreType(trustStoreType)
.withTrustStorePath(trustStoreFile)
.withTrustStorePasswordSupplier(
null == trustStorePasswordFile
? null
: new FileBasedPasswordProvider(trustStorePasswordFile))
.withCrlFilePath(crlFile)
.build());
}
public void checkPkiBlockCreationOptionsDependencies(
final Logger logger, final CommandLine commandLine) {
CommandLineUtils.checkOptionDependencies(
logger,
commandLine,
"--Xpki-block-creation-enabled",
!enabled,
asList(
"--Xpki-block-creation-keystore-file", "--Xpki-block-creation-keystore-password-file"));
}
}

@ -17,6 +17,7 @@ package org.hyperledger.besu.controller;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import org.hyperledger.besu.config.GenesisConfigFile; import org.hyperledger.besu.config.GenesisConfigFile;
import org.hyperledger.besu.consensus.qbft.pki.PkiBlockCreationConfiguration;
import org.hyperledger.besu.crypto.NodeKey; import org.hyperledger.besu.crypto.NodeKey;
import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.methods.JsonRpcMethods; import org.hyperledger.besu.ethereum.api.jsonrpc.methods.JsonRpcMethods;
@ -91,6 +92,8 @@ public abstract class BesuControllerBuilder {
protected MiningParameters miningParameters; protected MiningParameters miningParameters;
protected ObservableMetricsSystem metricsSystem; protected ObservableMetricsSystem metricsSystem;
protected PrivacyParameters privacyParameters; protected PrivacyParameters privacyParameters;
protected Optional<PkiBlockCreationConfiguration> pkiBlockCreationConfiguration =
Optional.empty();
protected Path dataDirectory; protected Path dataDirectory;
protected Clock clock; protected Clock clock;
protected NodeKey nodeKey; protected NodeKey nodeKey;
@ -160,6 +163,12 @@ public abstract class BesuControllerBuilder {
return this; return this;
} }
public BesuControllerBuilder pkiBlockCreationConfiguration(
final Optional<PkiBlockCreationConfiguration> pkiBlockCreationConfiguration) {
this.pkiBlockCreationConfiguration = pkiBlockCreationConfiguration;
return this;
}
public BesuControllerBuilder dataDirectory(final Path dataDirectory) { public BesuControllerBuilder dataDirectory(final Path dataDirectory) {
this.dataDirectory = dataDirectory; this.dataDirectory = dataDirectory;
return this; return this;

@ -73,13 +73,11 @@ import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.p2p.config.SubProtocolConfiguration; import org.hyperledger.besu.ethereum.p2p.config.SubProtocolConfiguration;
import org.hyperledger.besu.ethereum.transaction.TransactionSimulator; import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.pki.keystore.KeyStoreWrapper;
import org.hyperledger.besu.util.Subscribers; import org.hyperledger.besu.util.Subscribers;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -93,14 +91,12 @@ public class QbftBesuControllerBuilder extends BftBesuControllerBuilder {
private BftEventQueue bftEventQueue; private BftEventQueue bftEventQueue;
private QbftConfigOptions qbftConfig; private QbftConfigOptions qbftConfig;
private ValidatorPeers peers; private ValidatorPeers peers;
// TODO initialize this in BesuCommand as part of the PKI setup (will be done in a follow up PR)
private final Optional<KeyStoreWrapper> pkiKeyStore = Optional.empty();
@Override @Override
protected Supplier<BftExtraDataCodec> bftExtraDataCodec() { protected Supplier<BftExtraDataCodec> bftExtraDataCodec() {
return Suppliers.memoize( return Suppliers.memoize(
() -> { () -> {
if (pkiKeyStore.isPresent()) { if (pkiBlockCreationConfiguration.isPresent()) {
return new PkiQbftExtraDataCodec(); return new PkiQbftExtraDataCodec();
} else { } else {
return new QbftExtraDataCodec(); return new QbftExtraDataCodec();
@ -295,9 +291,12 @@ public class QbftBesuControllerBuilder extends BftBesuControllerBuilder {
validatorProvider = new TransactionValidatorProvider(blockchain, validatorContractController); validatorProvider = new TransactionValidatorProvider(blockchain, validatorContractController);
} }
if (pkiKeyStore.isPresent()) { if (pkiBlockCreationConfiguration.isPresent()) {
return new PkiQbftContext( return new PkiQbftContext(
validatorProvider, epochManager, bftBlockInterface().get(), pkiKeyStore.get()); validatorProvider,
epochManager,
bftBlockInterface().get(),
pkiBlockCreationConfiguration.get());
} else { } else {
return new BftContext(validatorProvider, epochManager, bftBlockInterface().get()); return new BftContext(validatorProvider, epochManager, bftBlockInterface().get());
} }

@ -75,6 +75,7 @@ import org.hyperledger.besu.ethereum.worldstate.PrunerConfiguration;
import org.hyperledger.besu.metrics.StandardMetricCategory; import org.hyperledger.besu.metrics.StandardMetricCategory;
import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration; import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration;
import org.hyperledger.besu.nat.NatMethod; import org.hyperledger.besu.nat.NatMethod;
import org.hyperledger.besu.pki.config.PkiKeyStoreConfiguration;
import org.hyperledger.besu.plugin.data.EnodeURL; import org.hyperledger.besu.plugin.data.EnodeURL;
import org.hyperledger.besu.util.number.Fraction; import org.hyperledger.besu.util.number.Fraction;
import org.hyperledger.besu.util.number.Percentage; import org.hyperledger.besu.util.number.Percentage;
@ -99,6 +100,7 @@ import java.util.stream.Stream;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.io.Resources; import com.google.common.io.Resources;
import io.vertx.core.json.JsonObject; import io.vertx.core.json.JsonObject;
import org.apache.commons.io.FileUtils;
import org.apache.commons.text.StringEscapeUtils; import org.apache.commons.text.StringEscapeUtils;
import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Level;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
@ -4312,4 +4314,73 @@ public class BesuCommandTest extends CommandTestAbstract {
assertThat(AbstractAltBnPrecompiledContract.isNative()).isTrue(); assertThat(AbstractAltBnPrecompiledContract.isNative()).isTrue();
verify(mockLogger).info("Using LibEthPairings native alt bn128"); verify(mockLogger).info("Using LibEthPairings native alt bn128");
} }
@Test
public void pkiBlockCreationIsDisabledByDefault() {
parseCommand();
verifyNoInteractions(mockPkiBlockCreationConfigProvider);
}
@Test
public void pkiBlockCreationKeyStoreFileRequired() {
parseCommand(
"--Xpki-block-creation-enabled",
"--Xpki-block-creation-keystore-password-file",
"/tmp/pwd");
assertThat(commandErrorOutput.toString())
.contains("KeyStore file is required when PKI Block Creation is enabled");
}
@Test
public void pkiBlockCreationPasswordFileRequired() {
parseCommand(
"--Xpki-block-creation-enabled", "--Xpki-block-creation-keystore-file", "/tmp/keystore");
assertThat(commandErrorOutput.toString())
.contains(
"File containing password to unlock keystore is required when PKI Block Creation is enabled");
}
@Rule public TemporaryFolder pkiTempFolder = new TemporaryFolder();
@Test
public void pkiBlockCreationFullConfig() throws Exception {
// Create temp file with password
final File pwdFile = pkiTempFolder.newFile("pwd");
FileUtils.writeStringToFile(pwdFile, "foo", UTF_8);
parseCommand(
"--Xpki-block-creation-enabled",
"--Xpki-block-creation-keystore-type",
"JKS",
"--Xpki-block-creation-keystore-file",
"/tmp/keystore",
"--Xpki-block-creation-keystore-password-file",
pwdFile.getAbsolutePath(),
"--Xpki-block-creation-keystore-certificate-alias",
"anAlias",
"--Xpki-block-creation-truststore-type",
"JKS",
"--Xpki-block-creation-truststore-file",
"/tmp/truststore",
"--Xpki-block-creation-truststore-password-file",
pwdFile.getAbsolutePath(),
"--Xpki-block-creation-crl-file",
"/tmp/crl");
final PkiKeyStoreConfiguration pkiKeyStoreConfig =
pkiKeyStoreConfigurationArgumentCaptor.getValue();
assertThat(pkiKeyStoreConfig).isNotNull();
assertThat(pkiKeyStoreConfig.getKeyStoreType()).isEqualTo("JKS");
assertThat(pkiKeyStoreConfig.getKeyStorePath()).isEqualTo(Path.of("/tmp/keystore"));
assertThat(pkiKeyStoreConfig.getKeyStorePassword()).isEqualTo("foo");
assertThat(pkiKeyStoreConfig.getCertificateAlias()).isEqualTo("anAlias");
assertThat(pkiKeyStoreConfig.getTrustStoreType()).isEqualTo("JKS");
assertThat(pkiKeyStoreConfig.getTrustStorePath()).isEqualTo(Path.of("/tmp/truststore"));
assertThat(pkiKeyStoreConfig.getTrustStorePassword()).isEqualTo("foo");
assertThat(pkiKeyStoreConfig.getCrlFilePath()).hasValue(Path.of("/tmp/crl"));
}
} }

@ -37,6 +37,8 @@ import org.hyperledger.besu.cli.options.unstable.MetricsCLIOptions;
import org.hyperledger.besu.cli.options.unstable.NetworkingOptions; import org.hyperledger.besu.cli.options.unstable.NetworkingOptions;
import org.hyperledger.besu.cli.options.unstable.SynchronizerOptions; import org.hyperledger.besu.cli.options.unstable.SynchronizerOptions;
import org.hyperledger.besu.cli.options.unstable.TransactionPoolOptions; import org.hyperledger.besu.cli.options.unstable.TransactionPoolOptions;
import org.hyperledger.besu.consensus.qbft.pki.PkiBlockCreationConfiguration;
import org.hyperledger.besu.consensus.qbft.pki.PkiBlockCreationConfigurationProvider;
import org.hyperledger.besu.controller.BesuController; import org.hyperledger.besu.controller.BesuController;
import org.hyperledger.besu.controller.BesuControllerBuilder; import org.hyperledger.besu.controller.BesuControllerBuilder;
import org.hyperledger.besu.controller.NoopPluginServiceFactory; import org.hyperledger.besu.controller.NoopPluginServiceFactory;
@ -62,6 +64,7 @@ import org.hyperledger.besu.ethereum.storage.StorageProvider;
import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration; import org.hyperledger.besu.ethereum.worldstate.DataStorageConfiguration;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration; import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration;
import org.hyperledger.besu.pki.config.PkiKeyStoreConfiguration;
import org.hyperledger.besu.plugin.services.BesuConfiguration; import org.hyperledger.besu.plugin.services.BesuConfiguration;
import org.hyperledger.besu.plugin.services.PicoCLIOptions; import org.hyperledger.besu.plugin.services.PicoCLIOptions;
import org.hyperledger.besu.plugin.services.StorageService; import org.hyperledger.besu.plugin.services.StorageService;
@ -156,6 +159,9 @@ public abstract class CommandTestAbstract {
@Mock @Mock
protected Logger mockLogger; protected Logger mockLogger;
@Mock protected PkiBlockCreationConfigurationProvider mockPkiBlockCreationConfigProvider;
@Mock protected PkiBlockCreationConfiguration mockPkiBlockCreationConfiguration;
@Captor protected ArgumentCaptor<Collection<Bytes>> bytesCollectionCollector; @Captor protected ArgumentCaptor<Collection<Bytes>> bytesCollectionCollector;
@Captor protected ArgumentCaptor<Path> pathArgumentCaptor; @Captor protected ArgumentCaptor<Path> pathArgumentCaptor;
@Captor protected ArgumentCaptor<String> stringArgumentCaptor; @Captor protected ArgumentCaptor<String> stringArgumentCaptor;
@ -170,6 +176,7 @@ public abstract class CommandTestAbstract {
@Captor protected ArgumentCaptor<StorageProvider> storageProviderArgumentCaptor; @Captor protected ArgumentCaptor<StorageProvider> storageProviderArgumentCaptor;
@Captor protected ArgumentCaptor<EthProtocolConfiguration> ethProtocolConfigurationArgumentCaptor; @Captor protected ArgumentCaptor<EthProtocolConfiguration> ethProtocolConfigurationArgumentCaptor;
@Captor protected ArgumentCaptor<DataStorageConfiguration> dataStorageConfigurationArgumentCaptor; @Captor protected ArgumentCaptor<DataStorageConfiguration> dataStorageConfigurationArgumentCaptor;
@Captor protected ArgumentCaptor<PkiKeyStoreConfiguration> pkiKeyStoreConfigurationArgumentCaptor;
@Captor @Captor
protected ArgumentCaptor<Optional<PermissioningConfiguration>> protected ArgumentCaptor<Optional<PermissioningConfiguration>>
@ -196,6 +203,8 @@ public abstract class CommandTestAbstract {
when(mockControllerBuilder.messagePermissioningProviders(any())) when(mockControllerBuilder.messagePermissioningProviders(any()))
.thenReturn(mockControllerBuilder); .thenReturn(mockControllerBuilder);
when(mockControllerBuilder.privacyParameters(any())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.privacyParameters(any())).thenReturn(mockControllerBuilder);
when(mockControllerBuilder.pkiBlockCreationConfiguration(any()))
.thenReturn(mockControllerBuilder);
when(mockControllerBuilder.clock(any())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.clock(any())).thenReturn(mockControllerBuilder);
when(mockControllerBuilder.isRevertReasonEnabled(false)).thenReturn(mockControllerBuilder); when(mockControllerBuilder.isRevertReasonEnabled(false)).thenReturn(mockControllerBuilder);
when(mockControllerBuilder.storageProvider(any())).thenReturn(mockControllerBuilder); when(mockControllerBuilder.storageProvider(any())).thenReturn(mockControllerBuilder);
@ -286,6 +295,11 @@ public abstract class CommandTestAbstract {
lenient() lenient()
.when(mockBesuPluginContext.getService(StorageService.class)) .when(mockBesuPluginContext.getService(StorageService.class))
.thenReturn(Optional.of(storageService)); .thenReturn(Optional.of(storageService));
lenient()
.doReturn(mockPkiBlockCreationConfiguration)
.when(mockPkiBlockCreationConfigProvider)
.load(pkiKeyStoreConfigurationArgumentCaptor.capture());
} }
// Display outputs for debug purpose // Display outputs for debug purpose
@ -335,7 +349,8 @@ public abstract class CommandTestAbstract {
mockBesuPluginContext, mockBesuPluginContext,
environment, environment,
storageService, storageService,
securityModuleService); securityModuleService,
mockPkiBlockCreationConfigProvider);
besuCommands.add(besuCommand); besuCommands.add(besuCommand);
besuCommand.setBesuConfiguration(commonPluginConfiguration); besuCommand.setBesuConfiguration(commonPluginConfiguration);
@ -369,7 +384,8 @@ public abstract class CommandTestAbstract {
final BesuPluginContextImpl besuPluginContext, final BesuPluginContextImpl besuPluginContext,
final Map<String, String> environment, final Map<String, String> environment,
final StorageServiceImpl storageService, final StorageServiceImpl storageService,
final SecurityModuleServiceImpl securityModuleService) { final SecurityModuleServiceImpl securityModuleService,
final PkiBlockCreationConfigurationProvider pkiBlockCreationConfigProvider) {
super( super(
mockLogger, mockLogger,
mockBlockImporter, mockBlockImporter,
@ -382,7 +398,8 @@ public abstract class CommandTestAbstract {
storageService, storageService,
securityModuleService, securityModuleService,
new PermissioningServiceImpl(), new PermissioningServiceImpl(),
new PrivacyPluginServiceImpl()); new PrivacyPluginServiceImpl(),
pkiBlockCreationConfigProvider);
this.mockNodeKey = mockNodeKey; this.mockNodeKey = mockNodeKey;
this.keyPair = keyPair; this.keyPair = keyPair;
} }

@ -0,0 +1,46 @@
/*
* Copyright ConsenSys AG.
*
* 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.consensus.qbft.pki;
import org.hyperledger.besu.pki.keystore.KeyStoreWrapper;
public class PkiBlockCreationConfiguration {
private final KeyStoreWrapper keyStore;
private final KeyStoreWrapper trustStore;
private final String certificateAlias;
public PkiBlockCreationConfiguration(
final KeyStoreWrapper keyStore,
final KeyStoreWrapper trustStore,
final String certificateAlias) {
this.keyStore = keyStore;
this.trustStore = trustStore;
this.certificateAlias = certificateAlias;
}
public KeyStoreWrapper getKeyStore() {
return keyStore;
}
public KeyStoreWrapper getTrustStore() {
return trustStore;
}
public String getCertificateAlias() {
return certificateAlias;
}
}

@ -0,0 +1,91 @@
/*
* Copyright ConsenSys AG.
*
* 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.consensus.qbft.pki;
import static com.google.common.base.Preconditions.checkNotNull;
import org.hyperledger.besu.pki.config.PkiKeyStoreConfiguration;
import org.hyperledger.besu.pki.keystore.KeyStoreWrapper;
import org.hyperledger.besu.pki.keystore.SoftwareKeyStoreWrapper;
import java.nio.file.Path;
import com.google.common.annotations.VisibleForTesting;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class PkiBlockCreationConfigurationProvider {
private static final Logger LOG = LogManager.getLogger();
private final KeyStoreWrapperProvider keyStoreWrapperProvider;
public PkiBlockCreationConfigurationProvider() {
this(SoftwareKeyStoreWrapper::new);
}
@VisibleForTesting
PkiBlockCreationConfigurationProvider(final KeyStoreWrapperProvider keyStoreWrapperProvider) {
this.keyStoreWrapperProvider = checkNotNull(keyStoreWrapperProvider);
}
public PkiBlockCreationConfiguration load(
final PkiKeyStoreConfiguration pkiKeyStoreConfiguration) {
KeyStoreWrapper keyStore;
try {
keyStore =
keyStoreWrapperProvider.apply(
pkiKeyStoreConfiguration.getKeyStoreType(),
pkiKeyStoreConfiguration.getKeyStorePath(),
pkiKeyStoreConfiguration.getKeyStorePassword(),
null);
LOG.info("Loaded PKI Block Creation KeyStore {}", pkiKeyStoreConfiguration.getKeyStorePath());
} catch (Exception e) {
final String message = "Error loading PKI Block Creation KeyStore";
LOG.error(message, e);
throw new RuntimeException(message, e);
}
KeyStoreWrapper trustStore;
try {
trustStore =
keyStoreWrapperProvider.apply(
pkiKeyStoreConfiguration.getTrustStoreType(),
pkiKeyStoreConfiguration.getTrustStorePath(),
pkiKeyStoreConfiguration.getTrustStorePassword(),
pkiKeyStoreConfiguration.getCrlFilePath().orElse(null));
LOG.info(
"Loaded PKI Block Creation TrustStore {}", pkiKeyStoreConfiguration.getTrustStorePath());
} catch (Exception e) {
final String message = "Error loading PKI Block Creation TrustStore";
LOG.error(message, e);
throw new RuntimeException(message, e);
}
return new PkiBlockCreationConfiguration(
keyStore, trustStore, pkiKeyStoreConfiguration.getCertificateAlias());
}
@FunctionalInterface
interface KeyStoreWrapperProvider {
KeyStoreWrapper apply(
final String keyStoreType,
final Path keyStorePath,
final String keyStorePassword,
final Path crl);
}
}

@ -19,22 +19,21 @@ import org.hyperledger.besu.consensus.common.EpochManager;
import org.hyperledger.besu.consensus.common.bft.BftBlockInterface; import org.hyperledger.besu.consensus.common.bft.BftBlockInterface;
import org.hyperledger.besu.consensus.common.bft.BftContext; import org.hyperledger.besu.consensus.common.bft.BftContext;
import org.hyperledger.besu.consensus.common.validator.ValidatorProvider; import org.hyperledger.besu.consensus.common.validator.ValidatorProvider;
import org.hyperledger.besu.pki.keystore.KeyStoreWrapper;
public class PkiQbftContext extends BftContext { public class PkiQbftContext extends BftContext {
private final KeyStoreWrapper keyStoreWrapper; private final PkiBlockCreationConfiguration pkiBlockCreationConfiguration;
public PkiQbftContext( public PkiQbftContext(
final ValidatorProvider validatorProvider, final ValidatorProvider validatorProvider,
final EpochManager epochManager, final EpochManager epochManager,
final BftBlockInterface blockInterface, final BftBlockInterface blockInterface,
final KeyStoreWrapper keyStoreWrapper) { final PkiBlockCreationConfiguration pkiBlockCreationConfiguration) {
super(validatorProvider, epochManager, blockInterface); super(validatorProvider, epochManager, blockInterface);
this.keyStoreWrapper = keyStoreWrapper; this.pkiBlockCreationConfiguration = pkiBlockCreationConfiguration;
} }
public KeyStoreWrapper getKeyStoreWrapper() { public PkiBlockCreationConfiguration getPkiBlockCreationConfiguration() {
return keyStoreWrapper; return pkiBlockCreationConfiguration;
} }
} }

@ -0,0 +1,71 @@
/*
* Copyright ConsenSys AG.
*
* 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.consensus.qbft.pki;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.consensus.qbft.pki.PkiBlockCreationConfigurationProvider.KeyStoreWrapperProvider;
import org.hyperledger.besu.pki.config.PkiKeyStoreConfiguration;
import org.hyperledger.besu.pki.keystore.KeyStoreWrapper;
import java.nio.file.Path;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class PkiBlockCreationConfigurationProviderTest {
@Mock KeyStoreWrapperProvider keyStoreWrapperProvider;
@Mock KeyStoreWrapper keyStoreWrapper;
@Mock KeyStoreWrapper trustStoreWrapper;
@Test
public void pkiBlockCreationConfigurationIsLoadedCorrectly() {
when(keyStoreWrapperProvider.apply(any(), eq(Path.of("/tmp/keystore")), eq("pwd"), isNull()))
.thenReturn(keyStoreWrapper);
when(keyStoreWrapperProvider.apply(
any(), eq(Path.of("/tmp/truststore")), eq("pwd"), eq(Path.of("/tmp/crl"))))
.thenReturn(trustStoreWrapper);
final PkiKeyStoreConfiguration pkiKeyStoreConfiguration =
new PkiKeyStoreConfiguration.Builder()
.withKeyStorePath(Path.of("/tmp/keystore"))
.withKeyStorePasswordSupplier(() -> "pwd")
.withTrustStorePath(Path.of("/tmp/truststore"))
.withTrustStorePasswordSupplier(() -> "pwd")
.withCertificateAlias("anAlias")
.withCrlFilePath(Path.of("/tmp/crl"))
.build();
final PkiBlockCreationConfigurationProvider pkiBlockCreationConfigProvider =
new PkiBlockCreationConfigurationProvider(keyStoreWrapperProvider);
final PkiBlockCreationConfiguration pkiBlockCreationConfiguration =
pkiBlockCreationConfigProvider.load(pkiKeyStoreConfiguration);
assertThat(pkiBlockCreationConfiguration).isNotNull();
assertThat(pkiBlockCreationConfiguration.getKeyStore()).isNotNull();
assertThat(pkiBlockCreationConfiguration.getTrustStore()).isNotNull();
assertThat(pkiBlockCreationConfiguration.getCertificateAlias()).isEqualTo("anAlias");
}
}

@ -12,14 +12,15 @@
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
package org.hyperledger.besu.pki; package org.hyperledger.besu.pki.config;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Optional;
import java.util.function.Supplier; import java.util.function.Supplier;
public class PkiConfiguration { public class PkiKeyStoreConfiguration {
public static String DEFAULT_KEYSTORE_TYPE = "PKCS12"; public static String DEFAULT_KEYSTORE_TYPE = "PKCS12";
public static String DEFAULT_CERTIFICATE_ALIAS = "validator"; public static String DEFAULT_CERTIFICATE_ALIAS = "validator";
@ -31,15 +32,17 @@ public class PkiConfiguration {
private final String trustStoreType; private final String trustStoreType;
private final Path trustStorePath; private final Path trustStorePath;
private final Supplier<String> trustStorePasswordSupplier; private final Supplier<String> trustStorePasswordSupplier;
private final Optional<Path> crlFilePath;
public PkiConfiguration( public PkiKeyStoreConfiguration(
final String keyStoreType, final String keyStoreType,
final Path keyStorePath, final Path keyStorePath,
final Supplier<String> keyStorePasswordSupplier, final Supplier<String> keyStorePasswordSupplier,
final String certificateAlias, final String certificateAlias,
final String trustStoreType, final String trustStoreType,
final Path trustStorePath, final Path trustStorePath,
final Supplier<String> trustStorePasswordSupplier) { final Supplier<String> trustStorePasswordSupplier,
final Optional<Path> crlFilePath) {
this.keyStoreType = keyStoreType; this.keyStoreType = keyStoreType;
this.keyStorePath = keyStorePath; this.keyStorePath = keyStorePath;
this.keyStorePasswordSupplier = keyStorePasswordSupplier; this.keyStorePasswordSupplier = keyStorePasswordSupplier;
@ -47,6 +50,7 @@ public class PkiConfiguration {
this.trustStoreType = trustStoreType; this.trustStoreType = trustStoreType;
this.trustStorePath = trustStorePath; this.trustStorePath = trustStorePath;
this.trustStorePasswordSupplier = trustStorePasswordSupplier; this.trustStorePasswordSupplier = trustStorePasswordSupplier;
this.crlFilePath = crlFilePath;
} }
public String getKeyStoreType() { public String getKeyStoreType() {
@ -77,6 +81,10 @@ public class PkiConfiguration {
return trustStorePasswordSupplier.get(); return trustStorePasswordSupplier.get();
} }
public Optional<Path> getCrlFilePath() {
return crlFilePath;
}
public static final class Builder { public static final class Builder {
private String keyStoreType = DEFAULT_KEYSTORE_TYPE; private String keyStoreType = DEFAULT_KEYSTORE_TYPE;
@ -86,6 +94,7 @@ public class PkiConfiguration {
private String trustStoreType = DEFAULT_KEYSTORE_TYPE; private String trustStoreType = DEFAULT_KEYSTORE_TYPE;
private Path trustStorePath; private Path trustStorePath;
private Supplier<String> trustStorePasswordSupplier; private Supplier<String> trustStorePasswordSupplier;
private Path crlFilePath;
public Builder() {} public Builder() {}
@ -125,17 +134,23 @@ public class PkiConfiguration {
return this; return this;
} }
public PkiConfiguration build() { public Builder withCrlFilePath(final Path filePath) {
this.crlFilePath = filePath;
return this;
}
public PkiKeyStoreConfiguration build() {
requireNonNull(keyStoreType, "Key Store Type must not be null"); requireNonNull(keyStoreType, "Key Store Type must not be null");
requireNonNull(keyStorePasswordSupplier, "Key Store password supplier must not be null"); requireNonNull(keyStorePasswordSupplier, "Key Store password supplier must not be null");
return new PkiConfiguration( return new PkiKeyStoreConfiguration(
keyStoreType, keyStoreType,
keyStorePath, keyStorePath,
keyStorePasswordSupplier, keyStorePasswordSupplier,
certificateAlias, certificateAlias,
trustStoreType, trustStoreType,
trustStorePath, trustStorePath,
trustStorePasswordSupplier); trustStorePasswordSupplier,
Optional.ofNullable(crlFilePath));
} }
} }
} }
Loading…
Cancel
Save