diff --git a/consensus/clique/src/main/java/tech/pegasys/pantheon/consensus/clique/CliqueExtraData.java b/consensus/clique/src/main/java/tech/pegasys/pantheon/consensus/clique/CliqueExtraData.java
index bd18a99fdb..dcd59b6af9 100644
--- a/consensus/clique/src/main/java/tech/pegasys/pantheon/consensus/clique/CliqueExtraData.java
+++ b/consensus/clique/src/main/java/tech/pegasys/pantheon/consensus/clique/CliqueExtraData.java
@@ -15,8 +15,11 @@ package tech.pegasys.pantheon.consensus.clique;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
+import tech.pegasys.pantheon.crypto.SECP256K1.PrivateKey;
+import tech.pegasys.pantheon.crypto.SECP256K1.PublicKey;
import tech.pegasys.pantheon.crypto.SECP256K1.Signature;
import tech.pegasys.pantheon.ethereum.core.Address;
+import tech.pegasys.pantheon.ethereum.core.Util;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import tech.pegasys.pantheon.util.bytes.BytesValues;
@@ -106,4 +109,21 @@ public class CliqueExtraData {
public List
getValidators() {
return validators;
}
+
+ public static String createGenesisExtraDataString(final List privKeys) {
+ final List validators = convertPrivKeysToAddresses(privKeys);
+ final CliqueExtraData cliqueExtraData =
+ new CliqueExtraData(BytesValue.wrap(new byte[32]), null, validators);
+ final BytesValue output = cliqueExtraData.encode();
+ return output.toString();
+ }
+
+ private static List convertPrivKeysToAddresses(final List privKeys) {
+ final List validators = Lists.newArrayList();
+ for (PrivateKey privKey : privKeys) {
+ final PublicKey pubKey = PublicKey.create(privKey);
+ validators.add(Util.publicKeyToAddress(pubKey));
+ }
+ return validators;
+ }
}
diff --git a/consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/CliqueExtraDataTest.java b/consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/CliqueExtraDataTest.java
index 904b327b3a..8d17892b8a 100644
--- a/consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/CliqueExtraDataTest.java
+++ b/consensus/clique/src/test/java/tech/pegasys/pantheon/consensus/clique/CliqueExtraDataTest.java
@@ -15,15 +15,20 @@ package tech.pegasys.pantheon.consensus.clique;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair;
+import tech.pegasys.pantheon.crypto.SECP256K1.PrivateKey;
import tech.pegasys.pantheon.crypto.SECP256K1.Signature;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.AddressHelpers;
+import tech.pegasys.pantheon.ethereum.core.Util;
import tech.pegasys.pantheon.util.bytes.BytesValue;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.List;
+import java.util.stream.Collectors;
+import com.google.common.collect.Lists;
import org.bouncycastle.util.encoders.Hex;
import org.junit.Test;
@@ -88,4 +93,28 @@ public class CliqueExtraDataTest {
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("BytesValue is of invalid size - i.e. contains unused bytes.");
}
+
+ @Test
+ public void privKeysToExtraDataString() {
+ List nodeKeys = Lists.newArrayList();
+ for (int i = 0; i < 4; i++) {
+ nodeKeys.add(KeyPair.generate());
+ }
+
+ final List privKeys =
+ nodeKeys.stream().map(k -> k.getPrivateKey()).collect(Collectors.toList());
+
+ final String hexOutput = CliqueExtraData.createGenesisExtraDataString(privKeys);
+
+ final CliqueExtraData extraData = CliqueExtraData.decode(BytesValue.fromHexString(hexOutput));
+
+ final List expectedAddresses =
+ nodeKeys
+ .stream()
+ .map(k -> Util.publicKeyToAddress(k.getPublicKey()))
+ .collect(Collectors.toList());
+
+ assertThat(extraData.getValidators())
+ .containsExactly(expectedAddresses.toArray(new Address[expectedAddresses.size()]));
+ }
}