Support qbft encoding of extra data in the operator generate-blockchain subcommand (#2276)

Signed-off-by: Jason Frame <jasonwframe@gmail.com>
pull/2290/head
Jason Frame 4 years ago committed by GitHub
parent 4be132a7d1
commit 755979a771
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      besu/src/main/java/org/hyperledger/besu/cli/subcommands/operator/GenerateBlockchainConfig.java
  2. 70
      besu/src/test/java/org/hyperledger/besu/cli/operator/OperatorSubCommandTest.java
  3. 41
      besu/src/test/resources/operator/config_import_keys_qbft.json
  4. 32
      besu/src/test/resources/operator/config_no_config_section.json

@ -23,6 +23,7 @@ import org.hyperledger.besu.config.GenesisConfigOptions;
import org.hyperledger.besu.config.JsonGenesisConfigOptions; import org.hyperledger.besu.config.JsonGenesisConfigOptions;
import org.hyperledger.besu.config.JsonUtil; import org.hyperledger.besu.config.JsonUtil;
import org.hyperledger.besu.consensus.ibft.IbftExtraDataCodec; import org.hyperledger.besu.consensus.ibft.IbftExtraDataCodec;
import org.hyperledger.besu.consensus.qbft.QbftExtraDataCodec;
import org.hyperledger.besu.crypto.KeyPair; import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SECPPrivateKey; import org.hyperledger.besu.crypto.SECPPrivateKey;
import org.hyperledger.besu.crypto.SECPPublicKey; import org.hyperledger.besu.crypto.SECPPublicKey;
@ -59,7 +60,7 @@ import picocli.CommandLine.ParentCommand;
@Command( @Command(
name = "generate-blockchain-config", name = "generate-blockchain-config",
description = "Generates node keypairs and genesis file with RLP encoded IBFT 2.0 extra data.", description = "Generates node keypairs and genesis file with RLP encoded extra data.",
mixinStandardHelpOptions = true) mixinStandardHelpOptions = true)
class GenerateBlockchainConfig implements Runnable { class GenerateBlockchainConfig implements Runnable {
private static final Logger LOG = LogManager.getLogger(); private static final Logger LOG = LogManager.getLogger();
@ -234,7 +235,11 @@ class GenerateBlockchainConfig implements Runnable {
/** Computes RLP encoded exta data from pre filled list of addresses. */ /** Computes RLP encoded exta data from pre filled list of addresses. */
private void processExtraData() { private void processExtraData() {
final ObjectNode configNode = JsonUtil.getObjectNode(genesisConfig, "config").orElse(null); final ObjectNode configNode =
JsonUtil.getObjectNode(genesisConfig, "config")
.orElseThrow(
() -> new IllegalArgumentException("Missing config section in config file"));
final JsonGenesisConfigOptions genesisConfigOptions = final JsonGenesisConfigOptions genesisConfigOptions =
JsonGenesisConfigOptions.fromJsonObject(configNode); JsonGenesisConfigOptions.fromJsonObject(configNode);
if (genesisConfigOptions.isIbft2()) { if (genesisConfigOptions.isIbft2()) {
@ -242,6 +247,11 @@ class GenerateBlockchainConfig implements Runnable {
final String extraData = final String extraData =
IbftExtraDataCodec.encodeFromAddresses(addressesForGenesisExtraData).toString(); IbftExtraDataCodec.encodeFromAddresses(addressesForGenesisExtraData).toString();
genesisConfig.put("extraData", extraData); genesisConfig.put("extraData", extraData);
} else if (genesisConfigOptions.isQbft()) {
LOG.info("Generating QBFT extra data.");
final String extraData =
QbftExtraDataCodec.encodeFromAddresses(addressesForGenesisExtraData).toString();
genesisConfig.put("extraData", extraData);
} }
} }

@ -72,7 +72,7 @@ public class OperatorSubCommandTest extends CommandTestAbstract {
+ System.lineSeparator() + System.lineSeparator()
+ " generate-blockchain-config Generates node keypairs and genesis file with RLP" + " generate-blockchain-config Generates node keypairs and genesis file with RLP"
+ System.lineSeparator() + System.lineSeparator()
+ " encoded IBFT 2.0 extra data." + " encoded extra data."
+ System.lineSeparator() + System.lineSeparator()
+ " generate-log-bloom-cache Generate cached values of block log bloom filters."; + " generate-log-bloom-cache Generate cached values of block log bloom filters.";
@ -252,6 +252,22 @@ public class OperatorSubCommandTest extends CommandTestAbstract {
"0xb295c4242fb40c6e8ac7b831c916846050f191adc560b8098ba6ad513079571ec1be6e5e1a715857a13a91963097962e048c36c5863014b59e8f67ed3f667680 is not a valid public key for elliptic curve secp256r1"); "0xb295c4242fb40c6e8ac7b831c916846050f191adc560b8098ba6ad513079571ec1be6e5e1a715857a13a91963097962e048c36c5863014b59e8f67ed3f667680 is not a valid public key for elliptic curve secp256r1");
} }
@Test
public void shouldFailIfNoConfigSection() {
assertThatThrownBy(
() ->
runCmdAndCheckOutput(
cmd(),
"/operator/config_no_config_section.json",
tmpOutputDirectoryPath,
"genesis.json",
true,
asList("key.pub", "key.priv"),
Optional.of(new SECP256K1())))
.isInstanceOf(CommandLine.ExecutionException.class)
.hasMessageEndingWith("Missing config section in config file");
}
@Test @Test
public void shouldImportSecp256R1Keys() throws IOException { public void shouldImportSecp256R1Keys() throws IOException {
runCmdAndCheckOutput( runCmdAndCheckOutput(
@ -263,6 +279,34 @@ public class OperatorSubCommandTest extends CommandTestAbstract {
singletonList("key.pub")); singletonList("key.pub"));
} }
@Test
public void shouldCreateIbft2ExtraData() throws IOException {
runCmdAndCheckOutput(
cmd(),
"/operator/config_import_keys.json",
tmpOutputDirectoryPath,
"genesis.json",
false,
singletonList("key.pub"),
Optional.empty(),
Optional.of(
"0xf853a00000000000000000000000000000000000000000000000000000000000000000ea94d5feb0fc5a54a89f97aeb34c3df15397c19f6dd294d6a9a4c886eb008ac307abdc1f38745c1dd13a88808400000000c0"));
}
@Test
public void shouldCreateQbftExtraData() throws IOException {
runCmdAndCheckOutput(
cmd(),
"/operator/config_import_keys_qbft.json",
tmpOutputDirectoryPath,
"genesis.json",
false,
singletonList("key.pub"),
Optional.empty(),
Optional.of(
"0xf84fa00000000000000000000000000000000000000000000000000000000000000000ea94d5feb0fc5a54a89f97aeb34c3df15397c19f6dd294d6a9a4c886eb008ac307abdc1f38745c1dd13a88c080c0"));
}
private void runCmdAndCheckOutput( private void runCmdAndCheckOutput(
final Cmd cmd, final Cmd cmd,
final String configFile, final String configFile,
@ -278,6 +322,7 @@ public class OperatorSubCommandTest extends CommandTestAbstract {
genesisFileName, genesisFileName,
generate, generate,
expectedKeyFiles, expectedKeyFiles,
Optional.empty(),
Optional.empty()); Optional.empty());
} }
@ -290,6 +335,27 @@ public class OperatorSubCommandTest extends CommandTestAbstract {
final Collection<String> expectedKeyFiles, final Collection<String> expectedKeyFiles,
final Optional<SignatureAlgorithm> signatureAlgorithm) final Optional<SignatureAlgorithm> signatureAlgorithm)
throws IOException { throws IOException {
runCmdAndCheckOutput(
cmd,
configFile,
outputDirectoryPath,
genesisFileName,
generate,
expectedKeyFiles,
signatureAlgorithm,
Optional.empty());
}
private void runCmdAndCheckOutput(
final Cmd cmd,
final String configFile,
final Path outputDirectoryPath,
final String genesisFileName,
final boolean generate,
final Collection<String> expectedKeyFiles,
final Optional<SignatureAlgorithm> signatureAlgorithm,
final Optional<String> expectedExtraData)
throws IOException {
final URL configFilePath = this.getClass().getResource(configFile); final URL configFilePath = this.getClass().getResource(configFile);
parseCommand( parseCommand(
cmd( cmd(
@ -309,6 +375,8 @@ public class OperatorSubCommandTest extends CommandTestAbstract {
final String genesisString = contentOf(outputGenesisFile, UTF_8); final String genesisString = contentOf(outputGenesisFile, UTF_8);
final JsonObject genesisContent = new JsonObject(genesisString); final JsonObject genesisContent = new JsonObject(genesisString);
assertThat(genesisContent.containsKey("extraData")).isTrue(); assertThat(genesisContent.containsKey("extraData")).isTrue();
expectedExtraData.ifPresent(
extraData -> assertThat(genesisContent.getString("extraData")).isEqualTo(extraData));
final Path expectedKeysPath = outputDirectoryPath.resolve("keys"); final Path expectedKeysPath = outputDirectoryPath.resolve("keys");
final File keysDirectory = new File(expectedKeysPath.toUri()); final File keysDirectory = new File(expectedKeysPath.toUri());

@ -0,0 +1,41 @@
{
"genesis": {
"config": {
"chainId": 2017,
"petersburgBlock": 0,
"qbft": {
}
},
"nonce": "0x0",
"timestamp": "0x5b3c3d18",
"number": "0x0",
"gasUsed": "0x0",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x47b760",
"difficulty": "0x1",
"mixHash": "0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365",
"coinbase": "0x0000000000000000000000000000000000000000",
"ibft2": {
"blockperiodseconds": 2,
"epochlength": 30000,
"requesttimeoutseconds": 10
},
"alloc": {
"24defc2d149861d3d245749b81fe0e6b28e04f31": {
"balance": "0x446c3b15f9926687d2c40534fdb564000000000000"
},
"2a813d7db3de19b07f92268b6d4125ed295cbe00": {
"balance": "0x446c3b15f9926687d2c40534fdb542000000000000"
}
}
},
"blockchain": {
"nodes": {
"keys": [
"0x9481ee2b34196827e8c4807ae29c2675c15d3641e482ab81cc0bc3ef535755b426b9f5c474213df28a563fab0bf0830b455ee8955b3faac93075422a6abf160d",
"0xf9dff57fc0bae7cba6577d82caf40cf6d266afc2a212775eb45c2188ab30aeed01e724daf4203649d4aa7e30717bfeeaf0b4c30b5d651bad39bcae8429759889"
]
}
}
}

@ -0,0 +1,32 @@
{
"genesis": {
"nonce": "0x0",
"timestamp": "0x5b3c3d18",
"number": "0x0",
"gasUsed": "0x0",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x47b760",
"difficulty": "0x1",
"mixHash": "0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365",
"coinbase": "0x0000000000000000000000000000000000000000",
"ibft2": {
"blockperiodseconds": 2,
"epochlength": 30000,
"requesttimeoutseconds": 10
},
"alloc": {
"24defc2d149861d3d245749b81fe0e6b28e04f31": {
"balance": "0x446c3b15f9926687d2c40534fdb564000000000000"
},
"2a813d7db3de19b07f92268b6d4125ed295cbe00": {
"balance": "0x446c3b15f9926687d2c40534fdb542000000000000"
}
}
},
"blockchain": {
"nodes": {
"generate": true,
"count": 4
}
}
}
Loading…
Cancel
Save