Skip to content

Commit

Permalink
Secp wallet bulkload acceptance test
Browse files Browse the repository at this point in the history
  • Loading branch information
usmansaleem committed Aug 30, 2023
1 parent a55dd5a commit 18000d7
Show file tree
Hide file tree
Showing 5 changed files with 243 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ public class SignerConfiguration {
private Optional<ClientTlsOptions> downstreamTlsOptions;
private final Duration startupTimeout;
private final ChainIdProvider chainIdProvider;
private final Optional<KeystoresParameters> walletBulkloadParameters;

public SignerConfiguration(
final String hostname,
Expand Down Expand Up @@ -120,7 +121,8 @@ public SignerConfiguration(
final int downstreamHttpPort,
final Optional<ClientTlsOptions> downstreamTlsOptions,
final ChainIdProvider chainIdProvider,
final Optional<String> trustedSetup) {
final Optional<String> trustedSetup,
final Optional<KeystoresParameters> walletBulkloadParameters) {
this.hostname = hostname;
this.logLevel = logLevel;
this.httpRpcPort = httpRpcPort;
Expand Down Expand Up @@ -164,6 +166,7 @@ public SignerConfiguration(
this.downstreamTlsOptions = downstreamTlsOptions;
this.chainIdProvider = chainIdProvider;
this.trustedSetup = trustedSetup;
this.walletBulkloadParameters = walletBulkloadParameters;
}

public String hostname() {
Expand Down Expand Up @@ -345,4 +348,8 @@ public ChainIdProvider getChainIdProvider() {
public Optional<String> getTrustedSetup() {
return trustedSetup;
}

public Optional<KeystoresParameters> getWalletBulkloadParameters() {
return walletBulkloadParameters;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ public class SignerConfigurationBuilder {
private ChainIdProvider chainIdProvider = new ConfigurationChainId(DEFAULT_CHAIN_ID);
private String trustedSetup;

private KeystoresParameters walletBulkloadParameters;

public SignerConfigurationBuilder withLogLevel(final Level logLevel) {
this.logLevel = logLevel;
return this;
Expand Down Expand Up @@ -309,6 +311,12 @@ public SignerConfigurationBuilder withTrustedSetup(final String trustedSetup) {
return this;
}

public SignerConfigurationBuilder withWalletBulkloadParameters(
final KeystoresParameters walletBulkloadParameters) {
this.walletBulkloadParameters = walletBulkloadParameters;
return this;
}

public SignerConfiguration build() {
if (mode == null) {
throw new IllegalArgumentException("Mode cannot be null");
Expand Down Expand Up @@ -356,6 +364,7 @@ public SignerConfiguration build() {
downstreamHttpPort,
Optional.ofNullable(downstreamTlsOptions),
chainIdProvider,
Optional.ofNullable(trustedSetup));
Optional.ofNullable(trustedSetup),
Optional.ofNullable(walletBulkloadParameters));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import static tech.pegasys.web3signer.commandline.PicoCliAwsSecretsManagerParameters.AWS_SECRETS_TAG_NAMES_FILTER_OPTION;
import static tech.pegasys.web3signer.commandline.PicoCliAwsSecretsManagerParameters.AWS_SECRETS_TAG_VALUES_FILTER_OPTION;

import tech.pegasys.web3signer.commandline.config.PicoV3WalletBulkloadParameters;
import tech.pegasys.web3signer.core.config.ClientAuthConstraints;
import tech.pegasys.web3signer.core.config.TlsOptions;
import tech.pegasys.web3signer.core.config.client.ClientTlsOptions;
Expand All @@ -38,6 +39,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;

import com.google.common.collect.Lists;

Expand Down Expand Up @@ -128,11 +130,29 @@ public List<String> createCmdLineParams() {
if (signerConfig.getAzureKeyVaultParameters().isPresent()) {
createAzureArgs(params);
}

signerConfig.getWalletBulkloadParameters().ifPresent(setWalletBulkloadParameters(params));
}

return params;
}

private static Consumer<KeystoresParameters> setWalletBulkloadParameters(
final List<String> params) {
return keystoresParameters -> {
params.add(PicoV3WalletBulkloadParameters.WALLETS_PATH);
params.add(keystoresParameters.getKeystoresPath().toAbsolutePath().toString());
if (keystoresParameters.getKeystoresPasswordsPath() != null) {
params.add(PicoV3WalletBulkloadParameters.WALLETS_PASSWORDS_PATH);
params.add(keystoresParameters.getKeystoresPasswordsPath().toAbsolutePath().toString());
}
if (keystoresParameters.getKeystoresPasswordFile() != null) {
params.add(PicoV3WalletBulkloadParameters.WALLETS_PASSWORD_FILE);
params.add(keystoresParameters.getKeystoresPasswordFile().toAbsolutePath().toString());
}
};
}

@Override
public Optional<String> slashingProtectionDbUrl() {
return slashingProtectionDbUrl;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Copyright 2023 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.
*/
package tech.pegasys.web3signer.dsl.utils;

import java.util.List;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Splitter;

public class HealthCheckResultUtil {

private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

/*
Healthcheck Json looks like:
{
"status": "UP",
"checks": [
{
"id": "default-check",
"status": "UP"
},
{
"id": "keys-check",
"status": "UP",
"checks": [
{
"id": "wallet-bulk-loading",
"status": "UP",
"data": {
"keys-loaded": 4,
"error-count": 0
}
},
{
"id": "config-files-loading",
"status": "UP",
"data": {
"keys-loaded": 4,
"error-count": 0
}
}
]
}
],
"outcome": "UP"
}
*/
public static int getHealthCheckKeysCheckData(
String healthCheckJsonBody, final String healthCheckKeyName, final String dataKey)
throws JsonProcessingException {
final List<String> keyCheckIds = Splitter.on('/').splitToList(healthCheckKeyName);
int dataKeyValue = -1;
final JsonNode jsonNode = OBJECT_MAPPER.readTree(healthCheckJsonBody);
for (JsonNode checksNode : jsonNode.get("checks")) {
// id = keys-check
if (checksNode.get("id").asText().equals(keyCheckIds.get(0))) {
for (JsonNode keyChecksNode : checksNode.get("checks")) {
// id = See HealthCheckNames.java
if (keyChecksNode.get("id").asText().equals(keyCheckIds.get(1))) {
dataKeyValue = keyChecksNode.get("data").get(dataKey).asInt();
break;
}
}
}
}

return dataKeyValue;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
* Copyright 2023 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.
*/
package tech.pegasys.web3signer.tests.bulkloading;

import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.equalTo;
import static org.web3j.crypto.WalletUtils.generateWalletFile;
import static tech.pegasys.web3signer.core.config.HealthCheckNames.KEYS_CHECK_V3_WALLET_BULK_LOADING;
import static tech.pegasys.web3signer.dsl.utils.HealthCheckResultUtil.getHealthCheckKeysCheckData;

import tech.pegasys.web3signer.dsl.signer.SignerConfigurationBuilder;
import tech.pegasys.web3signer.dsl.utils.DefaultKeystoresParameters;
import tech.pegasys.web3signer.signing.KeyType;
import tech.pegasys.web3signer.signing.config.KeystoresParameters;
import tech.pegasys.web3signer.signing.secp256k1.EthPublicKeyUtils;
import tech.pegasys.web3signer.signing.util.IdentifierUtils;
import tech.pegasys.web3signer.tests.AcceptanceTestBase;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.GeneralSecurityException;
import java.security.interfaces.ECPublicKey;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

import io.restassured.http.ContentType;
import io.restassured.response.Response;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.web3j.crypto.CipherException;
import org.web3j.crypto.ECKeyPair;
import org.web3j.crypto.Keys;

public class SecpWalletBulkloadAcceptanceTest extends AcceptanceTestBase {
@TempDir private static Path walletsDir;
@TempDir private static Path walletsPasswordDir;

private static List<String> publicKeys;

@BeforeAll
static void initV3Wallets() throws IOException, GeneralSecurityException, CipherException {
publicKeys = new ArrayList<>();
for (int i = 0; i < 4; i++) {
final ECKeyPair ecKeyPair = Keys.createEcKeyPair();
final ECPublicKey ecPublicKey = EthPublicKeyUtils.createPublicKey(ecKeyPair.getPublicKey());
final String publicKeyHex =
IdentifierUtils.normaliseIdentifier(EthPublicKeyUtils.toHexString(ecPublicKey));
publicKeys.add(publicKeyHex);

// generate v3 wallet
final boolean useFullScrypt = false;
final String fileName =
generateWalletFile("test123", ecKeyPair, walletsDir.toFile(), useFullScrypt);

// write corresponding password
final Path passwordFile =
walletsPasswordDir.resolve(fileName.substring(0, fileName.lastIndexOf(".json")) + ".txt");
Files.writeString(passwordFile, "test123");
}
}

@ParameterizedTest
@MethodSource("buildWalletParameters")
void walletFilesAreBulkloaded(final KeystoresParameters walletBulkloadParameters)
throws Exception {
final SignerConfigurationBuilder configBuilder =
new SignerConfigurationBuilder()
.withMode("eth1")
.withWalletBulkloadParameters(walletBulkloadParameters);

startSigner(configBuilder.build());

final Response response = signer.callApiPublicKeys(KeyType.SECP256K1);
response
.then()
.statusCode(200)
.contentType(ContentType.JSON)
.body("", containsInAnyOrder(publicKeys.toArray(String[]::new)));

final Response healthcheckResponse = signer.healthcheck();
healthcheckResponse
.then()
.statusCode(200)
.contentType(ContentType.JSON)
.body("status", equalTo("UP"));

final String jsonBody = healthcheckResponse.body().asString();
final int keysLoaded =
getHealthCheckKeysCheckData(jsonBody, KEYS_CHECK_V3_WALLET_BULK_LOADING, "keys-loaded");
assertThat(keysLoaded).isEqualTo(publicKeys.size());
}

private static Stream<KeystoresParameters> buildWalletParameters() {
// build wallet bulkloading parameters, one with password dir, other with password file
final KeystoresParameters withPasswordDir =
new DefaultKeystoresParameters(walletsDir, walletsPasswordDir, null);

try (final Stream<Path> passwordFiles = Files.list(walletsPasswordDir)) {
final Path passwordFile = passwordFiles.findAny().orElseThrow();
final KeystoresParameters withPasswordFile =
new DefaultKeystoresParameters(walletsDir, null, passwordFile);
return Stream.of(withPasswordDir, withPasswordFile);
} catch (final IOException e) {
throw new UncheckedIOException(e);
}
}
}

0 comments on commit 18000d7

Please sign in to comment.