From 44764e545a46f3152f4ba885e6f17e60dfc3bc6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Garillot?= Date: Wed, 17 Jul 2019 11:06:27 -0700 Subject: [PATCH] [types, consensus, crypto] Converting validator_(verifier|signer) and dependencies to the new API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## This diff modifies this behaviour: - **Makes `ValidatorSigner`, `ValidatorVerifier` and their dependencies use the new API,** + most of the changes are in `types/validator_(signer|verifier).rs`, the rest is propagation - Hides terms that should only be used in tests from our binaries, through conditional compilation + those are mostly `Clone` of `PrivateKey`s and `Arbitrary` impls, plus proptest strategies + this uses a `#[cfg(any(test, feature="testing")]` flag. `cfg(test)` works within-crate, `cfg(feature="testing")` helps transfer out-of-crate dependencies. + Introduces corresponding `"testing"` feature flag on a couple of lower crates (nextgen_crypto, types) so that you can import crates under a test context. - `Validator(Verifier|Signer)::random()`, and `ValidatorSigner::new()` constructor now take [optional arguments](http://xion.io/post/code/rust-optional-args.html) reflecting a default value. - creating a `ValidatorSigner` does not need the public key any more and deduces it from the private key at constructor call (and then stores it). - there's a compatibility `generate_keypair` for tests that has an optional pluggable rng argument with safe default, ## Why this is better: - **`Validator(Verifier|Signer)` are safer, cryptographically agile, and expose one single true signing API which implementation is managed in the crypto module, irrespective of scheme.** - random tests based on cryptographic material are deterministic (customizable rng with safe, set default) - Cloning of Private Keys now can't happen outside of tests - proptest-related terms and strategies do not bloat our binaries any more ## Why this is worse: - temporarily this reintroduces a bincode dependency (end of `ed25519::compat`) - there are ugly Legacy <-> Nextgen key material conversions, esp in `libra_node`, (parts of) `config`, `network` ## Future improvements: - pull `libra_node`, `config`, `network` into this brave `nextgen_crypto` new world - in this temporary state, Cloning of Private Keys still happen in tests — much can be removed ## TODO in next diffs: - make a few more structures generic in a private / public Key parameter, esp. NodeConfig --- client/Cargo.toml | 1 + client/src/client_proxy.rs | 7 +- client/src/grpc_client.rs | 9 +- config/config_builder/Cargo.toml | 1 + config/config_builder/src/util.rs | 8 +- consensus/Cargo.toml | 9 + .../chained_bft/block_storage/block_store.rs | 7 +- .../block_storage/block_store_test.rs | 30 +-- .../chained_bft_consensus_provider.rs | 16 +- consensus/src/chained_bft/chained_bft_smr.rs | 144 ++++++------- .../src/chained_bft/chained_bft_smr_test.rs | 21 +- .../src/chained_bft/consensus_types/block.rs | 21 +- .../chained_bft/consensus_types/block_test.rs | 43 ++-- .../consensus_types/quorum_cert.rs | 7 +- .../src/chained_bft/event_processor_test.rs | 13 +- .../liveness/local_pacemaker_test.rs | 7 +- .../pacemaker_timeout_manager_test.rs | 8 +- .../chained_bft/liveness/proposer_election.rs | 3 +- .../liveness/rotating_proposer_test.rs | 13 +- .../src/chained_bft/liveness/timeout_msg.rs | 35 ++-- consensus/src/chained_bft/network.rs | 7 +- consensus/src/chained_bft/network_tests.rs | 11 +- consensus/src/chained_bft/proto_test.rs | 5 +- .../chained_bft/safety/safety_rules_test.rs | 20 +- consensus/src/chained_bft/safety/vote_msg.rs | 16 +- consensus/src/chained_bft/test_utils/mod.rs | 17 +- consensus/src/lib.rs | 1 + consensus/src/state_synchronizer/sync_test.rs | 11 +- crypto/legacy_crypto/src/lib.rs | 1 - crypto/nextgen_crypto/Cargo.toml | 4 + crypto/nextgen_crypto/src/ed25519.rs | 73 ++++++- crypto/nextgen_crypto/src/test_utils.rs | 31 ++- execution/execution_proto/Cargo.toml | 4 + execution/execution_proto/src/lib.rs | 19 +- language/vm/vm_genesis/Cargo.toml | 4 + libra_node/src/main_node.rs | 5 +- network/Cargo.toml | 5 + network/benches/network_bench.rs | 26 ++- network/src/common.rs | 5 +- network/src/connectivity_manager/test.rs | 7 +- network/src/protocols/discovery/mod.rs | 27 ++- network/src/protocols/discovery/test.rs | 15 +- network/src/validator_network/consensus.rs | 2 +- .../src/validator_network/network_builder.rs | 14 +- network/src/validator_network/test.rs | 39 ++-- types/Cargo.toml | 9 + types/src/account_address.rs | 28 ++- types/src/get_with_proof.rs | 5 +- types/src/ledger_info.rs | 6 +- types/src/validator_public_keys.rs | 38 ++-- types/src/validator_set.rs | 4 +- types/src/validator_signer.rs | 191 +++++++++--------- types/src/validator_verifier.rs | 109 ++++++---- 53 files changed, 710 insertions(+), 452 deletions(-) diff --git a/client/Cargo.toml b/client/Cargo.toml index e88d71c13f89..949f052c4dec 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -28,6 +28,7 @@ admission_control_proto = { version = "0.1.0", path = "../admission_control/admi config = { path = "../config" } crash_handler = { path = "../common/crash_handler" } crypto = { path = "../crypto/legacy_crypto" } +nextgen_crypto = { path = "../crypto/nextgen_crypto" } failure = { package = "failure_ext", path = "../common/failure_ext" } libc = "0.2.48" libra_wallet = { path = "./libra_wallet" } diff --git a/client/src/client_proxy.rs b/client/src/client_proxy.rs index 37e76367e40a..16710f9a432c 100644 --- a/client/src/client_proxy.rs +++ b/client/src/client_proxy.rs @@ -10,6 +10,7 @@ use futures::{future::Future, stream::Stream}; use hyper; use libra_wallet::{io_utils, wallet_library::WalletLibrary}; use logger::prelude::*; +use nextgen_crypto::ed25519::Ed25519PublicKey; use num_traits::{ cast::{FromPrimitive, ToPrimitive}, identities::Zero, @@ -117,7 +118,11 @@ impl ClientProxy { ); // Total 3f + 1 validators, 2f + 1 correct signatures are required. // If < 4 validators, all validators have to agree. - let validator_verifier = Arc::new(ValidatorVerifier::new(validators)); + let validator_pubkeys: HashMap = validators + .into_iter() + .map(|(key, value)| (key, value.into())) + .collect(); + let validator_verifier = Arc::new(ValidatorVerifier::new(validator_pubkeys)); let client = GRPCClient::new(host, ac_port, validator_verifier)?; let accounts = vec![]; diff --git a/client/src/grpc_client.rs b/client/src/grpc_client.rs index 7113ce8da9ba..88c7ccaaef8a 100644 --- a/client/src/grpc_client.rs +++ b/client/src/grpc_client.rs @@ -15,6 +15,7 @@ use failure::prelude::*; use futures::Future; use grpcio::{CallOption, ChannelBuilder, EnvBuilder}; use logger::prelude::*; +use nextgen_crypto::ed25519::*; use proto_conv::{FromProto, IntoProto}; use std::sync::Arc; use types::{ @@ -36,12 +37,16 @@ const MAX_GRPC_RETRY_COUNT: u64 = 1; /// Struct holding dependencies of client. pub struct GRPCClient { client: AdmissionControlClient, - validator_verifier: Arc, + validator_verifier: Arc>, } impl GRPCClient { /// Construct a new Client instance. - pub fn new(host: &str, port: &str, validator_verifier: Arc) -> Result { + pub fn new( + host: &str, + port: &str, + validator_verifier: Arc>, + ) -> Result { let conn_addr = format!("{}:{}", host, port); // Create a GRPC client diff --git a/config/config_builder/Cargo.toml b/config/config_builder/Cargo.toml index ec94505bc301..b4403f6962b7 100644 --- a/config/config_builder/Cargo.toml +++ b/config/config_builder/Cargo.toml @@ -16,6 +16,7 @@ toml = "0.4" config = { path = ".." } crypto = { path = "../../crypto/legacy_crypto" } +nextgen_crypto = { path = "../../crypto/nextgen_crypto" } failure = { path = "../../common/failure_ext", package = "failure_ext" } generate_keypair = { path = "../generate_keypair" } proto_conv = { path = "../../common/proto_conv", features = ["derive"] } diff --git a/config/config_builder/src/util.rs b/config/config_builder/src/util.rs index 228d8412aa2c..0526403dd080 100644 --- a/config/config_builder/src/util.rs +++ b/config/config_builder/src/util.rs @@ -5,7 +5,7 @@ use config::{ config::{NodeConfig, NodeConfigHelpers}, trusted_peers::{TrustedPeersConfig, TrustedPeersConfigHelpers}, }; -use crypto::signing::KeyPair; +use crypto::signing::{self, KeyPair}; use failure::prelude::*; use proto_conv::IntoProtoBytes; use std::{convert::TryFrom, fs::File, io::prelude::*, path::Path}; @@ -23,8 +23,8 @@ pub fn gen_genesis_transaction>( .map(|(peer_id, peer)| { ValidatorPublicKeys::new( AccountAddress::try_from(peer_id.clone()).expect("[config] invalid peer_id"), - peer.get_consensus_public(), - peer.get_network_signing_public(), + peer.get_consensus_public().into(), + peer.get_network_signing_public().into(), peer.get_network_identity_public(), ) }) @@ -43,7 +43,7 @@ pub fn gen_genesis_transaction>( pub fn get_test_config() -> (NodeConfig, KeyPair) { // TODO: test config should be moved here instead of config crate let config = NodeConfigHelpers::get_single_node_test_config(true); - let (private_key, _) = ::crypto::signing::generate_keypair(); + let (private_key, _) = signing::generate_keypair(); let keypair = KeyPair::new(private_key); gen_genesis_transaction( diff --git a/consensus/Cargo.toml b/consensus/Cargo.toml index a0f51c87dbfe..e31238f8352c 100644 --- a/consensus/Cargo.toml +++ b/consensus/Cargo.toml @@ -28,6 +28,7 @@ canonical_serialization = { path = "../common/canonical_serialization" } channel = { path = "../common/channel" } config = { path = "../config" } crypto = { path = "../crypto/legacy_crypto" } +nextgen_crypto = { path = "../crypto/nextgen_crypto" } execution_proto = { path = "../execution/execution_proto" } failure = { path = "../common/failure_ext", package = "failure_ext" } grpc_helpers = { path = "../common/grpc_helpers" } @@ -61,3 +62,11 @@ execution_service = { path = "../execution/execution_service" } storage_service = { path = "../storage/storage_service" } vm_genesis = { path = "../language/vm/vm_genesis" } vm_validator = { path = "../vm_validator" } + +[dev-dependencies.nextgen_crypto] +features = ["testing"] +path = "../crypto/nextgen_crypto" + +[dev-dependencies.types] +features = ["testing"] +path = "../types" diff --git a/consensus/src/chained_bft/block_storage/block_store.rs b/consensus/src/chained_bft/block_storage/block_store.rs index 15d61ce2fa7a..10b6129982ba 100644 --- a/consensus/src/chained_bft/block_storage/block_store.rs +++ b/consensus/src/chained_bft/block_storage/block_store.rs @@ -19,6 +19,7 @@ use logger::prelude::*; use crate::{chained_bft::persistent_storage::RecoveryData, state_replication::StateComputeResult}; use crypto::hash::CryptoHash; use mirai_annotations::checked_precondition; +use nextgen_crypto::ed25519::*; use std::{ collections::{vec_deque::VecDeque, HashMap}, sync::{Arc, RwLock}, @@ -56,7 +57,7 @@ pub enum NeedFetchResult { /// | -------------> D3 pub struct BlockStore { inner: Arc>>, - validator_signer: ValidatorSigner, + validator_signer: ValidatorSigner, state_computer: Arc>, enforce_increasing_timestamps: bool, /// The persistent storage backing up the in-memory data structure, every write should go @@ -68,7 +69,7 @@ impl BlockStore { pub async fn new( storage: Arc>, initial_data: RecoveryData, - validator_signer: ValidatorSigner, + validator_signer: ValidatorSigner, state_computer: Arc>, enforce_increasing_timestamps: bool, max_pruned_blocks_in_mem: usize, @@ -160,7 +161,7 @@ impl BlockStore { *self.inner.write().unwrap() = tree; } - pub fn signer(&self) -> &ValidatorSigner { + pub fn signer(&self) -> &ValidatorSigner { &self.validator_signer } diff --git a/consensus/src/chained_bft/block_storage/block_store_test.rs b/consensus/src/chained_bft/block_storage/block_store_test.rs index 6a9214736bf3..8a22f92c7366 100644 --- a/consensus/src/chained_bft/block_storage/block_store_test.rs +++ b/consensus/src/chained_bft/block_storage/block_store_test.rs @@ -16,6 +16,7 @@ use crate::chained_bft::{ }; use crypto::HashValue; use futures::executor::block_on; +use nextgen_crypto::{ed25519::*, *}; use proptest::prelude::*; use std::{cmp::min, collections::HashSet, sync::Arc}; use types::{account_address::AccountAddress, validator_signer::ValidatorSigner}; @@ -162,16 +163,16 @@ proptest! { #[test] fn test_block_store_insert( - (keypairs, blocks) in block_test::block_forest_and_its_keys( + (mut private_keys, blocks) in block_test::block_forest_and_its_keys( // quorum size 10, // recursion depth 50) ){ - let authors: HashSet = keypairs.iter().map(|(_, public_key)| AccountAddress::from(*public_key)).collect(); - let (priv_key, pub_key) = keypairs.first().expect("several keypairs generated"); - let signer = ValidatorSigner::new(AccountAddress::from(*pub_key), *pub_key, priv_key.clone()); - let block_store = build_empty_tree_with_custom_signing(signer.clone()); + let authors: HashSet = private_keys.iter().map(|private_key| AccountAddress::from_public_key(&private_key.public_key())).collect(); + let priv_key = private_keys.pop().expect("several keypairs generated"); + let signer = ValidatorSigner::new(None, priv_key); + let block_store = build_empty_tree_with_custom_signing(signer); for block in blocks { if block.round() > 0 && authors.contains(&block.author()) { let known_parent = block_store.block_exists(block.parent_id()); @@ -312,17 +313,18 @@ fn test_insert_vote() { let qc_size = 10; let mut signers = vec![]; let mut author_public_keys = vec![]; - for _ in 0..qc_size { - let signer = ValidatorSigner::random(); + + for i in 0..qc_size { + let signer = ValidatorSigner::::random([i as u8; 32]); author_public_keys.push(( - AccountAddress::from(signer.public_key()), + AccountAddress::from_public_key(&signer.public_key()), signer.public_key(), )); signers.push(signer); } - let my_signer = ValidatorSigner::random(); + let my_signer = ValidatorSigner::random([qc_size as u8; 32]); author_public_keys.push(( - AccountAddress::from(my_signer.public_key()), + AccountAddress::from_public_key(&my_signer.public_key()), my_signer.public_key(), )); let block_store = build_empty_tree_with_custom_signing(my_signer); @@ -425,13 +427,13 @@ fn test_need_fetch_for_qc() { let a3 = inserter.insert_block(a2.as_ref(), 3); block_on(block_tree.prune_tree(a2.id())); let need_fetch_qc = placeholder_certificate_for_block( - vec![block_tree.signer().clone()], + vec![block_tree.signer()], HashValue::zero(), a3.round() + 1, ); let too_old_qc = QuorumCert::certificate_for_genesis(); let can_insert_qc = - placeholder_certificate_for_block(vec![block_tree.signer().clone()], a3.id(), a3.round()); + placeholder_certificate_for_block(vec![block_tree.signer()], a3.id(), a3.round()); let duplicate_qc = block_tree.get_quorum_cert_for_block(a2.id()).unwrap(); assert_eq!( block_tree.need_fetch_for_quorum_cert(&need_fetch_qc), @@ -464,7 +466,7 @@ fn test_need_sync_for_qc() { let a3 = inserter.insert_block(a2.as_ref(), 3); block_on(block_tree.prune_tree(a3.id())); let qc = placeholder_certificate_for_block( - vec![block_tree.signer().clone()], + vec![block_tree.signer()], HashValue::zero(), a3.round() + 3, ); @@ -473,7 +475,7 @@ fn test_need_sync_for_qc() { true ); let qc = placeholder_certificate_for_block( - vec![block_tree.signer().clone()], + vec![block_tree.signer()], HashValue::zero(), a3.round() + 2, ); diff --git a/consensus/src/chained_bft/chained_bft_consensus_provider.rs b/consensus/src/chained_bft/chained_bft_consensus_provider.rs index 18a87d35d0af..6daa825f46a3 100644 --- a/consensus/src/chained_bft/chained_bft_consensus_provider.rs +++ b/consensus/src/chained_bft/chained_bft_consensus_provider.rs @@ -13,6 +13,7 @@ use crate::{ txn_manager::MempoolProxy, }; use network::validator_network::{ConsensusNetworkEvents, ConsensusNetworkSender}; +use nextgen_crypto::ed25519::*; use crate::{ chained_bft::{ @@ -34,10 +35,10 @@ use types::{ struct InitialSetup { author: Author, - signer: ValidatorSigner, + signer: ValidatorSigner, quorum_size: usize, peers: Arc>, - validator: Arc, + validator: Arc>, } /// Supports the implementation of ConsensusProvider using LibraBFT. @@ -115,16 +116,21 @@ impl ChainedBftProvider { let author = AccountAddress::try_from(peer_id_str).expect("Failed to parse peer id of a validator"); let private_key = node_config.base.peer_keypairs.get_consensus_private(); - let public_key = node_config.base.peer_keypairs.get_consensus_public(); - let signer = ValidatorSigner::new(author, public_key, private_key); + let _public_key = node_config.base.peer_keypairs.get_consensus_public(); + let signer = ValidatorSigner::new(author, private_key.into()); let peers_with_public_keys = node_config.base.trusted_peers.get_trusted_consensus_peers(); + let peers_with_nextgen_public_keys = peers_with_public_keys + .clone() + .into_iter() + .map(|(k, v)| (AccountAddress::clone(&k), v.into())) + .collect(); let peers = Arc::new( peers_with_public_keys .keys() .map(AccountAddress::clone) .collect(), ); - let validator = Arc::new(ValidatorVerifier::new(peers_with_public_keys)); + let validator = Arc::new(ValidatorVerifier::new(peers_with_nextgen_public_keys)); counters::EPOCH_NUM.set(0); // No reconfiguration yet, so it is always zero counters::CURRENT_EPOCH_NUM_VALIDATORS.set(validator.len() as i64); counters::CURRENT_EPOCH_QUORUM_SIZE.set(validator.quorum_size() as i64); diff --git a/consensus/src/chained_bft/chained_bft_smr.rs b/consensus/src/chained_bft/chained_bft_smr.rs index 619122b4af6a..9a740594c767 100644 --- a/consensus/src/chained_bft/chained_bft_smr.rs +++ b/consensus/src/chained_bft/chained_bft_smr.rs @@ -34,6 +34,7 @@ use futures::{ future::{FutureExt, TryFutureExt}, stream::StreamExt, }; +use nextgen_crypto::ed25519::*; use types::validator_signer::ValidatorSigner; use config::config::ConsensusConfig; @@ -78,7 +79,7 @@ pub struct ChainedBftSMR { author: P, // TODO [Reconfiguration] quorum size is just a function of current validator set. quorum_size: usize, - signer: ValidatorSigner, + signer: Option>, proposers: Vec

