diff --git a/exonum-java-binding/CHANGELOG.md b/exonum-java-binding/CHANGELOG.md index 19810d3d29..a8059b3d70 100644 --- a/exonum-java-binding/CHANGELOG.md +++ b/exonum-java-binding/CHANGELOG.md @@ -15,6 +15,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +### Removed +- Replaced Blockchain#getActualConfiguration with Blockchain#getConsensusConfiguration, + returning only the consensus configuration (now also containing the validator public keys) + as a protobuf message. + ## [0.8.0] - 2019-09-09 ### Overview diff --git a/exonum-java-binding/common/src/main/java/com/exonum/binding/common/configuration/ConsensusConfiguration.java b/exonum-java-binding/common/src/main/java/com/exonum/binding/common/configuration/ConsensusConfiguration.java deleted file mode 100644 index b8b1905ef8..0000000000 --- a/exonum-java-binding/common/src/main/java/com/exonum/binding/common/configuration/ConsensusConfiguration.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2018 The Exonum Team - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.exonum.binding.common.configuration; - -import com.google.auto.value.AutoValue; -import com.google.gson.Gson; -import com.google.gson.TypeAdapter; -import com.google.gson.annotations.SerializedName; - -/** - * Blockchain Consensus algorithm parameters. - * - *

See Exonum configuration for - * Consensus configuration details. - */ -@AutoValue -public abstract class ConsensusConfiguration { - - /** - * Interval between first two rounds. This interval defines the time that passes - * between the moment a new block is committed to the blockchain and the - * time when second round starts, regardless of whether a new block has - * been committed during this period or not. - * - *

Note that rounds in Exonum do not have a defined end time. Nodes in a new round can - * continue to vote for proposals and process messages related to previous rounds. - */ - @SerializedName("first_round_timeout") - public abstract long firstRoundTimeout(); - - /** - * This parameter defines the frequency with which a node broadcasts its status message to the - * network. - */ - @SerializedName("status_timeout") - public abstract long statusTimeout(); - - /** - * This parameter defines the frequency with which a node requests collected Connect messages - * from a random peer node in the network. - */ - @SerializedName("peers_timeout") - public abstract long peersTimeout(); - - /** - * Maximum number of transactions per block. - */ - @SerializedName("txs_block_limit") - public abstract int txsBlockLimit(); - - /** - * This parameter determines the maximum size of both consensus messages and transactions. - */ - @SerializedName("max_message_len") - public abstract int maxMessageLen(); - - /** - * Minimal propose timeout. - */ - @SerializedName("min_propose_timeout") - public abstract long minProposeTimeout(); - - /** - * Maximal propose timeout. - */ - @SerializedName("max_propose_timeout") - public abstract long maxProposeTimeout(); - - /** - * Amount of transactions in pool to start use min_propose_timeout. - */ - @SerializedName("propose_timeout_threshold") - public abstract int proposeTimeoutThreshold(); - - /** - * Provides a Gson type adapter for this class. - * - * @see com.exonum.binding.common.serialization.json.CommonTypeAdapterFactory - */ - public static TypeAdapter typeAdapter(Gson gson) { - return new AutoValue_ConsensusConfiguration.GsonTypeAdapter(gson); - } - - public static ConsensusConfiguration.Builder builder() { - return new AutoValue_ConsensusConfiguration.Builder(); - } - - @AutoValue.Builder - public abstract static class Builder { - public abstract Builder firstRoundTimeout(long firstRoundTimeout); - - public abstract Builder statusTimeout(long statusTimeout); - - public abstract Builder peersTimeout(long peersTimeout); - - public abstract Builder txsBlockLimit(int txsBlockLimit); - - public abstract Builder maxMessageLen(int maxMessageLen); - - public abstract Builder minProposeTimeout(long minProposeTimeout); - - public abstract Builder maxProposeTimeout(long maxProposeTimeout); - - public abstract Builder proposeTimeoutThreshold(int proposeTimeoutThreshold); - - public abstract ConsensusConfiguration build(); - } -} diff --git a/exonum-java-binding/common/src/main/java/com/exonum/binding/common/configuration/StoredConfiguration.java b/exonum-java-binding/common/src/main/java/com/exonum/binding/common/configuration/StoredConfiguration.java deleted file mode 100644 index 51030467c0..0000000000 --- a/exonum-java-binding/common/src/main/java/com/exonum/binding/common/configuration/StoredConfiguration.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2018 The Exonum Team - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.exonum.binding.common.configuration; - -import com.exonum.binding.common.hash.HashCode; -import com.google.auto.value.AutoValue; -import com.google.gson.Gson; -import com.google.gson.TypeAdapter; -import com.google.gson.annotations.SerializedName; -import java.util.List; - -/** - * Represents a blockchain configuration which is a set of values that determine - * the network access parameters of a node and behavior of the node while operating in the network. - * - *

See Exonum configuration for - * configuration details. - * - *

Services configuration parameters would be available after - * (https://jira.bf.local/browse/ECR-2683) would be implemented. - */ -@AutoValue -public abstract class StoredConfiguration { - - /** - * Hash of the previous configuration, which can be used to find that configuration. - */ - @SerializedName("previous_cfg_hash") - public abstract HashCode previousCfgHash(); - - /** - * The height, starting from which this configuration becomes actual. - */ - @SerializedName("actual_from") - public abstract long actualFrom(); - - /** - * List of validators consensus and service public keys. - * - * @see Validator keys configuration section - */ - @SerializedName("validator_keys") - public abstract List validatorKeys(); - - /** - * Consensus algorithm parameters. - */ - @SerializedName("consensus") - public abstract ConsensusConfiguration consensusConfiguration(); - - //TODO add majorityCount, services fields (https://jira.bf.local/browse/ECR-2683) - - /** - * Provides a Gson type adapter for this class. - * - * @see com.exonum.binding.common.serialization.json.CommonTypeAdapterFactory - */ - public static TypeAdapter typeAdapter(Gson gson) { - return new AutoValue_StoredConfiguration.GsonTypeAdapter(gson); - } - - public static Builder builder() { - return new AutoValue_StoredConfiguration.Builder(); - } - - @AutoValue.Builder - public abstract static class Builder { - public abstract Builder previousCfgHash(HashCode previousCfgHash); - - public abstract Builder actualFrom(long actualFrom); - - public abstract Builder validatorKeys(List validatorKeys); - - public abstract Builder consensusConfiguration(ConsensusConfiguration consensusConfiguration); - - public abstract StoredConfiguration build(); - } -} diff --git a/exonum-java-binding/common/src/main/java/com/exonum/binding/common/configuration/ValidatorKey.java b/exonum-java-binding/common/src/main/java/com/exonum/binding/common/configuration/ValidatorKey.java deleted file mode 100644 index 1d5e3ff39b..0000000000 --- a/exonum-java-binding/common/src/main/java/com/exonum/binding/common/configuration/ValidatorKey.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2018 The Exonum Team - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.exonum.binding.common.configuration; - -import com.exonum.binding.common.crypto.PublicKey; -import com.google.auto.value.AutoValue; -import com.google.gson.Gson; -import com.google.gson.TypeAdapter; -import com.google.gson.annotations.SerializedName; - -/** - * Public keys of validator nodes. - */ -@AutoValue -public abstract class ValidatorKey { - /** - * Consensus key is used for messages related to the consensus algorithm. - */ - @SerializedName("consensus_key") - public abstract PublicKey consensusKey(); - - /** - * Service key is used for services, for example, the configuration updater service. - */ - @SerializedName("service_key") - public abstract PublicKey serviceKey(); - - /** - * Provides a Gson type adapter for this class. - * - * @see com.exonum.binding.common.serialization.json.CommonTypeAdapterFactory - */ - public static TypeAdapter typeAdapter(Gson gson) { - return new AutoValue_ValidatorKey.GsonTypeAdapter(gson); - } - - public static ValidatorKey.Builder builder() { - return new AutoValue_ValidatorKey.Builder(); - } - - @AutoValue.Builder - public abstract static class Builder { - public abstract Builder consensusKey(PublicKey consensusKey); - - public abstract Builder serviceKey(PublicKey serviceKey); - - public abstract ValidatorKey build(); - } -} diff --git a/exonum-java-binding/common/src/test/java/com/exonum/binding/common/serialization/json/StoredConfigurationGsonSerializerTest.java b/exonum-java-binding/common/src/test/java/com/exonum/binding/common/serialization/json/StoredConfigurationGsonSerializerTest.java deleted file mode 100644 index 2cc8378d27..0000000000 --- a/exonum-java-binding/common/src/test/java/com/exonum/binding/common/serialization/json/StoredConfigurationGsonSerializerTest.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2018 The Exonum Team - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.exonum.binding.common.serialization.json; - -import static com.exonum.binding.common.serialization.json.JsonSerializer.json; -import static java.util.Collections.singletonList; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.hamcrest.MatcherAssert.assertThat; - -import com.exonum.binding.common.configuration.ConsensusConfiguration; -import com.exonum.binding.common.configuration.StoredConfiguration; -import com.exonum.binding.common.configuration.ValidatorKey; -import com.exonum.binding.common.crypto.PublicKey; -import com.exonum.binding.common.hash.HashCode; -import java.util.List; -import org.junit.jupiter.api.Test; - -class StoredConfigurationGsonSerializerTest { - - private static final String CONFIG_EXAMPLE = "{\n" - + "\"previous_cfg_hash\": \"000000000000000000000000000000000000000000000000000000000000000" - + "0\",\n" - + "\"actual_from\": 0,\n" - + "\"validator_keys\": [\n" - + " {\n" - + " \"consensus_key\": \"43eb3be553c55b02b65e08c18bb060404b27e362ccf108cbad94ea097de" - + "cbc0a\",\n" - + " \"service_key\": \"79c1fcefcbfaeae43575ab0ef793c24aae7b39186244e6552c18b8f7d0b0d" - + "e12\"\n" - + " }\n" - + "],\n" - + "\"consensus\": {\n" - + " \"first_round_timeout\": 3000,\n" - + " \"status_timeout\": 5000,\n" - + " \"peers_timeout\": 10000,\n" - + " \"txs_block_limit\": 1000,\n" - + " \"max_message_len\": 1048576,\n" - + " \"min_propose_timeout\": 10,\n" - + " \"max_propose_timeout\": 200,\n" - + " \"propose_timeout_threshold\": 500\n" - + "},\n" - + "\"majority_count\": null,\n" - + "\"services\": {\n" - + " \"configuration\": null,\n" - + " \"cryptocurrency-demo\": null\n" - + "}\n" - + "}"; - - @Test - void roundTripTest() { - StoredConfiguration configuration = createConfiguration(); - StoredConfiguration restoredConfiguration = json().fromJson( - json().toJson(configuration), StoredConfiguration.class); - - assertThat(restoredConfiguration, equalTo(configuration)); - } - - @Test - void readConfiguration() { - StoredConfiguration configuration = json() - .fromJson(CONFIG_EXAMPLE, StoredConfiguration.class); - - assertThat(configuration, notNullValue()); - assertThat(configuration.previousCfgHash(), - is(HashCode - .fromString("0000000000000000000000000000000000000000000000000000000000000000")) - ); - assertThat(configuration.actualFrom(), is(0L)); - - ConsensusConfiguration consensusConfiguration = configuration.consensusConfiguration(); - assertThat(consensusConfiguration, notNullValue()); - assertThat(consensusConfiguration.maxMessageLen(), is(1048576)); - assertThat(consensusConfiguration.maxProposeTimeout(), is(200L)); - assertThat(consensusConfiguration.minProposeTimeout(), is(10L)); - assertThat(consensusConfiguration.peersTimeout(), is(10000L)); - assertThat(consensusConfiguration.proposeTimeoutThreshold(), is(500)); - assertThat(consensusConfiguration.firstRoundTimeout(), is(3000L)); - assertThat(consensusConfiguration.statusTimeout(), is(5000L)); - assertThat(consensusConfiguration.txsBlockLimit(), is(1000)); - - List expectedValidatorKeys = singletonList(ValidatorKey.builder() - .consensusKey(PublicKey.fromHexString( - "43eb3be553c55b02b65e08c18bb060404b27e362ccf108cbad94ea097decbc0a")) - .serviceKey(PublicKey.fromHexString( - "79c1fcefcbfaeae43575ab0ef793c24aae7b39186244e6552c18b8f7d0b0de12")) - .build()); - assertThat(configuration.validatorKeys(), is(expectedValidatorKeys)); - } - - private StoredConfiguration createConfiguration() { - return StoredConfiguration.builder() - .previousCfgHash(HashCode.fromString("11")) - .actualFrom(1) - .validatorKeys( - singletonList( - ValidatorKey.builder() - .consensusKey(PublicKey.fromHexString("22")) - .serviceKey(PublicKey.fromHexString("33")) - .build() - ) - ) - .consensusConfiguration( - ConsensusConfiguration.builder() - .firstRoundTimeout(1) - .statusTimeout(2) - .peersTimeout(3) - .txsBlockLimit(4) - .maxMessageLen(5) - .minProposeTimeout(6) - .maxProposeTimeout(7) - .proposeTimeoutThreshold(8) - .build() - ) - .build(); - } -} diff --git a/exonum-java-binding/core/src/main/java/com/exonum/binding/core/blockchain/Blockchain.java b/exonum-java-binding/core/src/main/java/com/exonum/binding/core/blockchain/Blockchain.java index 0f04b98971..454b5a0fd6 100644 --- a/exonum-java-binding/core/src/main/java/com/exonum/binding/core/blockchain/Blockchain.java +++ b/exonum-java-binding/core/src/main/java/com/exonum/binding/core/blockchain/Blockchain.java @@ -20,7 +20,6 @@ import com.exonum.binding.common.blockchain.TransactionLocation; import com.exonum.binding.common.blockchain.TransactionResult; -import com.exonum.binding.common.configuration.StoredConfiguration; import com.exonum.binding.common.hash.HashCode; import com.exonum.binding.common.message.TransactionMessage; import com.exonum.binding.core.storage.database.View; @@ -29,6 +28,7 @@ import com.exonum.binding.core.storage.indices.MapIndex; import com.exonum.binding.core.storage.indices.ProofListIndexProxy; import com.exonum.binding.core.storage.indices.ProofMapIndexProxy; +import com.exonum.binding.messages.Blockchain.Config; import com.google.common.annotations.VisibleForTesting; import java.util.Optional; @@ -231,13 +231,14 @@ public Block getLastBlock() { } /** - * Returns the configuration for the latest height of the blockchain, including services and their - * parameters. + * Returns the current consensus configuration of the network. * - * @throws RuntimeException if the "genesis block" was not created + * @throws IllegalStateException if the "genesis block" was not created + * @see Exonum configuration for + * consensus configuration information. */ - public StoredConfiguration getActualConfiguration() { - return schema.getActualConfiguration(); + public Config getConsensusConfiguration() { + return schema.getConsensusConfiguration(); } /** diff --git a/exonum-java-binding/core/src/main/java/com/exonum/binding/core/blockchain/CoreSchemaProxy.java b/exonum-java-binding/core/src/main/java/com/exonum/binding/core/blockchain/CoreSchemaProxy.java index 22364b5833..00c97e17f3 100644 --- a/exonum-java-binding/core/src/main/java/com/exonum/binding/core/blockchain/CoreSchemaProxy.java +++ b/exonum-java-binding/core/src/main/java/com/exonum/binding/core/blockchain/CoreSchemaProxy.java @@ -16,12 +16,11 @@ package com.exonum.binding.core.blockchain; -import static com.exonum.binding.common.serialization.json.JsonSerializer.json; import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkState; import com.exonum.binding.common.blockchain.TransactionLocation; import com.exonum.binding.common.blockchain.TransactionResult; -import com.exonum.binding.common.configuration.StoredConfiguration; import com.exonum.binding.common.hash.HashCode; import com.exonum.binding.common.message.TransactionMessage; import com.exonum.binding.common.serialization.Serializer; @@ -33,6 +32,7 @@ import com.exonum.binding.core.proxy.NativeHandle; import com.exonum.binding.core.proxy.ProxyDestructor; import com.exonum.binding.core.storage.database.View; +import com.exonum.binding.core.storage.indices.EntryIndexProxy; import com.exonum.binding.core.storage.indices.KeySetIndexProxy; import com.exonum.binding.core.storage.indices.ListIndex; import com.exonum.binding.core.storage.indices.ListIndexProxy; @@ -41,6 +41,7 @@ import com.exonum.binding.core.storage.indices.ProofListIndexProxy; import com.exonum.binding.core.storage.indices.ProofMapIndexProxy; import com.exonum.binding.core.util.LibraryLoader; +import com.exonum.binding.messages.Blockchain.Config; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -64,6 +65,8 @@ final class CoreSchemaProxy { TransactionResultSerializer.INSTANCE; private static final Serializer TRANSACTION_MESSAGE_SERIALIZER = StandardSerializers.transactionMessage(); + private static final Serializer CONSENSUS_CONFIG_SERIALIZER = + StandardSerializers.protobuf(Config.class); private CoreSchemaProxy(NativeHandle nativeHandle, View dbView) { this.nativeHandle = nativeHandle; @@ -175,14 +178,16 @@ KeySetIndexProxy getTransactionPool() { } /** - * Returns the configuration for the latest height of the blockchain. + * Returns the current consensus configuration of the network. * - * @throws RuntimeException if the "genesis block" was not created + * @throws IllegalStateException if the "genesis block" was not created */ - StoredConfiguration getActualConfiguration() { - String rawConfiguration = nativeGetActualConfiguration(nativeHandle.get()); - - return json().fromJson(rawConfiguration, StoredConfiguration.class); + Config getConsensusConfiguration() { + EntryIndexProxy configEntry = EntryIndexProxy.newInstance(CoreIndex.CONSENSUS_CONFIG, + dbView, CONSENSUS_CONFIG_SERIALIZER); + checkState(configEntry.isPresent(), "No consensus configuration: requesting the configuration " + + "before the genesis block was created"); + return configEntry.get(); } private static native long nativeCreate(long viewNativeHandle); @@ -191,8 +196,6 @@ StoredConfiguration getActualConfiguration() { private static native long nativeGetHeight(long nativeHandle); - private static native String nativeGetActualConfiguration(long nativeHandle); - /** * Returns the latest committed block. * @@ -211,6 +214,7 @@ private byte[] toCoreStorageKey(long value) { * Mapping for Exonum core indexes by name. */ private static final class CoreIndex { + private static final String PREFIX = "core."; private static final String BLOCK_TRANSACTIONS = PREFIX + "block_transactions"; private static final String ALL_BLOCK_HASHES = PREFIX + "block_hashes_by_height"; @@ -219,6 +223,7 @@ private static final class CoreIndex { private static final String TRANSACTIONS_RESULTS = PREFIX + "transaction_results"; private static final String TRANSACTIONS_LOCATIONS = PREFIX + "transactions_locations"; private static final String TRANSACTIONS_POOL = PREFIX + "transactions_pool"; + private static final String CONSENSUS_CONFIG = "consensus.config"; } } diff --git a/exonum-java-binding/core/src/test/java/com/exonum/binding/core/blockchain/CoreSchemaProxyIntegrationTest.java b/exonum-java-binding/core/src/test/java/com/exonum/binding/core/blockchain/CoreSchemaProxyIntegrationTest.java index 7bc6ad2561..e3e2ba5c45 100644 --- a/exonum-java-binding/core/src/test/java/com/exonum/binding/core/blockchain/CoreSchemaProxyIntegrationTest.java +++ b/exonum-java-binding/core/src/test/java/com/exonum/binding/core/blockchain/CoreSchemaProxyIntegrationTest.java @@ -30,7 +30,6 @@ import com.google.common.collect.ImmutableSet; import java.util.Set; import java.util.function.Consumer; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @RequiresNativeLibrary @@ -58,9 +57,9 @@ void getBlockTransactionsTest() { } @Test - @Disabled("ECR-3612") - void getActiveConfigurationBeforeGenesisBlock() { - assertSchema((schema) -> assertThrows(RuntimeException.class, schema::getActualConfiguration)); + void getConsensusConfigurationBeforeGenesisBlock() { + assertSchema((schema) -> + assertThrows(IllegalStateException.class, schema::getConsensusConfiguration)); } @Test diff --git a/exonum-java-binding/integration-tests/src/test/java/com/exonum/binding/test/BlockchainIntegrationTest.java b/exonum-java-binding/integration-tests/src/test/java/com/exonum/binding/test/BlockchainIntegrationTest.java index 1a35fbf898..205fe74e26 100644 --- a/exonum-java-binding/integration-tests/src/test/java/com/exonum/binding/test/BlockchainIntegrationTest.java +++ b/exonum-java-binding/integration-tests/src/test/java/com/exonum/binding/test/BlockchainIntegrationTest.java @@ -25,8 +25,6 @@ import com.exonum.binding.common.blockchain.TransactionLocation; import com.exonum.binding.common.blockchain.TransactionResult; -import com.exonum.binding.common.configuration.StoredConfiguration; -import com.exonum.binding.common.configuration.ValidatorKey; import com.exonum.binding.common.crypto.CryptoFunction; import com.exonum.binding.common.crypto.CryptoFunctions; import com.exonum.binding.common.crypto.KeyPair; @@ -42,6 +40,8 @@ import com.exonum.binding.core.storage.indices.MapIndex; import com.exonum.binding.core.storage.indices.ProofMapIndexProxy; import com.exonum.binding.core.transaction.RawTransaction; +import com.exonum.binding.messages.Blockchain.Config; +import com.exonum.binding.messages.Blockchain.ValidatorKeys; import com.exonum.binding.testkit.EmulatedNode; import com.exonum.binding.testkit.TestKit; import com.google.common.collect.ImmutableList; @@ -387,24 +387,24 @@ void getLastBlock() { } @Test - void getActualConfiguration() { + void getConsensusConfiguration() { testKitTest((blockchain) -> { - StoredConfiguration configuration = blockchain.getActualConfiguration(); - List validatorKeys = configuration.validatorKeys(); + Config configuration = blockchain.getConsensusConfiguration(); + int numKeysInConfig = configuration.getValidatorKeysCount(); // Check the number of validator keys - assertThat(validatorKeys).hasSize(VALIDATOR_COUNT); - - // Check the public key of the emulated node is included - List serviceKeys = validatorKeys.stream() - .map(ValidatorKey::serviceKey) + assertThat(numKeysInConfig).isEqualTo(VALIDATOR_COUNT); + + // Check the public service key of the emulated node is included + List serviceKeys = configuration.getValidatorKeysList().stream() + .map(ValidatorKeys::getServiceKey) + // fixme: [ECR-3734] highly error-prone and verbose key#getData.toByteArray susceptible + // to incorrect key#toByteArray. + .map(key -> PublicKey.fromBytes(key.getData().toByteArray())) .collect(toList()); EmulatedNode emulatedNode = testKit.getEmulatedNode(); PublicKey emulatedNodeServiceKey = emulatedNode.getServiceKeyPair().getPublicKey(); List expectedKeys = ImmutableList.of(emulatedNodeServiceKey); assertThat(serviceKeys).isEqualTo(expectedKeys); - - // Check the previous config is empty - assertThat(configuration.previousCfgHash()).isEqualTo(ZERO_HASH_CODE); }); } diff --git a/exonum-java-binding/qa-service/src/main/java/com/exonum/binding/qaservice/ApiController.java b/exonum-java-binding/qa-service/src/main/java/com/exonum/binding/qaservice/ApiController.java index ec1aa32161..cbf36ea7c0 100644 --- a/exonum-java-binding/qa-service/src/main/java/com/exonum/binding/qaservice/ApiController.java +++ b/exonum-java-binding/qa-service/src/main/java/com/exonum/binding/qaservice/ApiController.java @@ -18,7 +18,7 @@ import static com.exonum.binding.common.serialization.json.JsonSerializer.json; import static com.exonum.binding.qaservice.ApiController.QaPaths.COUNTER_ID_PARAM; -import static com.exonum.binding.qaservice.ApiController.QaPaths.GET_ACTUAL_CONFIGURATION_PATH; +import static com.exonum.binding.qaservice.ApiController.QaPaths.GET_CONSENSUS_CONFIGURATION_PATH; import static com.exonum.binding.qaservice.ApiController.QaPaths.GET_COUNTER_PATH; import static com.exonum.binding.qaservice.ApiController.QaPaths.SUBMIT_CREATE_COUNTER_TX_PATH; import static com.exonum.binding.qaservice.ApiController.QaPaths.SUBMIT_INCREMENT_COUNTER_TX_PATH; @@ -30,19 +30,21 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.net.HttpHeaders.CONTENT_TYPE; import static com.google.common.net.HttpHeaders.LOCATION; +import static com.google.common.net.MediaType.OCTET_STREAM; import static java.net.HttpURLConnection.HTTP_BAD_REQUEST; import static java.net.HttpURLConnection.HTTP_CREATED; import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR; import static java.net.HttpURLConnection.HTTP_NOT_FOUND; -import com.exonum.binding.common.configuration.StoredConfiguration; import com.exonum.binding.common.crypto.PublicKey; import com.exonum.binding.common.hash.HashCode; +import com.exonum.binding.messages.Blockchain.Config; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; import io.vertx.core.Handler; import io.vertx.core.MultiMap; +import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpServerRequest; import io.vertx.core.http.HttpServerResponse; import io.vertx.ext.web.Router; @@ -80,7 +82,7 @@ void mountApi(Router router) { .put(SUBMIT_VALID_ERROR_TX_PATH, this::submitValidErrorTx) .put(SUBMIT_UNKNOWN_TX_PATH, this::submitUnknownTx) .put(GET_COUNTER_PATH, this::getCounter) - .put(GET_ACTUAL_CONFIGURATION_PATH, this::getActualConfiguration) + .put(GET_CONSENSUS_CONFIGURATION_PATH, this::getConsensusConfiguration) .put(TIME_PATH, this::getTime) .put(VALIDATORS_TIMES_PATH, this::getValidatorsTimes) .build(); @@ -138,9 +140,12 @@ private void getCounter(RoutingContext rc) { respondWithJson(rc, counter); } - private void getActualConfiguration(RoutingContext rc) { - StoredConfiguration configuration = service.getActualConfiguration(); - respondWithJson(rc, configuration); + private void getConsensusConfiguration(RoutingContext rc) { + Config configuration = service.getConsensusConfiguration(); + + rc.response() + .putHeader(CONTENT_TYPE, OCTET_STREAM.toString()) + .write(Buffer.buffer(configuration.toByteArray())); } private void getTime(RoutingContext rc) { @@ -256,7 +261,7 @@ static class QaPaths { static final String COUNTER_ID_PARAM = "counterId"; static final String GET_COUNTER_PATH = "/counter/:" + COUNTER_ID_PARAM; @VisibleForTesting - static final String GET_ACTUAL_CONFIGURATION_PATH = "/actualConfiguration"; + static final String GET_CONSENSUS_CONFIGURATION_PATH = "/consensusConfiguration"; @VisibleForTesting static final String TIME_PATH = "/time"; @VisibleForTesting diff --git a/exonum-java-binding/qa-service/src/main/java/com/exonum/binding/qaservice/QaService.java b/exonum-java-binding/qa-service/src/main/java/com/exonum/binding/qaservice/QaService.java index 712d0f91d8..e3d45697b2 100644 --- a/exonum-java-binding/qa-service/src/main/java/com/exonum/binding/qaservice/QaService.java +++ b/exonum-java-binding/qa-service/src/main/java/com/exonum/binding/qaservice/QaService.java @@ -16,10 +16,10 @@ package com.exonum.binding.qaservice; -import com.exonum.binding.common.configuration.StoredConfiguration; import com.exonum.binding.common.crypto.PublicKey; import com.exonum.binding.common.hash.HashCode; import com.exonum.binding.core.service.Service; +import com.exonum.binding.messages.Blockchain.Config; import java.time.ZonedDateTime; import java.util.Map; import java.util.Optional; @@ -45,7 +45,7 @@ public interface QaService extends Service { Optional getValue(HashCode counterId); - StoredConfiguration getActualConfiguration(); + Config getConsensusConfiguration(); Optional getTime(); diff --git a/exonum-java-binding/qa-service/src/main/java/com/exonum/binding/qaservice/QaServiceImpl.java b/exonum-java-binding/qa-service/src/main/java/com/exonum/binding/qaservice/QaServiceImpl.java index 059faf1080..5678c9e01b 100644 --- a/exonum-java-binding/qa-service/src/main/java/com/exonum/binding/qaservice/QaServiceImpl.java +++ b/exonum-java-binding/qa-service/src/main/java/com/exonum/binding/qaservice/QaServiceImpl.java @@ -20,7 +20,6 @@ import static com.google.common.base.Preconditions.checkState; import static java.nio.charset.StandardCharsets.UTF_8; -import com.exonum.binding.common.configuration.StoredConfiguration; import com.exonum.binding.common.crypto.PublicKey; import com.exonum.binding.common.hash.HashCode; import com.exonum.binding.common.hash.Hashing; @@ -36,6 +35,7 @@ import com.exonum.binding.core.storage.indices.EntryIndexProxy; import com.exonum.binding.core.storage.indices.MapIndex; import com.exonum.binding.core.transaction.RawTransaction; +import com.exonum.binding.messages.Blockchain.Config; import com.exonum.binding.qaservice.transactions.CreateCounterTx; import com.exonum.binding.qaservice.transactions.ErrorTx; import com.exonum.binding.qaservice.transactions.IncrementCounterTx; @@ -190,13 +190,13 @@ public Optional getValue(HashCode counterId) { } @Override - public StoredConfiguration getActualConfiguration() { + public Config getConsensusConfiguration() { checkBlockchainInitialized(); return node.withSnapshot((view) -> { Blockchain blockchain = Blockchain.newInstance(view); - return blockchain.getActualConfiguration(); + return blockchain.getConsensusConfiguration(); }); } diff --git a/exonum-java-binding/qa-service/src/test/java/com/exonum/binding/qaservice/ApiControllerIntegrationTest.java b/exonum-java-binding/qa-service/src/test/java/com/exonum/binding/qaservice/ApiControllerIntegrationTest.java index c80b689a53..2e4c9474ac 100644 --- a/exonum-java-binding/qa-service/src/test/java/com/exonum/binding/qaservice/ApiControllerIntegrationTest.java +++ b/exonum-java-binding/qa-service/src/test/java/com/exonum/binding/qaservice/ApiControllerIntegrationTest.java @@ -17,7 +17,7 @@ package com.exonum.binding.qaservice; import static com.exonum.binding.common.hash.Hashing.sha256; -import static com.exonum.binding.qaservice.ApiController.QaPaths.GET_ACTUAL_CONFIGURATION_PATH; +import static com.exonum.binding.qaservice.ApiController.QaPaths.GET_CONSENSUS_CONFIGURATION_PATH; import static com.exonum.binding.qaservice.ApiController.QaPaths.SUBMIT_CREATE_COUNTER_TX_PATH; import static com.exonum.binding.qaservice.ApiController.QaPaths.SUBMIT_INCREMENT_COUNTER_TX_PATH; import static com.exonum.binding.qaservice.ApiController.QaPaths.SUBMIT_UNKNOWN_TX_PATH; @@ -32,7 +32,6 @@ import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR; import static java.net.HttpURLConnection.HTTP_NOT_FOUND; import static java.net.HttpURLConnection.HTTP_OK; -import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; import static org.mockito.ArgumentMatchers.eq; @@ -40,13 +39,11 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import com.exonum.binding.common.configuration.ConsensusConfiguration; -import com.exonum.binding.common.configuration.StoredConfiguration; -import com.exonum.binding.common.configuration.ValidatorKey; import com.exonum.binding.common.crypto.PublicKey; import com.exonum.binding.common.hash.HashCode; import com.exonum.binding.common.serialization.json.JsonSerializer; import com.exonum.binding.core.blockchain.serialization.CoreTypeAdapterFactory; +import com.exonum.binding.messages.Blockchain.Config; import com.google.common.collect.ImmutableMap; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; @@ -325,20 +322,19 @@ void multiMapTest() { } @Test - void getActualConfiguration(VertxTestContext context) { - StoredConfiguration configuration = createConfiguration(); - when(qaService.getActualConfiguration()).thenReturn(configuration); + void getConsensusConfiguration(VertxTestContext context) { + Config configuration = createConfiguration(); + when(qaService.getConsensusConfiguration()).thenReturn(configuration); - get(GET_ACTUAL_CONFIGURATION_PATH) + get(GET_CONSENSUS_CONFIGURATION_PATH) .send(context.succeeding(response -> context.verify(() -> { assertAll( () -> assertThat(response.statusCode()).isEqualTo(HTTP_OK), () -> { - String body = response.bodyAsString(); - StoredConfiguration storedConfiguration = JSON_SERIALIZER - .fromJson(body, StoredConfiguration.class); + Buffer body = response.bodyAsBuffer(); + Config consensusConfig = Config.parseFrom(body.getBytes()); - assertThat(storedConfiguration).isEqualTo(configuration); + assertThat(consensusConfig).isEqualTo(configuration); }); context.completeNow(); }))); @@ -431,30 +427,16 @@ private Handler>> checkCreatedTransaction( })); } - private StoredConfiguration createConfiguration() { - return StoredConfiguration.builder() - .previousCfgHash(HashCode.fromString("11")) - .actualFrom(1) - .validatorKeys( - singletonList( - ValidatorKey.builder() - .consensusKey(PublicKey.fromHexString("22")) - .serviceKey(PublicKey.fromHexString("33")) - .build() - ) - ) - .consensusConfiguration( - ConsensusConfiguration.builder() - .firstRoundTimeout(1) - .statusTimeout(2) - .peersTimeout(3) - .txsBlockLimit(4) - .maxMessageLen(5) - .minProposeTimeout(6) - .maxProposeTimeout(7) - .proposeTimeoutThreshold(8) - .build() - ) + private static Config createConfiguration() { + return Config.newBuilder() + .setFirstRoundTimeout(1) + .setStatusTimeout(2) + .setPeersTimeout(3) + .setTxsBlockLimit(4) + .setMaxMessageLen(5) + .setMinProposeTimeout(6) + .setMaxProposeTimeout(7) + .setProposeTimeoutThreshold(8) .build(); } } diff --git a/exonum-java-binding/qa-service/src/test/java/com/exonum/binding/qaservice/QaServiceImplIntegrationTest.java b/exonum-java-binding/qa-service/src/test/java/com/exonum/binding/qaservice/QaServiceImplIntegrationTest.java index 6d424f59ab..70b54156a5 100644 --- a/exonum-java-binding/qa-service/src/test/java/com/exonum/binding/qaservice/QaServiceImplIntegrationTest.java +++ b/exonum-java-binding/qa-service/src/test/java/com/exonum/binding/qaservice/QaServiceImplIntegrationTest.java @@ -16,7 +16,6 @@ package com.exonum.binding.qaservice; -import static com.exonum.binding.common.hash.Hashing.DEFAULT_HASH_SIZE_BYTES; import static com.exonum.binding.common.hash.Hashing.sha256; import static com.exonum.binding.qaservice.QaServiceImpl.AFTER_COMMIT_COUNTER_NAME; import static com.exonum.binding.qaservice.QaServiceImpl.DEFAULT_COUNTER_NAME; @@ -28,14 +27,14 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; -import com.exonum.binding.common.configuration.StoredConfiguration; -import com.exonum.binding.common.configuration.ValidatorKey; import com.exonum.binding.common.crypto.PublicKey; import com.exonum.binding.common.hash.HashCode; import com.exonum.binding.common.message.TransactionMessage; import com.exonum.binding.core.service.Schema; import com.exonum.binding.core.storage.database.Snapshot; import com.exonum.binding.core.storage.indices.MapIndex; +import com.exonum.binding.messages.Blockchain.Config; +import com.exonum.binding.messages.Blockchain.ValidatorKeys; import com.exonum.binding.qaservice.transactions.UnknownTx; import com.exonum.binding.test.RequiresNativeLibrary; import com.exonum.binding.testkit.EmulatedNode; @@ -180,21 +179,19 @@ void afterCommit(TestKit testKit) { } @Test - void getActualConfiguration(@ValidatorCount(NEW_VALIDATOR_COUNT) TestKit testKit) { + void getConsensusConfiguration(@ValidatorCount(NEW_VALIDATOR_COUNT) TestKit testKit) { QaServiceImpl service = testKit.getService(QaService.ID, QaServiceImpl.class); - StoredConfiguration configuration = service.getActualConfiguration(); + Config configuration = service.getConsensusConfiguration(); - HashCode expectedPreviousCfgHash = HashCode.fromBytes(new byte[DEFAULT_HASH_SIZE_BYTES]); - assertThat(configuration.previousCfgHash()).isEqualTo(expectedPreviousCfgHash); + assertThat(configuration.getValidatorKeysCount()).isEqualTo(NEW_VALIDATOR_COUNT); EmulatedNode emulatedNode = testKit.getEmulatedNode(); - List validatorKeys = configuration.validatorKeys(); - - assertThat(validatorKeys).hasSize(NEW_VALIDATOR_COUNT); - PublicKey emulatedNodeServiceKey = emulatedNode.getServiceKeyPair().getPublicKey(); - List serviceKeys = configuration.validatorKeys().stream() - .map(ValidatorKey::serviceKey) + List serviceKeys = configuration.getValidatorKeysList().stream() + .map(ValidatorKeys::getServiceKey) + // fixme: [ECR-3734] highly error-prone and verbose key#getData.toByteArray susceptible + // to incorrect key#toByteArray. + .map(key -> PublicKey.fromBytes(key.getData().toByteArray())) .collect(toList()); assertThat(serviceKeys).hasSize(NEW_VALIDATOR_COUNT); diff --git a/exonum-java-binding/testkit/src/test/java/com/exonum/binding/testkit/TestKitParameterizationTest.java b/exonum-java-binding/testkit/src/test/java/com/exonum/binding/testkit/TestKitParameterizationTest.java index 52559418a1..5b2fdd61ea 100644 --- a/exonum-java-binding/testkit/src/test/java/com/exonum/binding/testkit/TestKitParameterizationTest.java +++ b/exonum-java-binding/testkit/src/test/java/com/exonum/binding/testkit/TestKitParameterizationTest.java @@ -21,7 +21,6 @@ import com.exonum.binding.core.blockchain.Blockchain; import com.exonum.binding.core.storage.database.Snapshot; import java.util.function.Consumer; - import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -59,7 +58,7 @@ void testTestKitValidatorCount(@ValidatorCount(NEW_VALIDATOR_COUNT) TestKit test private static Consumer verifyNumValidators(int expected) { return (view) -> { Blockchain blockchain = Blockchain.newInstance(view); - assertThat(blockchain.getActualConfiguration().validatorKeys().size()) + assertThat(blockchain.getConsensusConfiguration().getValidatorKeysCount()) .isEqualTo(expected); }; } diff --git a/exonum-java-binding/testkit/src/test/java/com/exonum/binding/testkit/TestKitTest.java b/exonum-java-binding/testkit/src/test/java/com/exonum/binding/testkit/TestKitTest.java index b9c824a066..d58f9f139d 100644 --- a/exonum-java-binding/testkit/src/test/java/com/exonum/binding/testkit/TestKitTest.java +++ b/exonum-java-binding/testkit/src/test/java/com/exonum/binding/testkit/TestKitTest.java @@ -186,7 +186,7 @@ void createTestKitWithSeveralValidators() { .build()) { Snapshot view = testKit.getSnapshot(); Blockchain blockchain = Blockchain.newInstance(view); - assertThat(blockchain.getActualConfiguration().validatorKeys().size()) + assertThat(blockchain.getConsensusConfiguration().getValidatorKeysCount()) .isEqualTo(validatorCount); } } @@ -201,7 +201,7 @@ void createTestKitWithAuditorAndAdditionalValidators() { .build()) { Snapshot view = testKit.getSnapshot(); Blockchain blockchain = Blockchain.newInstance(view); - assertThat(blockchain.getActualConfiguration().validatorKeys().size()) + assertThat(blockchain.getConsensusConfiguration().getValidatorKeysCount()) .isEqualTo(validatorCount); } }