From 755979a7710c97f99ac944541b81d849b5e110f0 Mon Sep 17 00:00:00 2001 From: Jason Frame Date: Mon, 17 May 2021 12:26:03 +1000 Subject: [PATCH] Support qbft encoding of extra data in the operator generate-blockchain subcommand (#2276) Signed-off-by: Jason Frame --- .../operator/GenerateBlockchainConfig.java | 14 +++- .../cli/operator/OperatorSubCommandTest.java | 70 ++++++++++++++++++- .../operator/config_import_keys_qbft.json | 41 +++++++++++ .../operator/config_no_config_section.json | 32 +++++++++ 4 files changed, 154 insertions(+), 3 deletions(-) create mode 100644 besu/src/test/resources/operator/config_import_keys_qbft.json create mode 100644 besu/src/test/resources/operator/config_no_config_section.json diff --git a/besu/src/main/java/org/hyperledger/besu/cli/subcommands/operator/GenerateBlockchainConfig.java b/besu/src/main/java/org/hyperledger/besu/cli/subcommands/operator/GenerateBlockchainConfig.java index 8319310113..2cfd7b1c7d 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/subcommands/operator/GenerateBlockchainConfig.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/subcommands/operator/GenerateBlockchainConfig.java @@ -23,6 +23,7 @@ import org.hyperledger.besu.config.GenesisConfigOptions; import org.hyperledger.besu.config.JsonGenesisConfigOptions; import org.hyperledger.besu.config.JsonUtil; 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.SECPPrivateKey; import org.hyperledger.besu.crypto.SECPPublicKey; @@ -59,7 +60,7 @@ import picocli.CommandLine.ParentCommand; @Command( 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) class GenerateBlockchainConfig implements Runnable { 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. */ 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 = JsonGenesisConfigOptions.fromJsonObject(configNode); if (genesisConfigOptions.isIbft2()) { @@ -242,6 +247,11 @@ class GenerateBlockchainConfig implements Runnable { final String extraData = IbftExtraDataCodec.encodeFromAddresses(addressesForGenesisExtraData).toString(); 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); } } diff --git a/besu/src/test/java/org/hyperledger/besu/cli/operator/OperatorSubCommandTest.java b/besu/src/test/java/org/hyperledger/besu/cli/operator/OperatorSubCommandTest.java index 717015ac99..363b58eb68 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/operator/OperatorSubCommandTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/operator/OperatorSubCommandTest.java @@ -72,7 +72,7 @@ public class OperatorSubCommandTest extends CommandTestAbstract { + System.lineSeparator() + " generate-blockchain-config Generates node keypairs and genesis file with RLP" + System.lineSeparator() - + " encoded IBFT 2.0 extra data." + + " encoded extra data." + System.lineSeparator() + " 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"); } + @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 public void shouldImportSecp256R1Keys() throws IOException { runCmdAndCheckOutput( @@ -263,6 +279,34 @@ public class OperatorSubCommandTest extends CommandTestAbstract { 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( final Cmd cmd, final String configFile, @@ -278,6 +322,7 @@ public class OperatorSubCommandTest extends CommandTestAbstract { genesisFileName, generate, expectedKeyFiles, + Optional.empty(), Optional.empty()); } @@ -290,6 +335,27 @@ public class OperatorSubCommandTest extends CommandTestAbstract { final Collection expectedKeyFiles, final Optional signatureAlgorithm) 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 expectedKeyFiles, + final Optional signatureAlgorithm, + final Optional expectedExtraData) + throws IOException { final URL configFilePath = this.getClass().getResource(configFile); parseCommand( cmd( @@ -309,6 +375,8 @@ public class OperatorSubCommandTest extends CommandTestAbstract { final String genesisString = contentOf(outputGenesisFile, UTF_8); final JsonObject genesisContent = new JsonObject(genesisString); assertThat(genesisContent.containsKey("extraData")).isTrue(); + expectedExtraData.ifPresent( + extraData -> assertThat(genesisContent.getString("extraData")).isEqualTo(extraData)); final Path expectedKeysPath = outputDirectoryPath.resolve("keys"); final File keysDirectory = new File(expectedKeysPath.toUri()); diff --git a/besu/src/test/resources/operator/config_import_keys_qbft.json b/besu/src/test/resources/operator/config_import_keys_qbft.json new file mode 100644 index 0000000000..fe757326b4 --- /dev/null +++ b/besu/src/test/resources/operator/config_import_keys_qbft.json @@ -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" + ] + } + } +} diff --git a/besu/src/test/resources/operator/config_no_config_section.json b/besu/src/test/resources/operator/config_no_config_section.json new file mode 100644 index 0000000000..2841aea9c4 --- /dev/null +++ b/besu/src/test/resources/operator/config_no_config_section.json @@ -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 + } + } +} \ No newline at end of file