, runtime: Option, block_store: Option>>, @@ -93,7 +94,7 @@ impl ChainedBftSMR { pub fn new( author: P, quorum_size: usize, - signer: ValidatorSigner, + signer: ValidatorSigner, proposers: Vec

, network: ConsensusNetworkImpl, runtime: Runtime, @@ -104,7 +105,7 @@ impl ChainedBftSMR { Self { author, quorum_size, - signer, + signer: Some(signer), proposers, runtime: Some(runtime), block_store: None, @@ -437,72 +438,79 @@ impl StateMachineReplication for ChainedBftSMR, + signer: ValidatorSigner, + validator: Arc>, peers: Arc>, proposer: Vec, smr_id: usize, @@ -50,8 +52,8 @@ impl SMRNode { fn start( quorum_size: usize, playground: &mut NetworkPlayground, - signer: ValidatorSigner, - validator: Arc, + signer: ValidatorSigner, + validator: Arc>, peers: Arc>, proposer: Vec, smr_id: usize, @@ -159,13 +161,8 @@ impl SMRNode { ValidatorVerifier::new_with_quorum_size(author_to_public_keys, quorum_size) .expect("Invalid quorum_size."), ); - let peers: Arc> = Arc::new( - signers - .clone() - .into_iter() - .map(|signer| signer.author()) - .collect(), - ); + let peers: Arc> = + Arc::new(signers.iter().map(|signer| signer.author()).collect()); let proposer = { match proposer_type { FixedProposer => vec![peers[0]], @@ -197,7 +194,7 @@ fn verify_finality_proof(node: &SMRNode, ledger_info_with_sig: &LedgerInfoWithSi assert_eq!( Ok(()), node.validator - .verify_signature(*author, ledger_info_hash, signature) + .verify_signature(*author, ledger_info_hash, &(signature.clone().into())) ); } } diff --git a/consensus/src/chained_bft/consensus_types/block.rs b/consensus/src/chained_bft/consensus_types/block.rs index a6c337d0be04..f16beaf364b1 100644 --- a/consensus/src/chained_bft/consensus_types/block.rs +++ b/consensus/src/chained_bft/consensus_types/block.rs @@ -14,11 +14,12 @@ use canonical_serialization::{ }; use crypto::{ hash::{BlockHasher, CryptoHash, CryptoHasher, GENESIS_BLOCK_ID}, - HashValue, Signature, + HashValue, }; use failure::Result; use mirai_annotations::{checked_precondition, checked_precondition_eq}; use network::proto::Block as ProtoBlock; +use nextgen_crypto::ed25519::*; use proto_conv::{FromProto, IntoProto}; use rmp_serde::{from_slice, to_vec_named}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; @@ -84,7 +85,7 @@ pub struct Block { /// Author of the block that can be validated by the author's public key and the signature author: Author, /// Signature that the hash of this block has been authored by the owner of the private key - signature: Signature, + signature: Ed25519Signature, } impl Display for Block { @@ -104,7 +105,7 @@ where // Make an empty genesis block pub fn make_genesis_block() -> Self { let ancestor_id = HashValue::zero(); - let genesis_validator_signer = ValidatorSigner::genesis(); + let genesis_validator_signer = ValidatorSigner::::genesis(); let state = ExecutedState::state_for_genesis(); // Genesis carries a placeholder quorum certificate to its parent id with LedgerInfo // carrying information about version `0`. @@ -138,7 +139,7 @@ where timestamp_usecs: 0, // The beginning of UNIX TIME quorum_cert: genesis_quorum_cert, author: genesis_validator_signer.author(), - signature, + signature: signature.into(), } } @@ -151,7 +152,7 @@ where height: Height, timestamp_usecs: u64, quorum_cert: QuorumCert, - validator_signer: &ValidatorSigner, + validator_signer: &ValidatorSigner, ) -> Self { let block_internal = BlockSerializer { parent_id, @@ -187,7 +188,7 @@ where round: Round, timestamp_usecs: u64, quorum_cert: QuorumCert, - validator_signer: &ValidatorSigner, + validator_signer: &ValidatorSigner, ) -> Self { // A block must carry a QC to its parent. checked_precondition_eq!(quorum_cert.certified_block_id(), parent_block.id()); @@ -210,7 +211,7 @@ where pub fn verify( &self, - validator: &ValidatorVerifier, + validator: &ValidatorVerifier, ) -> ::std::result::Result<(), BlockVerificationError> { if self.is_genesis_block() { return Ok(()); @@ -251,7 +252,7 @@ where self.author } - pub fn signature(&self) -> &Signature { + pub fn signature(&self) -> &Ed25519Signature { &self.signature } @@ -354,7 +355,7 @@ where proto.set_round(self.round()); proto.set_height(self.height()); proto.set_quorum_cert(self.quorum_cert().clone().into_proto()); - proto.set_signature(self.signature().to_compact().as_ref().into()); + proto.set_signature(self.signature().to_bytes().as_ref().into()); proto.set_author(self.author.into()); proto } @@ -375,7 +376,7 @@ where let height = object.get_height(); let quorum_cert = QuorumCert::from_proto(object.take_quorum_cert())?; let author = Author::try_from(object.take_author())?; - let signature = Signature::from_compact(object.get_signature())?; + let signature = Ed25519Signature::try_from(object.get_signature())?; Ok(Block { id, parent_id, diff --git a/consensus/src/chained_bft/consensus_types/block_test.rs b/consensus/src/chained_bft/consensus_types/block_test.rs index 19a5dd95271e..467a4b7d0d3f 100644 --- a/consensus/src/chained_bft/consensus_types/block_test.rs +++ b/consensus/src/chained_bft/consensus_types/block_test.rs @@ -7,13 +7,16 @@ use crate::chained_bft::{ test_utils::placeholder_certificate_for_block, }; -use crypto::{HashValue, PrivateKey, PublicKey}; +use crypto::HashValue; +use nextgen_crypto::ed25519::Ed25519PrivateKey; use proptest::prelude::*; use std::{ panic, time::{Duration, SystemTime, UNIX_EPOCH}, }; -use types::validator_signer::{self, ValidatorSigner}; +#[cfg(test)] +use types::validator_signer::proptests; +use types::validator_signer::ValidatorSigner; type LinearizedBlockForest = Vec>; @@ -27,7 +30,7 @@ prop_compose! { parent_id_strategy: impl Strategy, round_strategy: impl Strategy, height: Height, - signer_strategy: impl Strategy, + signer_strategy: impl Strategy>, )( parent_id in parent_id_strategy, round in round_strategy, @@ -62,7 +65,7 @@ prop_compose! { HashValue::arbitrary(), Round::arbitrary(), 123, - validator_signer::arb_signer(), + proptests::arb_signer(), ) ) -> Block> { block @@ -89,7 +92,7 @@ prop_compose! { parent_id: block.parent_id(), quorum_cert: block.quorum_cert().clone(), author: block.author(), - signature: *block.signature(), + signature: block.signature().clone(), } } } @@ -117,7 +120,7 @@ prop_compose! { /// of the parent. This, depending on branching, does not require the /// QC to always be an ancestor or the parent to always be the highest QC fn child( - signer_strategy: impl Strategy, + signer_strategy: impl Strategy>, block_forest_strategy: impl Strategy>>, )( signer in signer_strategy, @@ -151,17 +154,14 @@ prop_compose! { /// vector fn block_forest_from_keys( depth: u32, - key_pairs: Vec<(PrivateKey, PublicKey)>, + keypairs: Vec, ) -> impl Strategy>> { let leaf = leaf_strategy().prop_map(|block| vec![block]); // Note that having `expected_branch_size` of 1 seems to generate significantly larger trees // than desired (this is my understanding after reading the documentation: // https://docs.rs/proptest/0.3.0/proptest/strategy/trait.Strategy.html#method.prop_recursive) leaf.prop_recursive(depth, depth, 2, move |inner| { - child( - validator_signer::mostly_in_keypair_pool(key_pairs.clone()), - inner, - ) + child(proptests::mostly_in_keypair_pool(keypairs.clone()), inner) }) } @@ -169,17 +169,12 @@ fn block_forest_from_keys( pub fn block_forest_and_its_keys( quorum_size: usize, depth: u32, -) -> impl Strategy< - Value = ( - Vec<(PrivateKey, PublicKey)>, - LinearizedBlockForest>, - ), -> { - proptest::collection::vec(validator_signer::arb_keypair(), quorum_size).prop_flat_map( - move |key_pairs| { +) -> impl Strategy, LinearizedBlockForest>)> { + proptest::collection::vec(proptests::arb_signing_key(), quorum_size).prop_flat_map( + move |private_key| { ( - Just(key_pairs.clone()), - block_forest_from_keys(depth, key_pairs), + Just(private_key.clone()), + block_forest_from_keys(depth, private_key), ) }, ) @@ -197,7 +192,7 @@ fn test_genesis() { #[test] fn test_block_relation() { - let signer = ValidatorSigner::random(); + let signer = ValidatorSigner::random(None); // Test genesis and the next block let genesis_block = Block::make_genesis_block(); let quorum_cert = QuorumCert::certificate_for_genesis(); @@ -226,7 +221,7 @@ fn test_block_relation() { #[test] fn test_block_qc() { // Verify that it's impossible to create a block with QC that doesn't point to a parent. - let signer = ValidatorSigner::random(); + let signer = ValidatorSigner::random(None); // Test genesis and the next block let genesis_block = Block::make_genesis_block(); let genesis_qc = QuorumCert::certificate_for_genesis(); @@ -240,7 +235,7 @@ fn test_block_qc() { genesis_qc.clone(), &signer, ); - let a1_qc = placeholder_certificate_for_block(vec![signer.clone()], a1.id(), a1.round()); + let a1_qc = placeholder_certificate_for_block(vec![&signer], a1.id(), a1.round()); let result = panic::catch_unwind(|| { // should panic because qc does not point to parent diff --git a/consensus/src/chained_bft/consensus_types/quorum_cert.rs b/consensus/src/chained_bft/consensus_types/quorum_cert.rs index 786cebbb107b..cc67cec8f156 100644 --- a/consensus/src/chained_bft/consensus_types/quorum_cert.rs +++ b/consensus/src/chained_bft/consensus_types/quorum_cert.rs @@ -14,6 +14,7 @@ use crypto::{ }; use failure::Result; use network::proto::QuorumCert as ProtoQuorumCert; +use nextgen_crypto::ed25519::*; use proto_conv::{FromProto, IntoProto}; use serde::{Deserialize, Serialize}; use std::{ @@ -97,7 +98,7 @@ impl QuorumCert { pub fn certificate_for_genesis() -> QuorumCert { let genesis_digest = VoteMsg::vote_digest(*GENESIS_BLOCK_ID, ExecutedState::state_for_genesis(), 0); - let signer = ValidatorSigner::genesis(); + let signer = ValidatorSigner::::genesis(); let li = LedgerInfo::new( 0, *ACCUMULATOR_PLACEHOLDER_HASH, @@ -110,7 +111,7 @@ impl QuorumCert { .sign_message(li.hash()) .expect("Fail to sign genesis ledger info"); let mut signatures = HashMap::new(); - signatures.insert(signer.author(), signature); + signatures.insert(signer.author(), signature.into()); QuorumCert::new( *GENESIS_BLOCK_ID, ExecutedState::state_for_genesis(), @@ -121,7 +122,7 @@ impl QuorumCert { pub fn verify( &self, - validator: &ValidatorVerifier, + validator: &ValidatorVerifier, ) -> ::std::result::Result<(), VoteMsgVerificationError> { let vote_hash = VoteMsg::vote_digest( self.certified_block_id, diff --git a/consensus/src/chained_bft/event_processor_test.rs b/consensus/src/chained_bft/event_processor_test.rs index 1f040ceba9b1..24bfe0f11a80 100644 --- a/consensus/src/chained_bft/event_processor_test.rs +++ b/consensus/src/chained_bft/event_processor_test.rs @@ -46,6 +46,7 @@ use network::{ proto::BlockRetrievalStatus, validator_network::{ConsensusNetworkEvents, ConsensusNetworkSender}, }; +use nextgen_crypto::ed25519::*; use proto_conv::FromProto; use std::{ collections::HashMap, @@ -69,7 +70,7 @@ struct NodeSetup { new_rounds_receiver: channel::Receiver, winning_proposals_receiver: channel::Receiver>, storage: Arc>, - signer: ValidatorSigner, + signer: ValidatorSigner, proposer_author: Author, peers: Arc>, pacemaker: Arc, @@ -78,7 +79,7 @@ struct NodeSetup { impl NodeSetup { fn build_empty_store( - signer: ValidatorSigner, + signer: ValidatorSigner, storage: Arc>, initial_data: RecoveryData, ) -> Arc> { @@ -146,8 +147,8 @@ impl NodeSetup { ) -> Vec { let mut signers = vec![]; let mut peers = vec![]; - for _ in 0..num_nodes { - let signer = ValidatorSigner::random(); + for i in 0..num_nodes { + let signer = ValidatorSigner::random([i as u8; 32]); peers.push(signer.author()); signers.push(signer); } @@ -172,7 +173,7 @@ impl NodeSetup { fn new( playground: &mut NetworkPlayground, executor: TaskExecutor, - signer: ValidatorSigner, + signer: ValidatorSigner, proposer_author: Author, peers: Arc>, storage: Arc>, @@ -543,7 +544,7 @@ fn process_new_round_msg_test() { // Populate block_0 and a quorum certificate for block_0 on non_proposer let block_0_quorum_cert = placeholder_certificate_for_block( - vec![static_proposer.signer.clone(), non_proposer.signer.clone()], + vec![&static_proposer.signer, &non_proposer.signer], block_0_id, 1, ); diff --git a/consensus/src/chained_bft/liveness/local_pacemaker_test.rs b/consensus/src/chained_bft/liveness/local_pacemaker_test.rs index fa52749f1bfc..db5e78a35de7 100644 --- a/consensus/src/chained_bft/liveness/local_pacemaker_test.rs +++ b/consensus/src/chained_bft/liveness/local_pacemaker_test.rs @@ -16,6 +16,7 @@ use crate::{ }; use channel; use futures::{executor::block_on, StreamExt}; +use nextgen_crypto::ed25519::*; use std::{sync::Arc, time::Duration, u64}; use tokio::runtime; use types::validator_signer::ValidatorSigner; @@ -72,9 +73,9 @@ fn test_basic_timeout() { fn test_timeout_certificate() { let runtime = consensus_runtime(); let rounds = 5; - let mut signers: Vec = vec![]; - for _round in 1..rounds { - let signer = ValidatorSigner::random(); + let mut signers: Vec> = vec![]; + for round in 1..rounds { + let signer = ValidatorSigner::::random([round as u8; 32]); signers.push(signer); } let (pm, mut new_round_events_receiver) = make_pacemaker(&runtime); diff --git a/consensus/src/chained_bft/liveness/pacemaker_timeout_manager_test.rs b/consensus/src/chained_bft/liveness/pacemaker_timeout_manager_test.rs index e21789aa9502..9e774fbdaea8 100644 --- a/consensus/src/chained_bft/liveness/pacemaker_timeout_manager_test.rs +++ b/consensus/src/chained_bft/liveness/pacemaker_timeout_manager_test.rs @@ -21,8 +21,8 @@ fn test_basic() { .persistent_liveness_storage(), ); assert_eq!(timeout_manager.highest_timeout_certificate(), None); - let validator_signer1 = ValidatorSigner::random(); - let validator_signer2 = ValidatorSigner::random(); + let validator_signer1 = ValidatorSigner::random([0u8; 32]); + let validator_signer2 = ValidatorSigner::random([1u8; 32]); // No timeout certificate generated on adding 2 timeouts from the same author let timeout_signer1_round1 = PacemakerTimeout::new(1, &validator_signer1); @@ -103,8 +103,8 @@ fn test_basic() { #[test] fn test_recovery_from_highest_timeout_certificate() { - let validator_signer1 = ValidatorSigner::random(); - let validator_signer2 = ValidatorSigner::random(); + let validator_signer1 = ValidatorSigner::random([0u8; 32]); + let validator_signer2 = ValidatorSigner::random([1u8; 32]); let timeout1 = PacemakerTimeout::new(10, &validator_signer1); let timeout2 = PacemakerTimeout::new(11, &validator_signer2); diff --git a/consensus/src/chained_bft/liveness/proposer_election.rs b/consensus/src/chained_bft/liveness/proposer_election.rs index d1299a376a9d..1315ea5d4e9b 100644 --- a/consensus/src/chained_bft/liveness/proposer_election.rs +++ b/consensus/src/chained_bft/liveness/proposer_election.rs @@ -9,6 +9,7 @@ use crate::chained_bft::{ use failure::Result; use futures::Future; use network::proto::Proposal as ProtoProposal; +use nextgen_crypto::ed25519::*; use proto_conv::{FromProto, IntoProto}; use rmp_serde::{from_slice, to_vec_named}; use serde::{de::DeserializeOwned, Serialize}; @@ -43,7 +44,7 @@ pub struct ProposalInfo { } impl ProposalInfo { - pub fn verify(&self, validator: &ValidatorVerifier) -> Result<()> { + pub fn verify(&self, validator: &ValidatorVerifier) -> Result<()> { self.proposal .verify(validator) .map_err(|e| format_err!("{:?}", e))?; diff --git a/consensus/src/chained_bft/liveness/rotating_proposer_test.rs b/consensus/src/chained_bft/liveness/rotating_proposer_test.rs index 8e90a0c47943..2c2ffeabea14 100644 --- a/consensus/src/chained_bft/liveness/rotating_proposer_test.rs +++ b/consensus/src/chained_bft/liveness/rotating_proposer_test.rs @@ -11,14 +11,15 @@ use crate::chained_bft::{ }; use channel; use futures::{executor::block_on, StreamExt}; +use nextgen_crypto::ed25519::*; use std::sync::Arc; use types::validator_signer::ValidatorSigner; #[test] fn test_rotating_proposer() { - let chosen_validator_signer = ValidatorSigner::random(); + let chosen_validator_signer = ValidatorSigner::::random([0u8; 32]); let chosen_author = chosen_validator_signer.author(); - let another_validator_signer = ValidatorSigner::random(); + let another_validator_signer = ValidatorSigner::::random([1u8; 32]); let another_author = another_validator_signer.author(); let proposers = vec![chosen_author, another_author]; let (winning_proposals_sender, winning_proposals_receiver) = channel::new_test(1_024); @@ -97,9 +98,9 @@ fn test_rotating_proposer() { #[test] fn test_rotating_proposer_with_three_contiguous_rounds() { - let chosen_validator_signer = ValidatorSigner::random(); + let chosen_validator_signer = ValidatorSigner::::random([0u8; 32]); let chosen_author = chosen_validator_signer.author(); - let another_validator_signer = ValidatorSigner::random(); + let another_validator_signer = ValidatorSigner::::random([1u8; 32]); let another_author = another_validator_signer.author(); let proposers = vec![chosen_author, another_author]; let (winning_proposals_sender, winning_proposals_receiver) = channel::new_test(1_024); @@ -175,9 +176,9 @@ fn test_rotating_proposer_with_three_contiguous_rounds() { #[test] fn test_fixed_proposer() { - let chosen_validator_signer = ValidatorSigner::random(); + let chosen_validator_signer = ValidatorSigner::::random([0u8; 32]); let chosen_author = chosen_validator_signer.author(); - let another_validator_signer = ValidatorSigner::random(); + let another_validator_signer = ValidatorSigner::::random([1u8; 32]); let another_author = another_validator_signer.author(); let (winning_proposals_sender, winning_proposals_receiver) = channel::new_test(1_024); let pe = Arc::new(RotatingProposer::::new( diff --git a/consensus/src/chained_bft/liveness/timeout_msg.rs b/consensus/src/chained_bft/liveness/timeout_msg.rs index ce01ad40b880..b4f0be50331d 100644 --- a/consensus/src/chained_bft/liveness/timeout_msg.rs +++ b/consensus/src/chained_bft/liveness/timeout_msg.rs @@ -9,9 +9,10 @@ use crate::chained_bft::{ use canonical_serialization::{CanonicalSerialize, CanonicalSerializer, SimpleSerializer}; use crypto::{ hash::{CryptoHash, CryptoHasher, PacemakerTimeoutHasher, TimeoutMsgHasher}, - HashValue, Signature, + HashValue, }; use network; +use nextgen_crypto::ed25519::*; use proto_conv::{FromProto, IntoProto}; use protobuf::RepeatedField; use serde::{Deserialize, Serialize}; @@ -55,12 +56,12 @@ impl CryptoHash for PacemakerTimeoutSerializer { pub struct PacemakerTimeout { round: Round, author: Author, - signature: Signature, + signature: Ed25519Signature, } impl PacemakerTimeout { /// Creates new PacemakerTimeout - pub fn new(round: Round, validator_signer: &ValidatorSigner) -> Self { + pub fn new(round: Round, validator_signer: &ValidatorSigner) -> Self { let author = validator_signer.author(); let digest = PacemakerTimeoutSerializer { round, author }.hash(); let signature = validator_signer @@ -87,7 +88,10 @@ impl PacemakerTimeout { } /// Verifies that this message has valid signature - pub fn verify(&self, validator: &ValidatorVerifier) -> Result<(), VerifyError> { + pub fn verify( + &self, + validator: &ValidatorVerifier, + ) -> Result<(), VerifyError> { validator.verify_signature(self.author, self.digest(), &self.signature) } @@ -97,7 +101,7 @@ impl PacemakerTimeout { } /// Returns the signature of the author for this timeout - pub fn signature(&self) -> &Signature { + pub fn signature(&self) -> &Ed25519Signature { &self.signature } } @@ -109,7 +113,7 @@ impl IntoProto for PacemakerTimeout { let mut proto = Self::ProtoType::new(); proto.set_round(self.round); proto.set_author(self.author.into()); - proto.set_signature(self.signature.to_compact().as_ref().into()); + proto.set_signature(self.signature.to_bytes().as_ref().into()); proto } } @@ -120,7 +124,7 @@ impl FromProto for PacemakerTimeout { fn from_proto(mut object: Self::ProtoType) -> failure::Result { let round = object.get_round(); let author = Author::try_from(object.take_author())?; - let signature = Signature::from_compact(object.get_signature())?; + let signature = Ed25519Signature::try_from(object.get_signature())?; Ok(PacemakerTimeout { round, author, @@ -171,7 +175,7 @@ pub struct TimeoutMsg { highest_ledger_info: QuorumCert, pacemaker_timeout: PacemakerTimeout, author: Author, - signature: Signature, + signature: Ed25519Signature, } impl TimeoutMsg { @@ -180,7 +184,7 @@ impl TimeoutMsg { highest_quorum_certificate: QuorumCert, highest_ledger_info: QuorumCert, pacemaker_timeout: PacemakerTimeout, - validator_signer: &ValidatorSigner, + validator_signer: &ValidatorSigner, ) -> TimeoutMsg { let author = validator_signer.author(); let digest = Self::new_round_digest( @@ -234,7 +238,10 @@ impl TimeoutMsg { } /// Verifies that this message has valid signature - pub fn verify(&self, validator: &ValidatorVerifier) -> Result<(), VerifyError> { + pub fn verify( + &self, + validator: &ValidatorVerifier, + ) -> Result<(), VerifyError> { validator.verify_signature(self.author, self.digest(), &self.signature)?; self.pacemaker_timeout.verify(validator) } @@ -246,7 +253,7 @@ impl TimeoutMsg { /// Returns a reference to the signature of the author #[allow(dead_code)] - pub fn signature(&self) -> &Signature { + pub fn signature(&self) -> &Ed25519Signature { &self.signature } } @@ -260,7 +267,7 @@ impl IntoProto for TimeoutMsg { proto.set_highest_ledger_info(self.highest_ledger_info.into_proto()); proto.set_pacemaker_timeout(self.pacemaker_timeout.into_proto()); proto.set_author(self.author.into()); - proto.set_signature(self.signature.to_compact().as_ref().into()); + proto.set_signature(self.signature.to_bytes().as_ref().into()); proto } } @@ -273,7 +280,7 @@ impl FromProto for TimeoutMsg { let highest_ledger_info = QuorumCert::from_proto(object.take_highest_ledger_info())?; let pacemaker_timeout = PacemakerTimeout::from_proto(object.take_pacemaker_timeout())?; let author = Author::try_from(object.take_author())?; - let signature = Signature::from_compact(object.get_signature())?; + let signature = Ed25519Signature::try_from(object.get_signature())?; Ok(TimeoutMsg { highest_quorum_certificate, highest_ledger_info, @@ -324,7 +331,7 @@ impl PacemakerTimeoutCertificate { /// Verifies that timeouts in message actually certify the round pub fn verify( &self, - validator: &ValidatorVerifier, + validator: &ValidatorVerifier, ) -> Result<(), PacemakerTimeoutCertificateVerificationError> { let mut min_round: Option = None; let mut unique_authors = HashSet::new(); diff --git a/consensus/src/chained_bft/network.rs b/consensus/src/chained_bft/network.rs index ad83a9da0ce4..a5c76d7f8201 100644 --- a/consensus/src/chained_bft/network.rs +++ b/consensus/src/chained_bft/network.rs @@ -27,6 +27,7 @@ use network::{ proto::{BlockRetrievalStatus, ConsensusMsg, RequestBlock, RespondBlock, RespondChunk}, validator_network::{ConsensusNetworkEvents, ConsensusNetworkSender, Event, RpcError}, }; +use nextgen_crypto::ed25519::*; use proto_conv::{FromProto, IntoProto}; use protobuf::Message; use std::{ @@ -109,7 +110,7 @@ pub struct ConsensusNetworkImpl { self_sender: channel::Sender, failure::Error>>, self_receiver: Option, failure::Error>>>, peers: Arc>, - validator: Arc, + validator: Arc>, } impl Clone for ConsensusNetworkImpl { @@ -132,7 +133,7 @@ impl ConsensusNetworkImpl { network_sender: ConsensusNetworkSender, network_events: ConsensusNetworkEvents, peers: Arc>, - validator: Arc, + validator: Arc>, ) -> Self { let (self_sender, self_receiver) = channel::new(1_024, &counters::PENDING_SELF_MESSAGES); ConsensusNetworkImpl { @@ -317,7 +318,7 @@ struct NetworkTask { chunk_request_tx: channel::Sender, timeout_msg_tx: channel::Sender, all_events: S, - validator: Arc, + validator: Arc>, } impl NetworkTask diff --git a/consensus/src/chained_bft/network_tests.rs b/consensus/src/chained_bft/network_tests.rs index a1592429abcb..35afa02865d2 100644 --- a/consensus/src/chained_bft/network_tests.rs +++ b/consensus/src/chained_bft/network_tests.rs @@ -21,6 +21,7 @@ use network::{ protocols::rpc::InboundRpcRequest, validator_network::{ConsensusNetworkEvents, ConsensusNetworkSender}, }; +use nextgen_crypto::ed25519::*; use proto_conv::FromProto; use std::{ collections::{HashMap, HashSet}, @@ -326,8 +327,8 @@ fn test_network_api() { let mut nodes = Vec::new(); let mut author_to_public_keys = HashMap::new(); let mut signers = Vec::new(); - for _ in 0..num_nodes { - let random_validator_signer = ValidatorSigner::random(); + for i in 0..num_nodes { + let random_validator_signer = ValidatorSigner::random([i as u8; 32]); author_to_public_keys.insert( random_validator_signer.author(), random_validator_signer.public_key(), @@ -363,7 +364,7 @@ fn test_network_api() { ); let proposal = ProposalInfo { proposal: Block::make_genesis_block(), - proposer_info: ValidatorSigner::genesis().author(), + proposer_info: ValidatorSigner::::genesis().author(), timeout_certificate: None, highest_ledger_info: QuorumCert::certificate_for_genesis(), }; @@ -397,8 +398,8 @@ fn test_rpc() { let mut playground = NetworkPlayground::new(runtime.executor()); let mut nodes = Vec::new(); let mut author_to_public_keys = HashMap::new(); - for _ in 0..num_nodes { - let random_validator_signer = ValidatorSigner::random(); + for i in 0..num_nodes { + let random_validator_signer = ValidatorSigner::::random([i as u8; 32]); author_to_public_keys.insert( random_validator_signer.author(), random_validator_signer.public_key(), diff --git a/consensus/src/chained_bft/proto_test.rs b/consensus/src/chained_bft/proto_test.rs index 8b33fce19d58..08a0b64b376a 100644 --- a/consensus/src/chained_bft/proto_test.rs +++ b/consensus/src/chained_bft/proto_test.rs @@ -11,6 +11,7 @@ use crate::{ state_replication::ExecutedState, }; use crypto::HashValue; +use nextgen_crypto::ed25519::Ed25519PrivateKey; use proto_conv::test_helper::assert_protobuf_encode_decode; use types::validator_signer::ValidatorSigner; @@ -22,7 +23,7 @@ fn test_proto_convert_block() { #[test] fn test_proto_convert_proposal() { - let author = ValidatorSigner::random().author(); + let author = ValidatorSigner::::random(None).author(); let proposal = ProposalInfo { proposal: Block::::make_genesis_block(), proposer_info: author, @@ -34,7 +35,7 @@ fn test_proto_convert_proposal() { #[test] fn test_proto_convert_vote() { - let signer = ValidatorSigner::random(); + let signer = ValidatorSigner::::random(None); let vote = VoteMsg::new( HashValue::random(), ExecutedState::state_for_genesis(), diff --git a/consensus/src/chained_bft/safety/safety_rules_test.rs b/consensus/src/chained_bft/safety/safety_rules_test.rs index 494d5a77f2c1..4d25326b6b9c 100644 --- a/consensus/src/chained_bft/safety/safety_rules_test.rs +++ b/consensus/src/chained_bft/safety/safety_rules_test.rs @@ -16,7 +16,7 @@ use std::{ hash::{Hash, Hasher}, sync::Arc, }; -use types::{account_address::AccountAddress, validator_signer::ValidatorSigner}; +use types::validator_signer::ValidatorSigner; fn calculate_hash(t: &T) -> u64 { let mut s = DefaultHasher::new(); @@ -56,20 +56,22 @@ cached_key! { proptest! { #[test] fn test_blocks_commits_safety_rules( - (keypairs, blocks) in block_test::block_forest_and_its_keys( + (mut keypairs, blocks) in block_test::block_forest_and_its_keys( // quorum size 10, // recursion depth 50) ) { - let (priv_key, pub_key) = keypairs.first().expect("several keypairs generated"); - let signer = ValidatorSigner::new(AccountAddress::from(*pub_key), *pub_key, priv_key.clone()); + let first_key = keypairs.pop().expect("several keys"); + let first_signer = ValidatorSigner::new(None, first_key); + let mut qc_signers = vec![first_signer.clone()]; - let mut qc_signers = vec![]; - for (priv_key, pub_key) in keypairs { - qc_signers.push(ValidatorSigner::new(AccountAddress::from(pub_key), pub_key, priv_key.clone())); + for priv_key in keypairs { + let signer = ValidatorSigner::new(None, priv_key); + qc_signers.push(signer); } - let block_tree = build_empty_tree_with_custom_signing(signer.clone()); + + let block_tree = build_empty_tree_with_custom_signing(first_signer.clone()); let mut inserter = TreeInserter::new(block_tree.clone()); let mut safety_rules = SafetyRules::new(block_tree.clone(), ConsensusState::default()); @@ -90,7 +92,7 @@ proptest! { continue; } - let insert_res = inserter.insert_pre_made_block(block.clone(), &signer, qc_signers.clone()); + let insert_res = inserter.insert_pre_made_block(block.clone(), &first_signer, qc_signers.iter().collect()); let id_and_qc = |ref block: Arc>>| { (block.id(), block.quorum_cert().clone()) }; let (inserted_id, inserted_qc) = id_and_qc(insert_res.clone()); safety_rules.update(&inserted_qc); diff --git a/consensus/src/chained_bft/safety/vote_msg.rs b/consensus/src/chained_bft/safety/vote_msg.rs index 610e89c08d37..87f83e14e715 100644 --- a/consensus/src/chained_bft/safety/vote_msg.rs +++ b/consensus/src/chained_bft/safety/vote_msg.rs @@ -12,6 +12,7 @@ use crypto::{ }; use failure::Result as ProtoResult; use network::proto::Vote as ProtoVote; +use nextgen_crypto::ed25519::*; use proto_conv::{FromProto, IntoProto}; use serde::{Deserialize, Serialize}; use std::{ @@ -106,7 +107,7 @@ impl VoteMsg { round: Round, author: Author, mut ledger_info_placeholder: LedgerInfo, - validator_signer: &ValidatorSigner, + validator_signer: &ValidatorSigner, ) -> Self { ledger_info_placeholder.set_consensus_data_hash(Self::vote_digest( proposed_block_id, @@ -122,7 +123,7 @@ impl VoteMsg { round, author, ledger_info: ledger_info_placeholder, - signature: li_sig, + signature: li_sig.into(), } } @@ -158,12 +159,19 @@ impl VoteMsg { /// Verifies that the consensus data hash of LedgerInfo corresponds to the vote info, /// and then verifies the signature. - pub fn verify(&self, validator: &ValidatorVerifier) -> Result<(), VoteMsgVerificationError> { + pub fn verify( + &self, + validator: &ValidatorVerifier, + ) -> Result<(), VoteMsgVerificationError> { if self.ledger_info.consensus_data_hash() != self.vote_hash() { return Err(VoteMsgVerificationError::ConsensusDataMismatch); } validator - .verify_signature(self.author(), self.ledger_info.hash(), self.signature()) + .verify_signature( + self.author(), + self.ledger_info.hash(), + &(self.signature().clone().into()), + ) .map_err(VoteMsgVerificationError::SigVerifyError) } diff --git a/consensus/src/chained_bft/test_utils/mod.rs b/consensus/src/chained_bft/test_utils/mod.rs index 0b57c9d08b20..2d42dc2c4506 100644 --- a/consensus/src/chained_bft/test_utils/mod.rs +++ b/consensus/src/chained_bft/test_utils/mod.rs @@ -13,6 +13,7 @@ use crate::{ use crypto::{hash::CryptoHash, HashValue}; use futures::{channel::mpsc, executor::block_on}; use logger::{set_simple_logger, set_simple_logger_prefix}; +use nextgen_crypto::ed25519::*; use std::{collections::HashMap, sync::Arc}; use termion::color::*; use tokio::runtime; @@ -33,12 +34,12 @@ pub use mock_txn_manager::MockTransactionManager; pub type TestPayload = Vec; pub fn build_empty_tree() -> Arc>> { - let signer = ValidatorSigner::random(); - build_empty_tree_with_custom_signing(signer.clone()) + let signer = ValidatorSigner::random(None); + build_empty_tree_with_custom_signing(signer) } pub fn build_empty_tree_with_custom_signing( - my_signer: ValidatorSigner, + my_signer: ValidatorSigner, ) -> Arc>> { let (commit_cb_sender, _commit_cb_receiver) = mpsc::unbounded::(); let (storage, initial_data) = EmptyStorage::start_for_testing(); @@ -75,7 +76,7 @@ impl TreeInserter { ) -> Arc>> { // Node must carry a QC to its parent let parent_qc = placeholder_certificate_for_block( - vec![self.block_store.signer().clone()], + vec![self.block_store.signer()], parent.id(), parent.round(), ); @@ -104,8 +105,8 @@ impl TreeInserter { pub fn insert_pre_made_block( &mut self, block: Block>, - block_signer: &ValidatorSigner, - qc_signers: Vec, + block_signer: &ValidatorSigner, + qc_signers: Vec<&ValidatorSigner>, ) -> Arc>> { self.payload_val += 1; let new_round = if block.round() > 0 { @@ -139,7 +140,7 @@ pub fn placeholder_ledger_info() -> LedgerInfo { } pub fn placeholder_certificate_for_block( - signers: Vec, + signers: Vec<&ValidatorSigner>, certified_block_id: HashValue, certified_block_round: u64, ) -> QuorumCert { @@ -161,7 +162,7 @@ pub fn placeholder_certificate_for_block( let li_sig = signer .sign_message(ledger_info_placeholder.hash()) .expect("Failed to sign LedgerInfo"); - signatures.insert(signer.author(), li_sig); + signatures.insert(signer.author(), li_sig.into()); } QuorumCert::new( diff --git a/consensus/src/lib.rs b/consensus/src/lib.rs index 9058e4f1f2dc..5b6107660ddb 100644 --- a/consensus/src/lib.rs +++ b/consensus/src/lib.rs @@ -12,6 +12,7 @@ #![feature(drain_filter)] #![feature(checked_duration_since)] #![feature(crate_visibility_modifier)] +#![feature(test)] #![recursion_limit = "128"] #[macro_use] extern crate failure; diff --git a/consensus/src/state_synchronizer/sync_test.rs b/consensus/src/state_synchronizer/sync_test.rs index 4f0a55fe835c..dcf1bb36c5cb 100644 --- a/consensus/src/state_synchronizer/sync_test.rs +++ b/consensus/src/state_synchronizer/sync_test.rs @@ -30,9 +30,11 @@ use network::{ }, NetworkPublicKeys, ProtocolId, }; +use nextgen_crypto::{ed25519::compat, test_utils::TEST_SEED}; use parity_multiaddr::Multiaddr; use proto_conv::IntoProto; use protobuf::Message; +use rand::{rngs::StdRng, SeedableRng}; use rusty_fork::{rusty_fork_id, rusty_fork_test, rusty_fork_test_name}; use std::{ collections::HashMap, @@ -72,8 +74,9 @@ impl SynchronizerEnv { let protocols = vec![ProtocolId::from_static(CONSENSUS_RPC_PROTOCOL)]; // Setup signing public keys. - let (a_signing_private_key, a_signing_public_key) = signing::generate_keypair(); - let (b_signing_private_key, b_signing_public_key) = signing::generate_keypair(); + let mut rng = StdRng::from_seed(TEST_SEED); + let (a_signing_private_key, a_signing_public_key) = compat::generate_keypair(&mut rng); + let (b_signing_private_key, b_signing_public_key) = compat::generate_keypair(&mut rng); // Setup identity public keys. let (a_identity_private_key, a_identity_public_key) = x25519::generate_keypair(); let (b_identity_private_key, b_identity_public_key) = x25519::generate_keypair(); @@ -82,14 +85,14 @@ impl SynchronizerEnv { ( peers[0], NetworkPublicKeys { - signing_public_key: a_signing_public_key, + signing_public_key: a_signing_public_key.clone(), identity_public_key: a_identity_public_key, }, ), ( peers[1], NetworkPublicKeys { - signing_public_key: b_signing_public_key, + signing_public_key: b_signing_public_key.clone(), identity_public_key: b_identity_public_key, }, ), diff --git a/crypto/legacy_crypto/src/lib.rs b/crypto/legacy_crypto/src/lib.rs index 311e4167610e..b2182ddf84d0 100644 --- a/crypto/legacy_crypto/src/lib.rs +++ b/crypto/legacy_crypto/src/lib.rs @@ -5,7 +5,6 @@ #![deny(missing_docs)] #![feature(test)] -#![feature(trait_alias)] pub mod hash; pub mod hkdf; diff --git a/crypto/nextgen_crypto/Cargo.toml b/crypto/nextgen_crypto/Cargo.toml index e5ae924d6c4d..a4bdbb09e9b7 100644 --- a/crypto/nextgen_crypto/Cargo.toml +++ b/crypto/nextgen_crypto/Cargo.toml @@ -38,3 +38,7 @@ crypto = { path = "../legacy_crypto" } bitvec = "0.10.1" byteorder = "1.3.1" ripemd160 = "0.8.0" + +[features] +default = [] +testing = [] \ No newline at end of file diff --git a/crypto/nextgen_crypto/src/ed25519.rs b/crypto/nextgen_crypto/src/ed25519.rs index 808c60b2792c..9754090e2b7e 100644 --- a/crypto/nextgen_crypto/src/ed25519.rs +++ b/crypto/nextgen_crypto/src/ed25519.rs @@ -385,7 +385,16 @@ impl Eq for Ed25519Signature {} /// disappear after pub mod compat { use crate::ed25519::*; - use crypto::{PublicKey as LegacyPublicKey, Signature as LegacySignature}; + #[cfg(any(test, feature = "testing"))] + use bincode::{deserialize, serialize}; + use crypto::{ + PrivateKey as LegacyPrivateKey, PublicKey as LegacyPublicKey, Signature as LegacySignature, + }; + #[cfg(any(test, feature = "testing"))] + use proptest::{ + prelude::{Arbitrary, BoxedStrategy}, + strategy::{LazyJust, Strategy}, + }; impl From for LegacyPublicKey { fn from(public_key: Ed25519PublicKey) -> Self { @@ -399,6 +408,37 @@ pub mod compat { } } + #[cfg(any(test, feature = "testing"))] + impl From for LegacyPrivateKey { + fn from(private_key: Ed25519PrivateKey) -> Self { + // bincode requires size-padding on 8 bytes before the + // serialized material — so we reproduce this technique + let mut res = vec![ + ed25519_dalek::SECRET_KEY_LENGTH as u8, + 0u8, + 0u8, + 0u8, + 0u8, + 0u8, + 0u8, + 0u8, + ]; + let serialized: Vec = private_key.to_bytes().to_vec(); + res.extend(serialized); + deserialize::(&res).unwrap() + } + } + + #[cfg(any(test, feature = "testing"))] + impl From for Ed25519PrivateKey { + fn from(private_key: LegacyPrivateKey) -> Self { + let serialized: Vec = serialize(&private_key).unwrap(); + // The 8th index here is due to bincode's serialization, which + // preprends 8 bytes of size information to the serialized material + Ed25519PrivateKey::try_from(&serialized[8..]).unwrap() + } + } + impl From for Ed25519PublicKey { fn from(public_key: LegacyPublicKey) -> Self { let encoded_privkey = public_key.to_slice(); @@ -414,4 +454,35 @@ pub mod compat { } } + #[cfg(any(test, feature = "testing"))] + impl Clone for Ed25519PrivateKey { + fn clone(&self) -> Self { + let serialized: &[u8] = &(self.to_bytes()); + Ed25519PrivateKey::try_from(serialized).unwrap() + } + } + + use rand::rngs::StdRng; + /// Generate an arbitrary key pair, with possible Rng input + pub fn generate_keypair<'a, T>(opt_rng: T) -> (Ed25519PrivateKey, Ed25519PublicKey) + where + T: Into> + Sized, + { + if let Some(rng_mut_ref) = opt_rng.into() { + <(Ed25519PrivateKey, Ed25519PublicKey)>::generate_for_testing(rng_mut_ref) + } else { + let mut rng = StdRng::from_seed(crate::test_utils::TEST_SEED); + <(Ed25519PrivateKey, Ed25519PublicKey)>::generate_for_testing(&mut rng) + } + } + + #[cfg(any(test, feature = "testing"))] + impl Arbitrary for Ed25519PublicKey { + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + LazyJust::new(|| generate_keypair(None).1).boxed() + } + type Strategy = BoxedStrategy; + type Parameters = (); + } + } diff --git a/crypto/nextgen_crypto/src/test_utils.rs b/crypto/nextgen_crypto/src/test_utils.rs index de1995f573be..79428ac5a18e 100644 --- a/crypto/nextgen_crypto/src/test_utils.rs +++ b/crypto/nextgen_crypto/src/test_utils.rs @@ -7,15 +7,18 @@ use crate::traits::{SeedableCryptoRng, Uniform}; use bincode::serialize; use serde::Serialize; -/// A seed number for testing -pub const TEST_SEED: [u8; 32] = [0; 32]; +/// A deterministic seed for PRNGs related to keys +pub const TEST_SEED: [u8; 32] = [0u8; 32]; -/// A labeled tuple consisting of a private and public key +/// A keypair consisting of a private and public key #[cfg_attr(test, derive(Clone))] -pub struct KeyPair { - /// the private (signing) key component +pub struct KeyPair +where + for<'a> P: From<&'a S>, +{ + /// the private key component pub private_key: S, - /// the corresponding public (verifying) key component + /// the public key component pub public_key: P, } @@ -45,6 +48,22 @@ where } } +/// A pair consisting of a private and public key +impl Uniform for (S, P) +where + S: Uniform, + for<'a> P: From<&'a S>, +{ + fn generate_for_testing(rng: &mut R) -> Self + where + R: SeedableCryptoRng, + { + let private_key = S::generate_for_testing(rng); + let public_key = (&private_key).into(); + (private_key, public_key) + } +} + impl std::fmt::Debug for KeyPair where Priv: Serialize, diff --git a/execution/execution_proto/Cargo.toml b/execution/execution_proto/Cargo.toml index 15e37cc3e168..2ccd94e7740a 100644 --- a/execution/execution_proto/Cargo.toml +++ b/execution/execution_proto/Cargo.toml @@ -21,3 +21,7 @@ types = { path = "../../types" } [build-dependencies] build_helpers = { path = "../../common/build_helpers" } + +[dev-dependencies.types] +features = ["testing"] +path = "../../types" diff --git a/execution/execution_proto/src/lib.rs b/execution/execution_proto/src/lib.rs index 63b584df2361..66652a4edb62 100644 --- a/execution/execution_proto/src/lib.rs +++ b/execution/execution_proto/src/lib.rs @@ -10,6 +10,7 @@ mod protobuf_conversion_test; use crypto::HashValue; use failure::prelude::*; +#[cfg(any(test, feature = "testing"))] use proptest_derive::Arbitrary; use proto_conv::{FromProto, IntoProto}; use types::{ @@ -19,8 +20,9 @@ use types::{ vm_error::VMStatus, }; -#[derive(Arbitrary, Clone, Debug, Eq, PartialEq, FromProto, IntoProto)] +#[derive(Clone, Debug, Eq, PartialEq, FromProto, IntoProto)] #[ProtoType(crate::proto::execution::ExecuteBlockRequest)] +#[cfg_attr(any(test, feature = "testing"), derive(Arbitrary))] pub struct ExecuteBlockRequest { /// The list of transactions from consensus. pub transactions: Vec, @@ -46,7 +48,8 @@ impl ExecuteBlockRequest { } } -#[derive(Arbitrary, Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq)] +#[cfg_attr(any(test, feature = "testing"), derive(Arbitrary))] pub struct ExecuteBlockResponse { /// Root hash of the transaction accumulator as if all transactions in this block are applied. root_hash: HashValue, @@ -144,13 +147,15 @@ impl IntoProto for ExecuteBlockResponse { } } -#[derive(Arbitrary, Clone, Debug, Eq, PartialEq, FromProto, IntoProto)] +#[derive(Clone, Debug, Eq, PartialEq, FromProto, IntoProto)] +#[cfg_attr(any(test, feature = "testing"), derive(Arbitrary))] #[ProtoType(crate::proto::execution::CommitBlockRequest)] pub struct CommitBlockRequest { pub ledger_info_with_sigs: LedgerInfoWithSignatures, } -#[derive(Arbitrary, Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[cfg_attr(any(test, feature = "testing"), derive(Arbitrary))] pub enum CommitBlockResponse { Succeeded, Failed, @@ -182,13 +187,15 @@ impl IntoProto for CommitBlockResponse { } } -#[derive(Arbitrary, Clone, Debug, Eq, PartialEq, FromProto, IntoProto)] +#[derive(Clone, Debug, Eq, PartialEq, FromProto, IntoProto)] +#[cfg_attr(any(test, feature = "testing"), derive(Arbitrary))] #[ProtoType(crate::proto::execution::ExecuteChunkRequest)] pub struct ExecuteChunkRequest { pub txn_list_with_proof: TransactionListWithProof, pub ledger_info_with_sigs: LedgerInfoWithSignatures, } -#[derive(Arbitrary, Clone, Debug, Eq, PartialEq, FromProto, IntoProto)] +#[derive(Clone, Debug, Eq, PartialEq, FromProto, IntoProto)] +#[cfg_attr(any(test, feature = "testing"), derive(Arbitrary))] #[ProtoType(crate::proto::execution::ExecuteChunkResponse)] pub struct ExecuteChunkResponse {} diff --git a/language/vm/vm_genesis/Cargo.toml b/language/vm/vm_genesis/Cargo.toml index a2be29077f65..d898ddcf31cd 100644 --- a/language/vm/vm_genesis/Cargo.toml +++ b/language/vm/vm_genesis/Cargo.toml @@ -29,3 +29,7 @@ canonical_serialization = { path = "../../../common/canonical_serialization" } proptest = "0.9.3" proptest-derive = "0.1.1" proptest_helpers = { path = "../../../common/proptest_helpers" } + +[dev-dependencies.types] +features = ["testing"] +path = "../../../types" diff --git a/libra_node/src/main_node.rs b/libra_node/src/main_node.rs index c33d9b9300f2..12e76a5f1b39 100644 --- a/libra_node/src/main_node.rs +++ b/libra_node/src/main_node.rs @@ -159,7 +159,7 @@ pub fn setup_network( ( peer_id, NetworkPublicKeys { - signing_public_key, + signing_public_key: signing_public_key.into(), identity_public_key, }, ) @@ -174,6 +174,7 @@ pub fn setup_network( .map(|(peer_id, addrs)| (peer_id.try_into().expect("Invalid PeerId"), addrs)) .collect(); let network_signing_keypair = config.base.peer_keypairs.get_network_signing_keypair(); + let (private_key, public_key) = network_signing_keypair; let network_identity_keypair = config.base.peer_keypairs.get_network_identity_keypair(); let ( (mempool_network_sender, mempool_network_events), @@ -187,7 +188,7 @@ pub fn setup_network( }) .advertised_address(advertised_addr) .seed_peers(seed_peers) - .signing_keys(network_signing_keypair) + .signing_keys((private_key.into(), public_key.into())) .identity_keys(network_identity_keypair) .trusted_peers(trusted_peers) .discovery_interval_ms(config.network.discovery_interval_ms) diff --git a/network/Cargo.toml b/network/Cargo.toml index cd2a14ca0bde..80cbfa2c9228 100644 --- a/network/Cargo.toml +++ b/network/Cargo.toml @@ -22,6 +22,7 @@ unsigned-varint = { version = "0.2.2", features = ["codec"] } channel = { path = "../common/channel" } crypto = { path = "../crypto/legacy_crypto" } +nextgen_crypto = { path = "../crypto/nextgen_crypto" } failure = { package = "failure_ext", path = "../common/failure_ext" } logger = { path = "../common/logger" } memsocket = { path = "memsocket" } @@ -34,6 +35,10 @@ types = { path = "../types" } [dev-dependencies] criterion = { version = "0.2.11", features = ["real_blackbox"] } +[dev-dependencies.nextgen_crypto] +features = ["testing"] +path = "../crypto/nextgen_crypto" + [build-dependencies] protoc-rust = "2.5.0" diff --git a/network/benches/network_bench.rs b/network/benches/network_bench.rs index 4513a2537f68..187812edadf3 100644 --- a/network/benches/network_bench.rs +++ b/network/benches/network_bench.rs @@ -15,7 +15,7 @@ use criterion::{ criterion_group, criterion_main, AxisScale, Bencher, Criterion, ParameterizedBenchmark, PlotConfiguration, Throughput, }; -use crypto::{signing, x25519}; +use crypto::x25519; use futures::{ channel::mpsc, compat::Future01CompatExt, @@ -33,8 +33,10 @@ use network::{ }, NetworkPublicKeys, ProtocolId, }; +use nextgen_crypto::{ed25519::compat, test_utils::TEST_SEED}; use parity_multiaddr::Multiaddr; use protobuf::Message; +use rand::{rngs::StdRng, SeedableRng}; use std::{collections::HashMap, time::Duration}; use tokio::runtime::Runtime; use types::PeerId; @@ -57,11 +59,14 @@ fn direct_send_bench(b: &mut Bencher, msg_len: &usize) { ); // Setup keys for dialer. - let (dialer_signing_private_key, dialer_signing_public_key) = signing::generate_keypair(); + let mut rng = StdRng::from_seed(TEST_SEED); + let (dialer_signing_private_key, dialer_signing_public_key) = + compat::generate_keypair(&mut rng); let (dialer_identity_private_key, dialer_identity_public_key) = x25519::generate_keypair(); // Setup keys for listener. - let (listener_signing_private_key, listener_signing_public_key) = signing::generate_keypair(); + let (listener_signing_private_key, listener_signing_public_key) = + compat::generate_keypair(&mut rng); let (listener_identity_private_key, listener_identity_public_key) = x25519::generate_keypair(); // Setup trusted peers. @@ -69,14 +74,14 @@ fn direct_send_bench(b: &mut Bencher, msg_len: &usize) { ( dialer_peer_id, NetworkPublicKeys { - signing_public_key: dialer_signing_public_key, + signing_public_key: dialer_signing_public_key.clone().into(), identity_public_key: dialer_identity_public_key, }, ), ( listener_peer_id, NetworkPublicKeys { - signing_public_key: listener_signing_public_key, + signing_public_key: listener_signing_public_key.clone().into(), identity_public_key: listener_identity_public_key, }, ), @@ -180,11 +185,14 @@ fn rpc_bench(b: &mut Bencher, msg_len: &usize) { ); // Setup keys for dialer. - let (dialer_signing_private_key, dialer_signing_public_key) = signing::generate_keypair(); + let mut rng = StdRng::from_seed(TEST_SEED); + let (dialer_signing_private_key, dialer_signing_public_key) = + compat::generate_keypair(&mut rng); let (dialer_identity_private_key, dialer_identity_public_key) = x25519::generate_keypair(); // Setup keys for listener. - let (listener_signing_private_key, listener_signing_public_key) = signing::generate_keypair(); + let (listener_signing_private_key, listener_signing_public_key) = + compat::generate_keypair(&mut rng); let (listener_identity_private_key, listener_identity_public_key) = x25519::generate_keypair(); // Setup trusted peers. @@ -192,14 +200,14 @@ fn rpc_bench(b: &mut Bencher, msg_len: &usize) { ( dialer_peer_id, NetworkPublicKeys { - signing_public_key: dialer_signing_public_key, + signing_public_key: dialer_signing_public_key.clone().into(), identity_public_key: dialer_identity_public_key, }, ), ( listener_peer_id, NetworkPublicKeys { - signing_public_key: listener_signing_public_key, + signing_public_key: listener_signing_public_key.clone().into(), identity_public_key: listener_identity_public_key, }, ), diff --git a/network/src/common.rs b/network/src/common.rs index 22e052980b6c..795493be76b2 100644 --- a/network/src/common.rs +++ b/network/src/common.rs @@ -2,7 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 use crate::ProtocolId; -use crypto::{x25519::X25519PublicKey, PublicKey}; +use crypto::x25519::X25519PublicKey; +use nextgen_crypto::ed25519::*; use std::fmt; /// A Negotiated substream encapsulates a protocol and a substream for which that protocol has been @@ -28,7 +29,7 @@ impl fmt::Debug for NegotiatedSubstream { #[derive(Debug, Clone)] pub struct NetworkPublicKeys { /// This key can validate signed messages at the network layer. - pub signing_public_key: PublicKey, + pub signing_public_key: Ed25519PublicKey, /// This key establishes a node's identity in the p2p network. pub identity_public_key: X25519PublicKey, } diff --git a/network/src/connectivity_manager/test.rs b/network/src/connectivity_manager/test.rs index d8d1a3b04cfe..fdec1a8579b9 100644 --- a/network/src/connectivity_manager/test.rs +++ b/network/src/connectivity_manager/test.rs @@ -4,9 +4,10 @@ use super::*; use crate::peer_manager::PeerManagerRequest; use core::str::FromStr; -use crypto::{signing, x25519}; +use crypto::x25519; use futures::{FutureExt, SinkExt, TryFutureExt}; use memsocket::MemorySocket; +use nextgen_crypto::ed25519::compat; use std::io; use tokio::runtime::Runtime; use tokio_retry::strategy::FixedInterval; @@ -27,7 +28,7 @@ fn setup_conn_mgr( let (peer_mgr_notifs_tx, peer_mgr_notifs_rx) = channel::new_test(0); let (conn_mgr_reqs_tx, conn_mgr_reqs_rx) = channel::new_test(0); let (ticker_tx, ticker_rx) = channel::new_test(0); - let (_, signing_public_key) = signing::generate_keypair(); + let (_, signing_public_key) = compat::generate_keypair(None); let (_, identity_public_key) = x25519::generate_keypair(); let conn_mgr = { ConnectivityManager::new( @@ -61,7 +62,7 @@ fn setup_conn_mgr( fn gen_peer() -> (PeerId, NetworkPublicKeys) { let peer_id = PeerId::random(); - let (_, signing_public_key) = signing::generate_keypair(); + let (_, signing_public_key) = compat::generate_keypair(None); let (_, identity_public_key) = x25519::generate_keypair(); ( peer_id, diff --git a/network/src/protocols/discovery/mod.rs b/network/src/protocols/discovery/mod.rs index e2859939dec9..2a3bc813fb15 100644 --- a/network/src/protocols/discovery/mod.rs +++ b/network/src/protocols/discovery/mod.rs @@ -44,8 +44,9 @@ use bytes::Bytes; use channel; use crypto::{ hash::{CryptoHasher, DiscoveryMsgHasher}, - HashValue, Signature, + HashValue, Signature as LegacySignature, }; +use failure::Fail; use futures::{ compat::{Future01CompatExt, Sink01CompatExt}, future::{Future, FutureExt, TryFutureExt}, @@ -54,6 +55,7 @@ use futures::{ stream::{FusedStream, FuturesUnordered, Stream, StreamExt}, }; use logger::prelude::*; +use nextgen_crypto::ed25519::*; use parity_multiaddr::Multiaddr; use protobuf::{self, Message}; use rand::{rngs::SmallRng, FromEntropy, Rng}; @@ -112,7 +114,7 @@ where pub fn new( self_peer_id: PeerId, self_addrs: Vec, - signer: Signer, + signer: Signer, seed_peers: HashMap, trusted_peers: Arc>>, ticker: TTicker, @@ -380,7 +382,7 @@ fn create_peer_info(addrs: Vec) -> PeerInfo { // Creates a note by signing the given peer info, and combining the signature, peer_info and // peer_id into a note. -fn create_note(signer: &Signer, peer_id: PeerId, peer_info: PeerInfo) -> Note { +fn create_note(signer: &Signer, peer_id: PeerId, peer_info: PeerInfo) -> Note { let raw_info = peer_info .write_to_bytes() .expect("Protobuf serialization fails"); @@ -469,30 +471,33 @@ fn verify_signatures( signature: &[u8], msg: &[u8], ) -> Result<(), NetworkError> { - let verifier = SignatureValidator::new_with_quorum_size( + let verifier = SignatureValidator::::new_with_quorum_size( trusted_peers .read() .unwrap() .iter() .map(|(peer_id, network_public_keys)| { - (*peer_id, network_public_keys.signing_public_key) + ( + *peer_id, + network_public_keys.signing_public_key.clone().into(), + ) }) .collect(), 1, /* quorum size */ ) .expect("Quorum size should be valid."); - let signature = Signature::from_compact(signature) + let signature = Ed25519Signature::try_from(signature) .map_err(|err| err.context(NetworkErrorKind::SignatureError))?; verifier.verify_signature(signer, get_hash(msg), &signature)?; Ok(()) } -fn sign(signer: &Signer, msg: &[u8]) -> Vec { - signer +fn sign(signer: &Signer, msg: &[u8]) -> Vec { + let signature: Ed25519Signature = signer .sign_message(get_hash(msg)) - .expect("Message signing fails") - .to_compact() - .to_vec() + .expect("Message signing fails"); + let sig: LegacySignature = signature.into(); + sig.to_compact().to_vec() } async fn push_state_to_peer( diff --git a/network/src/protocols/discovery/test.rs b/network/src/protocols/discovery/test.rs index dc34c0e840da..46d04b589c29 100644 --- a/network/src/protocols/discovery/test.rs +++ b/network/src/protocols/discovery/test.rs @@ -4,9 +4,10 @@ use super::*; use crate::{peer_manager::PeerManagerRequest, proto::DiscoveryMsg}; use core::str::FromStr; -use crypto::{signing, x25519}; +use crypto::x25519; use futures::future::{FutureExt, TryFutureExt}; use memsocket::MemorySocket; +use nextgen_crypto::{ed25519::Ed25519PrivateKey, *}; use tokio::runtime::Runtime; fn get_random_seed() -> PeerInfo { @@ -27,7 +28,7 @@ fn setup_discovery( address: Multiaddr, seed_peer_id: PeerId, seed_peer_info: PeerInfo, - signer: Signer, + signer: Signer, trusted_peers: Arc>>, ) -> ( channel::Receiver>, @@ -88,15 +89,17 @@ async fn expect_address_update( } } -fn generate_network_pub_keys_and_signer(peer_id: PeerId) -> (NetworkPublicKeys, Signer) { - let (signing_priv_key, signing_pub_key) = signing::generate_keypair(); +fn generate_network_pub_keys_and_signer( + peer_id: PeerId, +) -> (NetworkPublicKeys, Signer) { + let (signing_priv_key, _) = compat::generate_keypair(None); let (_, identity_pub_key) = x25519::generate_keypair(); ( NetworkPublicKeys { - signing_public_key: signing_pub_key, + signing_public_key: signing_priv_key.public_key().clone().into(), identity_public_key: identity_pub_key, }, - Signer::new(peer_id, signing_pub_key, signing_priv_key), + Signer::new(peer_id, signing_priv_key.into()), ) } diff --git a/network/src/validator_network/consensus.rs b/network/src/validator_network/consensus.rs index dd52ca7215d9..d56e19f99e95 100644 --- a/network/src/validator_network/consensus.rs +++ b/network/src/validator_network/consensus.rs @@ -191,7 +191,7 @@ impl ConsensusNetworkSender { *keys.account_address(), NetworkPublicKeys { identity_public_key: *keys.network_identity_public_key(), - signing_public_key: *keys.network_signing_public_key(), + signing_public_key: keys.network_signing_public_key().clone(), }, ) }) diff --git a/network/src/validator_network/network_builder.rs b/network/src/validator_network/network_builder.rs index 193b098d88ec..0dcfa0c17d54 100644 --- a/network/src/validator_network/network_builder.rs +++ b/network/src/validator_network/network_builder.rs @@ -25,12 +25,10 @@ use crate::{ ProtocolId, }; use channel; -use crypto::{ - x25519::{X25519PrivateKey, X25519PublicKey}, - PrivateKey, PublicKey, -}; +use crypto::x25519::{X25519PrivateKey, X25519PublicKey}; use futures::{compat::Compat01As03, FutureExt, StreamExt, TryFutureExt}; use netcore::{multiplexing::StreamMultiplexer, transport::boxed::BoxedTransport}; +use nextgen_crypto::ed25519::*; use parity_multiaddr::Multiaddr; use std::{ collections::HashMap, @@ -95,7 +93,7 @@ pub struct NetworkBuilder { max_concurrent_network_reqs: u32, max_concurrent_network_notifs: u32, max_connection_delay_ms: u64, - signing_keys: Option<(PrivateKey, PublicKey)>, + signing_keys: Option<(Ed25519PrivateKey, Ed25519PublicKey)>, identity_keys: Option<(X25519PrivateKey, X25519PublicKey)>, } @@ -154,7 +152,7 @@ impl NetworkBuilder { } /// Set signing keys of local node. - pub fn signing_keys(&mut self, keys: (PrivateKey, PublicKey)) -> &mut Self { + pub fn signing_keys(&mut self, keys: (Ed25519PrivateKey, Ed25519PublicKey)) -> &mut Self { self.signing_keys = Some(keys); self } @@ -511,9 +509,9 @@ impl NetworkBuilder { .spawn(conn_mgr.start().boxed().unit_error().compat()); // Setup signer from keys. - let (signing_private_key, signing_public_key) = + let (signing_private_key, _signing_public_key) = self.signing_keys.take().expect("Signing keys not set"); - let signer = ValidatorSigner::new(self.peer_id, signing_public_key, signing_private_key); + let signer = ValidatorSigner::new(self.peer_id, signing_private_key); // Initialize and start Discovery actor. let discovery = Discovery::new( self.peer_id, diff --git a/network/src/validator_network/test.rs b/network/src/validator_network/test.rs index 4bf5e2576093..6cdbb1f5c5eb 100644 --- a/network/src/validator_network/test.rs +++ b/network/src/validator_network/test.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 //! Integration tests for validator_network. - +#![cfg(test)] use crate::{ common::NetworkPublicKeys, proto::{ConsensusMsg, MempoolSyncMsg, RequestBlock, RespondBlock, SignedTransaction}, @@ -12,13 +12,12 @@ use crate::{ }, ProtocolId, }; -use crypto::{ - signing::{self, generate_keypair}, - x25519, -}; +use crypto::x25519; use futures::{executor::block_on, future::join, StreamExt}; +use nextgen_crypto::{ed25519::compat, test_utils::TEST_SEED}; use parity_multiaddr::Multiaddr; use protobuf::Message as proto_msg; +use rand::{rngs::StdRng, SeedableRng}; use std::{collections::HashMap, time::Duration}; use tokio::runtime::Runtime; use types::{ @@ -35,7 +34,7 @@ fn test_network_builder() { let mempool_sync_protocol = ProtocolId::from_static(MEMPOOL_DIRECT_SEND_PROTOCOL); let consensus_get_blocks_protocol = ProtocolId::from_static(b"get_blocks"); let synchronizer_get_chunks_protocol = ProtocolId::from_static(b"get_chunks"); - let (signing_private_key, signing_public_key) = signing::generate_keypair(); + let (signing_private_key, signing_public_key) = compat::generate_keypair(None); let (identity_private_key, identity_public_key) = x25519::generate_keypair(); let ( @@ -44,7 +43,7 @@ fn test_network_builder() { _listen_addr, ) = NetworkBuilder::new(runtime.executor(), peer_id, addr) .transport(TransportType::Memory) - .signing_keys((signing_private_key, signing_public_key)) + .signing_keys((signing_private_key, signing_public_key.clone())) .identity_keys((identity_private_key, identity_public_key)) .trusted_peers( vec![( @@ -78,8 +77,11 @@ fn test_mempool_sync() { let listener_peer_id = PeerId::random(); let dialer_peer_id = PeerId::random(); // Setup signing public keys. - let (listener_signing_private_key, listener_signing_public_key) = signing::generate_keypair(); - let (dialer_signing_private_key, dialer_signing_public_key) = signing::generate_keypair(); + let mut rng = StdRng::from_seed(TEST_SEED); + let (listener_signing_private_key, listener_signing_public_key) = + compat::generate_keypair(&mut rng); + let (dialer_signing_private_key, dialer_signing_public_key) = + compat::generate_keypair(&mut rng); // Setup identity public keys. let (listener_identity_private_key, listener_identity_public_key) = x25519::generate_keypair(); let (dialer_identity_private_key, dialer_identity_public_key) = x25519::generate_keypair(); @@ -91,14 +93,14 @@ fn test_mempool_sync() { ( listener_peer_id, NetworkPublicKeys { - signing_public_key: listener_signing_public_key, + signing_public_key: listener_signing_public_key.clone(), identity_public_key: listener_identity_public_key, }, ), ( dialer_peer_id, NetworkPublicKeys { - signing_public_key: dialer_signing_public_key, + signing_public_key: dialer_signing_public_key.clone(), identity_public_key: dialer_identity_public_key, }, ), @@ -141,8 +143,8 @@ fn test_mempool_sync() { let mut mempool_msg = MempoolSyncMsg::new(); mempool_msg.set_peer_id(dialer_peer_id.into()); let sender = AccountAddress::new([0; ADDRESS_LENGTH]); - let keypair = generate_keypair(); - let txn = get_test_signed_txn(sender, 0, keypair.0, keypair.1, None); + let keypair = compat::generate_keypair(&mut rng); + let txn = get_test_signed_txn(sender, 0, keypair.0.into(), keypair.1.into(), None); mempool_msg.set_transactions(::protobuf::RepeatedField::from_vec(vec![txn.clone()])); let f_dialer = async move { @@ -197,8 +199,11 @@ fn test_consensus_rpc() { let listener_peer_id = PeerId::random(); let dialer_peer_id = PeerId::random(); // Setup signing public keys. - let (listener_signing_private_key, listener_signing_public_key) = signing::generate_keypair(); - let (dialer_signing_private_key, dialer_signing_public_key) = signing::generate_keypair(); + let mut rng = StdRng::from_seed(TEST_SEED); + let (listener_signing_private_key, listener_signing_public_key) = + compat::generate_keypair(&mut rng); + let (dialer_signing_private_key, dialer_signing_public_key) = + compat::generate_keypair(&mut rng); // Setup identity public keys. let (listener_identity_private_key, listener_identity_public_key) = x25519::generate_keypair(); let (dialer_identity_private_key, dialer_identity_public_key) = x25519::generate_keypair(); @@ -210,14 +215,14 @@ fn test_consensus_rpc() { ( listener_peer_id, NetworkPublicKeys { - signing_public_key: listener_signing_public_key, + signing_public_key: listener_signing_public_key.clone(), identity_public_key: listener_identity_public_key, }, ), ( dialer_peer_id, NetworkPublicKeys { - signing_public_key: dialer_signing_public_key, + signing_public_key: dialer_signing_public_key.clone(), identity_public_key: dialer_identity_public_key, }, ), diff --git a/types/Cargo.toml b/types/Cargo.toml index 03dc28d5ef73..2029278333f3 100644 --- a/types/Cargo.toml +++ b/types/Cargo.toml @@ -26,9 +26,18 @@ tiny-keccak = "1.4.2" canonical_serialization = { path = "../common/canonical_serialization"} crypto = { path = "../crypto/legacy_crypto" } +nextgen_crypto = { path = "../crypto/nextgen_crypto" } failure = { path = "../common/failure_ext", package = "failure_ext" } proptest_helpers = { path = "../common/proptest_helpers" } proto_conv = { path = "../common/proto_conv", features = ["derive"] } [build-dependencies] build_helpers = { path = "../common/build_helpers" } + +[dev-dependencies.nextgen_crypto] +features = ["testing"] +path = "../crypto/nextgen_crypto" + +[features] +default = [] +testing = ["nextgen_crypto/testing"] \ No newline at end of file diff --git a/types/src/account_address.rs b/types/src/account_address.rs index ceb7a770965f..c9d34b8590d0 100644 --- a/types/src/account_address.rs +++ b/types/src/account_address.rs @@ -10,10 +10,11 @@ use canonical_serialization::{ }; use crypto::{ hash::{AccountAddressHasher, CryptoHash, CryptoHasher}, - HashValue, PublicKey, + HashValue, PublicKey as LegacyPublicKey, }; use failure::prelude::*; use hex; +use nextgen_crypto::{ed25519::*, VerifyingKey}; use proptest_derive::Arbitrary; use proto_conv::{FromProto, IntoProto}; use rand::{rngs::OsRng, Rng}; @@ -53,6 +54,17 @@ impl AccountAddress { pub fn to_vec(&self) -> Vec { self.0.to_vec() } + + pub fn from_public_key(public_key: &PublicKey) -> Self { + // TODO: using keccak directly instead of crypto::hash because we have to make sure we use + // the same hash function that the Move transaction prologue is using. + // TODO: keccak is just a placeholder, make a principled choice for the hash function + let mut keccak = Keccak::new_sha3_256(); + let mut hash = [0u8; ADDRESS_LENGTH]; + keccak.update(&public_key.to_bytes()); + keccak.finalize(&mut hash); + AccountAddress::new(hash) + } } impl CryptoHash for AccountAddress { @@ -167,16 +179,10 @@ impl IntoProto for AccountAddress { } } -impl From for AccountAddress { - fn from(public_key: PublicKey) -> AccountAddress { - // TODO: using keccak directly instead of crypto::hash because we have to make sure we use - // the same hash function that the Move transaction prologue is using. - // TODO: keccak is just a placeholder, make a principled choice for the hash function - let mut keccak = Keccak::new_sha3_256(); - let mut hash = [0u8; ADDRESS_LENGTH]; - keccak.update(&public_key.to_slice()); - keccak.finalize(&mut hash); - AccountAddress::new(hash) +impl From for AccountAddress { + fn from(public_key: LegacyPublicKey) -> AccountAddress { + let ed25519_public_key: Ed25519PublicKey = public_key.into(); + AccountAddress::from_public_key(&ed25519_public_key) } } diff --git a/types/src/get_with_proof.rs b/types/src/get_with_proof.rs index b25d70b4e142..c068e9cac2d3 100644 --- a/types/src/get_with_proof.rs +++ b/types/src/get_with_proof.rs @@ -24,6 +24,7 @@ use crate::{ }; use crypto::hash::CryptoHash; use failure::prelude::*; +use nextgen_crypto::ed25519::*; use proptest_derive::Arbitrary; use proto_conv::{FromProto, IntoProto}; use std::{cmp, mem, sync::Arc}; @@ -73,7 +74,7 @@ impl UpdateToLatestLedgerResponse { /// verification. pub fn verify( &self, - validator_verifier: Arc, + validator_verifier: Arc>, request: &UpdateToLatestLedgerRequest, ) -> Result<()> { verify_update_to_latest_ledger_response( @@ -89,7 +90,7 @@ impl UpdateToLatestLedgerResponse { /// Verifies content of an [`UpdateToLatestLedgerResponse`] against the proofs it /// carries and the content of the corresponding [`UpdateToLatestLedgerRequest`] pub fn verify_update_to_latest_ledger_response( - validator_verifier: Arc, + validator_verifier: Arc>, req_client_known_version: u64, req_request_items: &[RequestItem], response_items: &[ResponseItem], diff --git a/types/src/ledger_info.rs b/types/src/ledger_info.rs index c40555fb771e..0f1b2b427863 100644 --- a/types/src/ledger_info.rs +++ b/types/src/ledger_info.rs @@ -14,6 +14,7 @@ use crypto::{ HashValue, Signature, }; use failure::prelude::*; +use nextgen_crypto::ed25519::*; use proptest_derive::Arbitrary; use proto_conv::{FromProto, IntoProto}; use serde::{Deserialize, Serialize}; @@ -217,7 +218,10 @@ impl LedgerInfoWithSignatures { &self.signatures } - pub fn verify(&self, validator: &ValidatorVerifier) -> ::std::result::Result<(), VerifyError> { + pub fn verify( + &self, + validator: &ValidatorVerifier, + ) -> ::std::result::Result<(), VerifyError> { if self.ledger_info.is_zero() { // We're not trying to verify nominal ledger info that does not carry any information. return Ok(()); diff --git a/types/src/validator_public_keys.rs b/types/src/validator_public_keys.rs index 1a5444f76617..78bdca954c59 100644 --- a/types/src/validator_public_keys.rs +++ b/types/src/validator_public_keys.rs @@ -10,24 +10,28 @@ use crate::{ use canonical_serialization::{ CanonicalDeserialize, CanonicalDeserializer, CanonicalSerialize, CanonicalSerializer, }; -use crypto::{x25519::X25519PublicKey, PublicKey}; +use crypto::x25519::X25519PublicKey; use failure::Result; +use nextgen_crypto::ed25519::*; +#[cfg(any(test, feature = "testing"))] use proptest_derive::Arbitrary; use proto_conv::{FromProto, IntoProto}; +use std::convert::TryFrom; /// After executing a special transaction that sets the validators that should be used for the /// next epoch, consensus and networking get the new list of validators. Consensus will have a /// public key to validate signed messages and networking will have a TBD public key for /// creating secure channels of communication between validators. The validators and their /// public keys may or may not change between epochs. -#[derive(Arbitrary, Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq)] +#[cfg_attr(any(test, feature = "testing"), derive(Arbitrary))] pub struct ValidatorPublicKeys { // Hash value of the current public key of the account address account_address: AccountAddress, // This key can validate messages sent from this validator - consensus_public_key: PublicKey, + consensus_public_key: Ed25519PublicKey, // This key can validate signed messages at the network layer - network_signing_public_key: PublicKey, + network_signing_public_key: Ed25519PublicKey, // This key establishes the corresponding PrivateKey holder's eligibility to join the p2p // network network_identity_public_key: X25519PublicKey, @@ -36,8 +40,8 @@ pub struct ValidatorPublicKeys { impl ValidatorPublicKeys { pub fn new( account_address: AccountAddress, - consensus_public_key: PublicKey, - network_signing_public_key: PublicKey, + consensus_public_key: Ed25519PublicKey, + network_signing_public_key: Ed25519PublicKey, network_identity_public_key: X25519PublicKey, ) -> Self { ValidatorPublicKeys { @@ -55,12 +59,12 @@ impl ValidatorPublicKeys { } /// Returns the key for validating signed messages from this validator - pub fn consensus_public_key(&self) -> &PublicKey { + pub fn consensus_public_key(&self) -> &Ed25519PublicKey { &self.consensus_public_key } /// Returns the key for validating signed messages at the network layers - pub fn network_signing_public_key(&self) -> &PublicKey { + pub fn network_signing_public_key(&self) -> &Ed25519PublicKey { &self.network_signing_public_key } @@ -75,9 +79,9 @@ impl FromProto for ValidatorPublicKeys { fn from_proto(object: Self::ProtoType) -> Result { let account_address = AccountAddress::from_proto(object.get_account_address().to_vec())?; - let consensus_public_key = PublicKey::from_slice(object.get_consensus_public_key())?; + let consensus_public_key = Ed25519PublicKey::try_from(object.get_consensus_public_key())?; let network_signing_public_key = - PublicKey::from_slice(object.get_network_signing_public_key())?; + Ed25519PublicKey::try_from(object.get_network_signing_public_key())?; let network_identity_public_key = X25519PublicKey::from_slice(object.get_network_identity_public_key())?; Ok(Self::new( @@ -95,9 +99,11 @@ impl IntoProto for ValidatorPublicKeys { fn into_proto(self) -> Self::ProtoType { let mut proto = Self::ProtoType::new(); proto.set_account_address(AccountAddress::into_proto(self.account_address)); - proto.set_consensus_public_key(PublicKey::to_slice(&self.consensus_public_key).to_vec()); + proto.set_consensus_public_key( + Ed25519PublicKey::to_bytes(&self.consensus_public_key).to_vec(), + ); proto.set_network_signing_public_key( - PublicKey::to_slice(&self.network_signing_public_key).to_vec(), + Ed25519PublicKey::to_bytes(&self.network_signing_public_key).to_vec(), ); proto.set_network_identity_public_key( X25519PublicKey::to_slice(&self.network_identity_public_key).to_vec(), @@ -110,9 +116,9 @@ impl CanonicalSerialize for ValidatorPublicKeys { fn serialize(&self, serializer: &mut impl CanonicalSerializer) -> Result<()> { serializer .encode_struct(&self.account_address)? - .encode_variable_length_bytes(&self.consensus_public_key.to_slice())? + .encode_variable_length_bytes(&self.consensus_public_key.to_bytes())? .encode_variable_length_bytes(&self.network_identity_public_key.to_slice())? - .encode_variable_length_bytes(&self.network_signing_public_key.to_slice())?; + .encode_variable_length_bytes(&self.network_signing_public_key.to_bytes())?; Ok(()) } } @@ -121,11 +127,11 @@ impl CanonicalDeserialize for ValidatorPublicKeys { fn deserialize(deserializer: &mut impl CanonicalDeserializer) -> Result { let account_address = deserializer.decode_struct::()?; let concensus_public_key = - PublicKey::from_slice(&deserializer.decode_variable_length_bytes()?)?; + Ed25519PublicKey::try_from(&deserializer.decode_variable_length_bytes()?[..])?; let network_identity_public_key = X25519PublicKey::from_slice(&deserializer.decode_variable_length_bytes()?)?; let network_signing_public_key = - PublicKey::from_slice(&deserializer.decode_variable_length_bytes()?)?; + Ed25519PublicKey::try_from(&deserializer.decode_variable_length_bytes()?[..])?; Ok(ValidatorPublicKeys::new( account_address, concensus_public_key, diff --git a/types/src/validator_set.rs b/types/src/validator_set.rs index d58446ab8b84..474db3baaad3 100644 --- a/types/src/validator_set.rs +++ b/types/src/validator_set.rs @@ -12,6 +12,7 @@ use canonical_serialization::{ SimpleDeserializer, }; use failure::prelude::*; +#[cfg(any(test, feature = "testing"))] use proptest_derive::Arbitrary; use proto_conv::{FromProto, IntoProto}; use std::collections::btree_map::BTreeMap; @@ -32,7 +33,8 @@ pub(crate) fn validator_set_path() -> Vec { AccessPath::resource_access_vec(&validator_set_tag(), &Accesses::empty()) } -#[derive(Arbitrary, Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq)] +#[cfg_attr(any(test, feature = "testing"), derive(Arbitrary))] pub struct ValidatorSet(Vec); impl ValidatorSet { diff --git a/types/src/validator_signer.rs b/types/src/validator_signer.rs index 503faa850297..e0f2b20ec1d5 100644 --- a/types/src/validator_signer.rs +++ b/types/src/validator_signer.rs @@ -2,71 +2,52 @@ // SPDX-License-Identifier: Apache-2.0 use crate::account_address::{AccountAddress, ADDRESS_LENGTH}; -use crypto::{signing, HashValue, PrivateKey, PublicKey, Signature}; +use crypto::HashValue; use failure::Error; -use proptest::{prelude::*, sample, strategy::LazyJust}; +use nextgen_crypto::{test_utils::TEST_SEED, SeedableCryptoRng, *}; +use rand::rngs::StdRng; use std::convert::TryFrom; /// ValidatorSigner associates an author with public and private keys with helpers for signing and /// validating. This struct can be used for all signing operations including block and network /// signing, respectively. -#[derive(Debug, Clone)] -pub struct ValidatorSigner { +#[derive(Debug)] +#[cfg_attr(any(test, feature = "testing"), derive(Clone))] +pub struct ValidatorSigner { author: AccountAddress, - public_key: PublicKey, + public_key: PrivateKey::VerifyingKeyMaterial, private_key: PrivateKey, } -impl ValidatorSigner { +impl ValidatorSigner { pub fn new( - account_address: AccountAddress, - public_key: PublicKey, + opt_account_address: impl Into>, private_key: PrivateKey, ) -> Self { - ValidatorSigner { - author: account_address, - public_key, - private_key, - } - } - - /// Generate the genesis block signer information. - pub fn genesis() -> Self { - let (private_key, public_key) = signing::generate_genesis_keypair(); - Self::new(AccountAddress::from(public_key), public_key, private_key) - } - - /// Generate a random set of public and private keys and author information. - pub fn random() -> Self { - let (private_key, public_key) = signing::generate_keypair(); - ValidatorSigner { - author: AccountAddress::from(public_key), - public_key, - private_key, - } - } + let public_key: PrivateKey::VerifyingKeyMaterial = private_key.public_key(); - /// For test only - makes signer with nicely looking account address that has specified integer - /// as fist byte, and rest are zeroes - pub fn from_int(num: u8) -> Self { - let mut address = [0; ADDRESS_LENGTH]; - address[0] = num; - let (private_key, public_key) = signing::generate_keypair(); + let account_address = opt_account_address + .into() + .unwrap_or_else(|| AccountAddress::from_public_key(&public_key)); ValidatorSigner { - author: AccountAddress::try_from(&address[..]).unwrap(), + author: account_address, public_key, private_key, } } /// Constructs a signature for `message` using `private_key`. - pub fn sign_message(&self, message: HashValue) -> Result { - signing::sign_message(message, &self.private_key) + pub fn sign_message(&self, message: HashValue) -> Result { + Ok(self.private_key.sign_message(&message)) } /// Checks that `signature` is valid for `message` using `public_key`. - pub fn verify_message(&self, message: HashValue, signature: &Signature) -> Result<(), Error> { - signing::verify_signature(message, signature, &self.public_key) + pub fn verify_message( + &self, + message: HashValue, + signature: &::SignatureMaterial, + ) -> Result<(), Error> { + signature.verify(&message, &self.public_key) } /// Returns the author associated with this signer. @@ -75,76 +56,102 @@ impl ValidatorSigner { } /// Returns the public key associated with this signer. - pub fn public_key(&self) -> PublicKey { - self.public_key + pub fn public_key(&self) -> PrivateKey::VerifyingKeyMaterial { + self.public_key.clone() } } -#[allow(clippy::redundant_closure)] -pub fn arb_keypair() -> impl Strategy { - prop_oneof![ - // The no_shrink here reflects that particular keypair choices out - // of random options are irrelevant. - LazyJust::new(|| signing::generate_keypair()).no_shrink(), - LazyJust::new(|| signing::generate_genesis_keypair()), - ] +impl ValidatorSigner { + /// Generate the genesis block signer information. + pub fn genesis() -> Self { + let genesis_key = PrivateKey::genesis(); + Self::new(None, genesis_key) + } } -prop_compose! { - pub fn signer_strategy(key_pair_strategy: impl Strategy)( - keypair in key_pair_strategy) -> ValidatorSigner { - let (private_key, public_key) = keypair; - let account_address = AccountAddress::from(public_key); - ValidatorSigner::new(account_address, public_key, private_key) +impl ValidatorSigner { + /// Generate a random set of public and private keys and author + /// information. + /// This takes an optional seed, which it initializes to + /// `test_utils::TEST_SEED` if passed `None` + pub fn random(opt_rng_seed: impl for<'a> Into>) -> Self { + let mut rng = StdRng::from_seed(opt_rng_seed.into().unwrap_or(TEST_SEED)); + let private_key = PrivateKey::generate_for_testing(&mut rng); + Self::new(None, private_key) } -} -#[allow(clippy::redundant_closure)] -pub fn rand_signer() -> impl Strategy { - // random signers warrant no shrinkage. - signer_strategy(arb_keypair()).no_shrink() + /// For test only - makes signer with nicely looking account address that has specified integer + /// as fist byte, and rest are zeroes + pub fn from_int(num: u8) -> Self { + let mut address = [0; ADDRESS_LENGTH]; + address[0] = num; + let mut rng = StdRng::from_seed(TEST_SEED); + let private_key = PrivateKey::generate_for_testing(&mut rng); + Self::new(AccountAddress::try_from(&address[..]).unwrap(), private_key) + } } -#[allow(clippy::redundant_closure)] -pub fn arb_signer() -> impl Strategy { - prop_oneof![rand_signer(), LazyJust::new(|| ValidatorSigner::genesis()),] -} +#[cfg(any(test, feature = "testing"))] +pub mod proptests { + use super::*; + #[cfg(test)] + use nextgen_crypto::ed25519::*; + use proptest::{prelude::*, sample, strategy::LazyJust}; + + #[allow(clippy::redundant_closure)] + pub fn arb_signing_key( + ) -> impl Strategy { + prop_oneof![ + // The no_shrink here reflects that particular keypair choices out + // of random options are irrelevant. + LazyJust::new(|| PrivateKey::generate_for_testing(&mut StdRng::from_seed(TEST_SEED))), + LazyJust::new(|| PrivateKey::genesis()), + ] + } -fn select_keypair( - key_pairs: Vec<(PrivateKey, PublicKey)>, -) -> impl Strategy { - // no_shrink() => shrinking is not relevant as signers are equivalent. - sample::select(key_pairs).no_shrink() -} + pub fn signer_strategy( + signing_key_strategy: impl Strategy, + ) -> impl Strategy> { + signing_key_strategy.prop_map(|signing_key| ValidatorSigner::new(None, signing_key)) + } -pub fn mostly_in_keypair_pool( - key_pairs: Vec<(PrivateKey, PublicKey)>, -) -> impl Strategy { - prop::strategy::Union::new_weighted(vec![ - (9, signer_strategy(select_keypair(key_pairs)).boxed()), - (1, arb_signer().boxed()), - ]) -} + #[allow(clippy::redundant_closure)] + pub fn rand_signer( + ) -> impl Strategy> { + signer_strategy(arb_signing_key()) + } -#[cfg(test)] -mod tests { - use crate::{ - account_address::AccountAddress, - validator_signer::{arb_keypair, arb_signer, ValidatorSigner}, - }; - use crypto::HashValue; - use proptest::prelude::*; + #[allow(clippy::redundant_closure)] + pub fn arb_signer( + ) -> impl Strategy> { + prop_oneof![rand_signer(), LazyJust::new(|| ValidatorSigner::genesis()),] + } + + fn select_keypair( + keys: Vec, + ) -> impl Strategy { + sample::select(keys) + } + + pub fn mostly_in_keypair_pool( + keys: Vec, + ) -> impl Strategy> { + prop::strategy::Union::new_weighted(vec![ + (9, signer_strategy(select_keypair(keys)).boxed()), + (1, arb_signer().boxed()), + ]) + } proptest! { #[test] - fn test_new_signer(keypair in arb_keypair()){ - let (private_key, public_key) = keypair; - let signer = ValidatorSigner::new(AccountAddress::from(public_key), public_key, private_key); + fn test_new_signer(signing_key in arb_signing_key::()){ + let public_key = signing_key.public_key(); + let signer = ValidatorSigner::new(None, signing_key); prop_assert_eq!(public_key, signer.public_key()); } #[test] - fn test_signer(signer in arb_signer(), message in HashValue::arbitrary()) { + fn test_signer(signer in arb_signer::(), message in HashValue::arbitrary()) { let signature = signer.sign_message(message).unwrap(); prop_assert!(signer .verify_message(message, &signature) diff --git a/types/src/validator_verifier.rs b/types/src/validator_verifier.rs index 88e2798dfe06..73cd3bf852fd 100644 --- a/types/src/validator_verifier.rs +++ b/types/src/validator_verifier.rs @@ -2,9 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 use crate::account_address::AccountAddress; -use crypto::{signing, HashValue, PublicKey, Signature}; use failure::prelude::*; use logger::prelude::*; +use nextgen_crypto::*; use std::collections::HashMap; /// Errors possible during signature verification. @@ -37,12 +37,12 @@ pub enum VerifyError { /// Supports validation of signatures for known authors. This struct can be used for all signature /// verification operations including block and network signature verification, respectively. #[derive(Clone)] -pub struct ValidatorVerifier { - author_to_public_keys: HashMap, +pub struct ValidatorVerifier

{ + author_to_public_keys: HashMap, quorum_size: usize, } -impl ValidatorVerifier { +impl ValidatorVerifier { /// Initialize with a map of author to public key and set quorum size to default (`2f + 1`) or /// zero if `author_to_public_keys` is empty. pub fn new(author_to_public_keys: HashMap) -> Self { @@ -92,13 +92,13 @@ impl ValidatorVerifier { &self, author: AccountAddress, hash: HashValue, - signature: &Signature, + signature: &PublicKey::SignatureMaterial, ) -> std::result::Result<(), VerifyError> { let public_key = self.author_to_public_keys.get(&author); match public_key { None => Err(VerifyError::UnknownAuthor), Some(public_key) => { - if signing::verify_signature(hash, signature, public_key).is_err() { + if public_key.verify_signature(&hash, signature).is_err() { Err(VerifyError::InvalidSignature) } else { Ok(()) @@ -112,34 +112,45 @@ impl ValidatorVerifier { /// attached signatures is invalid or it does not correspond to a known author. The latter is to /// prevent malicious users from adding arbitrary content to the signature payload that would go /// unnoticed. - pub fn verify_aggregated_signature( + pub fn verify_aggregated_signature( &self, hash: HashValue, - aggregated_signature: &HashMap, - ) -> std::result::Result<(), VerifyError> { + aggregated_signature: &HashMap, + ) -> std::result::Result<(), VerifyError> + where + T: Into + Clone, + { self.check_num_of_signatures(aggregated_signature)?; for (author, signature) in aggregated_signature { - self.verify_signature(*author, hash, signature)?; + self.verify_signature(*author, hash, &signature.clone().into())?; } Ok(()) } /// This function will try batch signature verification and falls back to normal /// iterated verification if batching fails. - pub fn batch_verify_aggregated_signature( + pub fn batch_verify_aggregated_signature( &self, hash: HashValue, - aggregated_signature: &HashMap, - ) -> std::result::Result<(), VerifyError> { + aggregated_signature: &HashMap, + ) -> std::result::Result<(), VerifyError> + where + T: Into + Clone, + { self.check_num_of_signatures(aggregated_signature)?; self.check_keys(aggregated_signature)?; - let signatures = aggregated_signature.values().copied().collect(); - let public_keys = aggregated_signature - .keys() - .map(|author| *(self.author_to_public_keys.get(&author).unwrap())) - .collect(); + let keys_and_signatures: Vec<(PublicKey, PublicKey::SignatureMaterial)> = + aggregated_signature + .iter() + .flat_map(|(author, signature)| { + let sig: PublicKey::SignatureMaterial = signature.clone().into(); + self.author_to_public_keys + .get(&author) + .and_then(|pub_key| Some((pub_key.clone(), sig))) + }) + .collect(); // Fallback is required to identify the source of the problem if batching fails. - if signing::batch_verify_signatures(hash, signatures, public_keys).is_err() { + if PublicKey::batch_verify_signatures(&hash, keys_and_signatures).is_err() { let iterated_verification = self.verify_aggregated_signature(hash, aggregated_signature); match iterated_verification { @@ -154,10 +165,13 @@ impl ValidatorVerifier { } /// Ensure there are at least quorum_size and not more than maximum expected signatures. - fn check_num_of_signatures( + fn check_num_of_signatures( &self, - aggregated_signature: &HashMap, - ) -> std::result::Result<(), VerifyError> { + aggregated_signature: &HashMap, + ) -> std::result::Result<(), VerifyError> + where + T: Into + Clone, + { let num_of_signatures = aggregated_signature.len(); if num_of_signatures < self.quorum_size { return Err(VerifyError::TooFewSignatures { @@ -176,10 +190,13 @@ impl ValidatorVerifier { /// Ensure there are only known authors. According to the threshold verification policy, /// invalid public keys are not allowed. - fn check_keys( + fn check_keys( &self, - aggregated_signature: &HashMap, - ) -> std::result::Result<(), VerifyError> { + aggregated_signature: &HashMap, + ) -> std::result::Result<(), VerifyError> + where + T: Into + Clone, + { for author in aggregated_signature.keys() { if self.author_to_public_keys.get(&author) == None { return Err(VerifyError::UnknownAuthor); @@ -228,12 +245,13 @@ mod tests { validator_signer::ValidatorSigner, validator_verifier::{ValidatorVerifier, VerifyError}, }; - use crypto::{HashValue, PublicKey, Signature}; + use crypto::HashValue; + use nextgen_crypto::{ed25519::*, test_utils::TEST_SEED}; use std::collections::HashMap; #[test] fn test_validator() { - let validator_signer = ValidatorSigner::random(); + let validator_signer = ValidatorSigner::::random(TEST_SEED); let random_hash = HashValue::random(); let signature = validator_signer.sign_message(random_hash).unwrap(); let validator = @@ -242,7 +260,7 @@ mod tests { validator.verify_signature(validator_signer.author(), random_hash, &signature), Ok(()) ); - let unknown_validator_signer = ValidatorSigner::random(); + let unknown_validator_signer = ValidatorSigner::::random([1; 32]); let unknown_signature = unknown_validator_signer.sign_message(random_hash).unwrap(); assert_eq!( validator.verify_signature( @@ -260,30 +278,36 @@ mod tests { #[test] fn test_quorum_validators() { - // Generate 7 random signers. - let validator_signers: Vec = - (0..7).map(|_| ValidatorSigner::random()).collect(); + const NUM_SIGNERS: u8 = 7; + // Generate NUM_SIGNERS random signers. + let validator_signers: Vec> = (0..NUM_SIGNERS) + .map(|i| ValidatorSigner::random([i; 32])) + .collect(); let random_hash = HashValue::random(); // Create a map from authors to public keys. - let mut author_to_public_key_map: HashMap = HashMap::new(); + let mut author_to_public_key_map: HashMap = + HashMap::new(); for validator in validator_signers.iter() { author_to_public_key_map.insert(validator.author(), validator.public_key()); } // Create a map from author to signatures. - let mut author_to_signature_map: HashMap = HashMap::new(); + let mut author_to_signature_map: HashMap = HashMap::new(); for validator in validator_signers.iter() { author_to_signature_map.insert( validator.author(), - validator.sign_message(random_hash).unwrap(), + validator.sign_message(random_hash).unwrap().into(), ); } - // Let's assume our verifier needs to satisfy at least 5 signatures from the original 7. - let validator_verifier = - ValidatorVerifier::new_with_quorum_size(author_to_public_key_map, 5) - .expect("Incorrect quorum size."); + // Let's assume our verifier needs to satisfy at least 5 signatures from the original + // NUM_SIGNERS. + let validator_verifier = ValidatorVerifier::::new_with_quorum_size( + author_to_public_key_map, + 5, + ) + .expect("Incorrect quorum size."); // Check against signatures == N; this will pass. assert_eq!( @@ -293,9 +317,11 @@ mod tests { ); // Add an extra unknown signer, signatures > N; this will fail. - let unknown_validator_signer = ValidatorSigner::random(); + let unknown_validator_signer = + ValidatorSigner::::random([NUM_SIGNERS + 1; 32]); let unknown_signature = unknown_validator_signer.sign_message(random_hash).unwrap(); - author_to_signature_map.insert(unknown_validator_signer.author(), unknown_signature); + author_to_signature_map + .insert(unknown_validator_signer.author(), unknown_signature.clone()); assert_eq!( validator_verifier .batch_verify_aggregated_signature(random_hash, &author_to_signature_map), @@ -321,7 +347,8 @@ mod tests { // Add an unknown signer, but quorum is satisfied and signatures <= N; this will fail as we // don't tolerate invalid signatures. - author_to_signature_map.insert(unknown_validator_signer.author(), unknown_signature); + author_to_signature_map + .insert(unknown_validator_signer.author(), unknown_signature.clone()); assert_eq!( validator_verifier .batch_verify_aggregated_signature(random_hash, &author_to_signature_map),