Make test generated keys deterministic when generating block data. (#1657)

BlockDataGenerator (a testing util) uses the default key generation code, which
is set to a production grade SecureRandom seed by default. However this creates 
non-deterministic results with the block data.

This changes the generator for test data to use a deterministic set of key 
pairs.
Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>
pull/2/head
Danno Ferrin 5 years ago committed by GitHub
parent c88dee9fe1
commit 0b6236a641
  1. 6
      crypto/src/main/java/tech/pegasys/pantheon/crypto/SECP256K1.java
  2. 59
      ethereum/core/src/test-support/java/tech/pegasys/pantheon/ethereum/core/BlockDataGenerator.java

@ -69,9 +69,9 @@ import org.bouncycastle.math.ec.custom.sec.SecP256K1Curve;
*/
public class SECP256K1 {
private static final String ALGORITHM = "ECDSA";
private static final String CURVE_NAME = "secp256k1";
private static final String PROVIDER = "BC";
public static final String ALGORITHM = "ECDSA";
public static final String CURVE_NAME = "secp256k1";
public static final String PROVIDER = "BC";
public static final ECDomainParameters CURVE;
public static final BigInteger HALF_CURVE_ORDER;

@ -17,6 +17,7 @@ import static java.util.stream.Collectors.toSet;
import static tech.pegasys.pantheon.ethereum.core.InMemoryStorageProvider.createInMemoryWorldStateArchive;
import tech.pegasys.pantheon.crypto.SECP256K1;
import tech.pegasys.pantheon.crypto.SecureRandomProvider;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetBlockHeaderFunctions;
import tech.pegasys.pantheon.ethereum.worldstate.WorldStateArchive;
import tech.pegasys.pantheon.util.bytes.Bytes32;
@ -24,6 +25,11 @@ import tech.pegasys.pantheon.util.bytes.BytesValue;
import tech.pegasys.pantheon.util.uint.UInt256;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.Security;
import java.security.spec.ECGenParameterSpec;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
@ -36,17 +42,46 @@ import java.util.Random;
import java.util.Set;
import java.util.stream.IntStream;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class BlockDataGenerator {
static {
Security.addProvider(new BouncyCastleProvider());
}
private final Random random;
private final KeyPairGenerator keyPairGenerator;
public BlockDataGenerator(final int seed) {
this.random = new Random(seed);
keyPairGenerator = createKeyPairGenerator(seed);
}
public BlockDataGenerator() {
this(1);
}
private KeyPairGenerator createKeyPairGenerator(final long seed) {
final KeyPairGenerator keyPairGenerator;
try {
keyPairGenerator = KeyPairGenerator.getInstance(SECP256K1.ALGORITHM, SECP256K1.PROVIDER);
} catch (final Exception e) {
throw new RuntimeException(e);
}
final ECGenParameterSpec ecGenParameterSpec = new ECGenParameterSpec(SECP256K1.CURVE_NAME);
try {
final SecureRandom secureRandom = SecureRandomProvider.createSecureRandom();
secureRandom.setSeed(seed);
keyPairGenerator.initialize(ecGenParameterSpec, secureRandom);
} catch (final InvalidAlgorithmParameterException e) {
throw new RuntimeException(e);
}
return keyPairGenerator;
}
/**
* Generates a sequence of blocks with some accounts and account storage pre-populated with random
* data.
@ -254,7 +289,7 @@ public class BlockDataGenerator {
.value(Wei.wrap(bytes32()))
.payload(payload)
.chainId(BigInteger.ONE)
.signAndBuild(SECP256K1.KeyPair.generate());
.signAndBuild(generateKeyPair());
}
public Transaction transaction() {
@ -266,7 +301,7 @@ public class BlockDataGenerator {
.value(Wei.wrap(bytes32()))
.payload(bytes32())
.chainId(BigInteger.ONE)
.signAndBuild(SECP256K1.KeyPair.generate());
.signAndBuild(generateKeyPair());
}
public Set<Transaction> transactions(final int n) {
@ -276,7 +311,7 @@ public class BlockDataGenerator {
Wei value = Wei.wrap(bytes32());
int chainId = 1;
Bytes32 payload = bytes32();
SECP256K1.Signature signature = SECP256K1.sign(payload, SECP256K1.KeyPair.generate());
final SECP256K1.Signature signature = SECP256K1.sign(payload, generateKeyPair());
final Set<Transaction> txs =
IntStream.range(0, n)
@ -402,6 +437,24 @@ public class BlockDataGenerator {
return bytes;
}
private SECP256K1.KeyPair generateKeyPair() {
final java.security.KeyPair rawKeyPair = keyPairGenerator.generateKeyPair();
final BCECPrivateKey privateKey = (BCECPrivateKey) rawKeyPair.getPrivate();
final BCECPublicKey publicKey = (BCECPublicKey) rawKeyPair.getPublic();
final BigInteger privateKeyValue = privateKey.getD();
// Ethereum does not use encoded public keys like bitcoin - see
// https://en.bitcoin.it/wiki/Elliptic_Curve_Digital_Signature_Algorithm for details
// Additionally, as the first bit is a constant prefix (0x04) we ignore this value
final byte[] publicKeyBytes = publicKey.getQ().getEncoded(false);
final BigInteger publicKeyValue =
new BigInteger(1, Arrays.copyOfRange(publicKeyBytes, 1, publicKeyBytes.length));
return new SECP256K1.KeyPair(
SECP256K1.PrivateKey.create(privateKeyValue), SECP256K1.PublicKey.create(publicKeyValue));
}
public static class BlockOptions {
private OptionalLong blockNumber = OptionalLong.empty();
private Optional<Hash> parentHash = Optional.empty();

Loading…
Cancel
Save