Skip to content

Commit

Permalink
Unit test for DefaultArtifactSignerProvider loading proxy keys
Browse files Browse the repository at this point in the history
  • Loading branch information
usmansaleem committed Oct 20, 2024
1 parent 82ce60b commit cd20c6c
Showing 1 changed file with 158 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,45 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import tech.pegasys.teku.bls.BLSKeyPair;
import tech.pegasys.web3signer.KeystoreUtil;
import tech.pegasys.web3signer.signing.ArtifactSigner;
import tech.pegasys.web3signer.signing.ArtifactSignerProvider;

import tech.pegasys.web3signer.signing.KeyType;
import tech.pegasys.web3signer.signing.secp256k1.EthPublicKeyUtils;

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.SecureRandom;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.IntStream;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.web3j.crypto.ECKeyPair;
import org.web3j.crypto.Keys;
import org.web3j.crypto.WalletUtils;
import org.web3j.crypto.exception.CipherException;

class DefaultArtifactSignerProviderTest {

private static final String PUBLIC_KEY1 =
"989d34725a2bfc3f15105f3f5fc8741f436c25ee1ee4f948e425d6bcb8c56bce6e06c269635b7e985a7ffa639e2409bf";
"0x989d34725a2bfc3f15105f3f5fc8741f436c25ee1ee4f948e425d6bcb8c56bce6e06c269635b7e985a7ffa639e2409bf";
private static final String PUBLIC_KEY2 =
"a99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c";
"0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c";

private ArtifactSignerProvider signerProvider;

@TempDir private Path commitBoostKeystoresPath;

@TempDir private Path commitBoostPasswordDir;

@AfterEach
void cleanup() {
if (signerProvider != null) {
Expand Down Expand Up @@ -85,4 +106,138 @@ void signerProviderCanMapInTwoSigners() {
assertThat(signerProvider.availableIdentifiers()).hasSize(2);
assertThat(signerProvider.availableIdentifiers()).containsOnly(PUBLIC_KEY1, PUBLIC_KEY2);
}

@Test
void proxySignersAreLoadedCorrectly() throws IOException {
final SecureRandom secureRandom = new SecureRandom();

// create random proxy signers
final KeystoresParameters commitBoostParameters =
new TestCommitBoostParameters(commitBoostKeystoresPath, commitBoostPasswordDir);

// create random BLS key pairs as proxy keys for public key1 and public key2
final List<BLSKeyPair> key1ProxyKeyPairs = randomBLSV4Keystores(secureRandom, PUBLIC_KEY1);
final List<BLSKeyPair> key2ProxyKeyPairs = randomBLSV4Keystores(secureRandom, PUBLIC_KEY2);

// create random secp key pairs as proxy keys for public key1 and public key2
final List<ECKeyPair> key1SecpKeyPairs = randomSecpV3Keystores(secureRandom, PUBLIC_KEY1);
final List<ECKeyPair> key2SecpKeyPairs = randomSecpV3Keystores(secureRandom, PUBLIC_KEY2);

// set up mock signers
final ArtifactSigner mockSigner1 = mock(ArtifactSigner.class);
when(mockSigner1.getIdentifier()).thenReturn(PUBLIC_KEY1);
final ArtifactSigner mockSigner2 = mock(ArtifactSigner.class);
when(mockSigner2.getIdentifier()).thenReturn(PUBLIC_KEY2);

signerProvider =
new DefaultArtifactSignerProvider(
() -> List.of(mockSigner1, mockSigner2), Optional.of(commitBoostParameters));

// methods under test
assertThatCode(() -> signerProvider.load().get()).doesNotThrowAnyException();

// assert that the proxy keys are loaded correctly
final Map<KeyType, List<String>> key1ProxyPublicKeys =
signerProvider.getProxyIdentifiers(PUBLIC_KEY1);

assertThat(key1ProxyPublicKeys.get(KeyType.BLS))
.containsExactlyInAnyOrder(getPublicKeysArray(key1ProxyKeyPairs));
assertThat(key1ProxyPublicKeys.get(KeyType.SECP256K1))
.containsExactlyInAnyOrder(getSecpPublicKeysArray(key1SecpKeyPairs));

final Map<KeyType, List<String>> key2ProxyPublicKeys =
signerProvider.getProxyIdentifiers(PUBLIC_KEY2);

assertThat(key2ProxyPublicKeys.get(KeyType.BLS))
.containsExactlyInAnyOrder(getPublicKeysArray(key2ProxyKeyPairs));
assertThat(key2ProxyPublicKeys.get(KeyType.SECP256K1))
.containsExactlyInAnyOrder(getSecpPublicKeysArray(key2SecpKeyPairs));
}

private List<BLSKeyPair> randomBLSV4Keystores(SecureRandom secureRandom, String identifier)
throws IOException {
final Path v4Dir =
Files.createDirectories(commitBoostKeystoresPath.resolve(identifier).resolve("v4"));

return IntStream.range(0, 4)
.mapToObj(
i -> {
final BLSKeyPair blsKeyPair = BLSKeyPair.random(secureRandom);
KeystoreUtil.createKeystoreFile(blsKeyPair, v4Dir, "password");
return blsKeyPair;
})
.toList();
}

private List<ECKeyPair> randomSecpV3Keystores(
final SecureRandom secureRandom, final String identifier) throws IOException {
final Path v3Dir =
Files.createDirectories(commitBoostKeystoresPath.resolve(identifier).resolve("v3"));
return IntStream.range(0, 4)
.mapToObj(
i -> {
try {
final ECKeyPair ecKeyPair = Keys.createEcKeyPair(secureRandom);
WalletUtils.generateWalletFile("password", ecKeyPair, v3Dir.toFile(), false);
return ecKeyPair;
} catch (GeneralSecurityException | CipherException | IOException e) {
throw new RuntimeException(e);
}
})
.toList();
}

private static String[] getPublicKeysArray(final List<BLSKeyPair> blsKeyPairs) {
return blsKeyPairs.stream()
.map(keyPair -> keyPair.getPublicKey().toString())
.toList()
.toArray(String[]::new);
}

private static String[] getSecpPublicKeysArray(final List<ECKeyPair> ecKeyPairs) {
return ecKeyPairs.stream()
.map(
keyPair ->
EthPublicKeyUtils.toHexString(
EthPublicKeyUtils.createPublicKey(keyPair.getPublicKey())))
.toList()
.toArray(String[]::new);
}

private static class TestCommitBoostParameters implements KeystoresParameters {
private final Path keystorePath;
private final Path passwordFile;

public TestCommitBoostParameters(final Path keystorePath, final Path passwordDir) {
this.keystorePath = keystorePath;
// create password file in passwordDir
this.passwordFile = passwordDir.resolve("password.txt");
// write text to password file
try {
Files.writeString(passwordFile, "password");
} catch (final IOException e) {
throw new UncheckedIOException(e);
}
}

@Override
public Path getKeystoresPath() {
return keystorePath;
}

@Override
public Path getKeystoresPasswordsPath() {
return null;
}

@Override
public Path getKeystoresPasswordFile() {
return passwordFile;
}

@Override
public boolean isEnabled() {
return true;
}
}
}

0 comments on commit cd20c6c

Please sign in to comment.