From a69cbef503adfd9011ded402e4db7f325504c49c Mon Sep 17 00:00:00 2001 From: Greg Nazario Date: Thu, 12 May 2022 12:01:57 -0700 Subject: [PATCH] [aptos-cli] Output Validator and VFN identity files for genesis (#926) Closes: #926 --- config/src/config/mod.rs | 11 ++- config/src/config/network_config.rs | 15 +++- config/src/keys.rs | 4 +- .../safety-rules/src/safety_rules_manager.rs | 8 +- crates/aptos/Cargo.toml | 2 +- crates/aptos/src/genesis/config.rs | 10 ++- crates/aptos/src/genesis/keys.rs | 76 ++++++++++++++----- 7 files changed, 92 insertions(+), 34 deletions(-) diff --git a/config/src/config/mod.rs b/config/src/config/mod.rs index 741686a8539e8..f88b1cf625998 100644 --- a/config/src/config/mod.rs +++ b/config/src/config/mod.rs @@ -160,12 +160,19 @@ impl WaypointConfig { } } +/// A single struct for reading / writing to a file for identity across config #[derive(Deserialize, Serialize)] pub struct IdentityBlob { - pub account_address: AccountAddress, - pub account_key: Ed25519PrivateKey, + /// Optional account address. Used for validators and validator full nodes + #[serde(skip_serializing_if = "Option::is_none")] + pub account_address: Option, + /// Optional account key. Only used for validators + #[serde(skip_serializing_if = "Option::is_none")] + pub account_key: Option, /// Optional consensus key. Only used for validators + #[serde(skip_serializing_if = "Option::is_none")] pub consensus_key: Option, + /// Network private key. Peer id is derived from this if account address is not present pub network_key: x25519::PrivateKey, } diff --git a/config/src/config/network_config.rs b/config/src/config/network_config.rs index 3fddc3c957f3a..eead31a7dbff3 100644 --- a/config/src/config/network_config.rs +++ b/config/src/config/network_config.rs @@ -10,7 +10,8 @@ use crate::{ use aptos_crypto::{x25519, Uniform}; use aptos_secure_storage::{CryptoStorage, KVStorage, Storage}; use aptos_types::{ - network_address::NetworkAddress, transaction::authenticator::AuthenticationKey, PeerId, + account_address::from_identity_public_key, network_address::NetworkAddress, + transaction::authenticator::AuthenticationKey, PeerId, }; use rand::{ rngs::{OsRng, StdRng}, @@ -138,7 +139,7 @@ impl NetworkConfig { impl NetworkConfig { pub fn identity_key(&self) -> x25519::PrivateKey { let key = match &self.identity { - Identity::FromConfig(config) => Some(config.key.clone().key), + Identity::FromConfig(config) => Some(config.key.private_key()), Identity::FromStorage(config) => { let storage: Storage = (&config.backend).into(); let key = storage @@ -218,7 +219,15 @@ impl NetworkConfig { } Identity::FromFile(config) => { let identity_blob: IdentityBlob = IdentityBlob::from_file(&config.path).unwrap(); - Some(identity_blob.account_address) + + // If account is not specified, generate peer id from public key + if let Some(address) = identity_blob.account_address { + Some(address) + } else { + Some(from_identity_public_key( + identity_blob.network_key.public_key(), + )) + } } Identity::None => None, } diff --git a/config/src/keys.rs b/config/src/keys.rs index 6a153200ccc23..c3aaccc30da0d 100644 --- a/config/src/keys.rs +++ b/config/src/keys.rs @@ -23,11 +23,11 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize}; #[derive(Debug, Deserialize, Serialize)] pub struct ConfigKey { #[serde(bound(deserialize = "T: Deserialize<'de>"))] - pub(crate) key: T, + key: T, } impl ConfigKey { - pub(crate) fn new(key: T) -> Self { + pub fn new(key: T) -> Self { Self { key } } diff --git a/consensus/safety-rules/src/safety_rules_manager.rs b/consensus/safety-rules/src/safety_rules_manager.rs index 46da2279cafdb..1452e4d013d68 100644 --- a/consensus/safety-rules/src/safety_rules_manager.rs +++ b/consensus/safety-rules/src/safety_rules_manager.rs @@ -62,11 +62,15 @@ pub fn storage(config: &SafetyRulesConfig) -> PersistentSafetyStorage { backend.try_into().expect("Unable to initialize storage"); PersistentSafetyStorage::initialize( internal_storage, - identity_blob.account_address, + identity_blob + .account_address + .expect("AccountAddress needed for safety rules"), identity_blob .consensus_key .expect("Consensus key needed for safety rules"), - identity_blob.account_key, + identity_blob + .account_key + .expect("Account key needed for safety rules"), waypoint, config.enable_cached_safety_data, ) diff --git a/crates/aptos/Cargo.toml b/crates/aptos/Cargo.toml index 5fcb55f42bd3c..774c63215b45a 100644 --- a/crates/aptos/Cargo.toml +++ b/crates/aptos/Cargo.toml @@ -41,7 +41,7 @@ toml = "0.5.9" uuid = { version = "1.0.0", features = ["v4", "serde"] } aptos-config = { path = "../../config" } -aptos-crypto = { path = "../aptos-crypto" } +aptos-crypto = { path = "../aptos-crypto", features = [] } aptos-github-client = { path = "../../secure/storage/github" } aptos-logger = { path = "../aptos-logger" } aptos-management = { path = "../../config/management" } diff --git a/crates/aptos/src/genesis/config.rs b/crates/aptos/src/genesis/config.rs index eae9f71a6ff83..93cb3d7804c4b 100644 --- a/crates/aptos/src/genesis/config.rs +++ b/crates/aptos/src/genesis/config.rs @@ -59,10 +59,12 @@ pub struct ValidatorConfiguration { pub consensus_key: Ed25519PublicKey, /// Key used for signing transactions with the account pub account_key: Ed25519PublicKey, - /// Public key used for network identity (same as account address) - pub network_key: x25519::PublicKey, + /// Public key used for validator network identity (same as account address) + pub validator_network_key: x25519::PublicKey, /// Host for validator which can be an IP or a DNS name pub validator_host: HostAndPort, + /// Public key used for full node network identity (same as account address) + pub full_node_network_key: x25519::PublicKey, /// Host for full node which can be an IP or a DNS name and is optional pub full_node_host: Option, /// Stake amount for consensus @@ -76,11 +78,11 @@ impl TryFrom for Validator { let auth_key = AuthenticationKey::ed25519(&config.account_key); let validator_addresses = vec![config .validator_host - .as_network_address(config.network_key) + .as_network_address(config.validator_network_key) .unwrap()]; let full_node_addresses = if let Some(full_node_host) = config.full_node_host { vec![full_node_host - .as_network_address(config.network_key) + .as_network_address(config.full_node_network_key) .unwrap()] } else { vec![] diff --git a/crates/aptos/src/genesis/keys.rs b/crates/aptos/src/genesis/keys.rs index 197e1301e8f43..c8b02e1d8805e 100644 --- a/crates/aptos/src/genesis/keys.rs +++ b/crates/aptos/src/genesis/keys.rs @@ -3,8 +3,8 @@ use crate::{ common::{ - types::{CliError, CliTypedResult}, - utils::{read_from_file, write_to_file}, + types::{CliError, CliTypedResult, PromptOptions}, + utils::{check_if_file_exists, read_from_file, write_to_file}, }, genesis::{ config::{HostAndPort, ValidatorConfiguration}, @@ -13,6 +13,7 @@ use crate::{ op::key, CliCommand, }; +use aptos_config::{config::IdentityBlob, keys::ConfigKey}; use aptos_crypto::{ed25519::Ed25519PrivateKey, x25519, PrivateKey}; use aptos_types::transaction::authenticator::AuthenticationKey; use async_trait::async_trait; @@ -22,39 +23,64 @@ use serde::{Deserialize, Serialize}; use std::path::PathBuf; const PRIVATE_KEYS_FILE: &str = "private-keys.yaml"; +const VALIDATOR_FILE: &str = "validator-identity.yaml"; +const VFN_FILE: &str = "validator-full-node-identity.yaml"; /// Generate account key, consensus key, and network key for a validator #[derive(Parser)] pub struct GenerateKeys { + #[clap(flatten)] + prompt_options: PromptOptions, /// Output path for the three keys #[clap(long, parse(from_os_str), default_value = ".")] output_dir: PathBuf, } #[async_trait] -impl CliCommand for GenerateKeys { +impl CliCommand> for GenerateKeys { fn command_name(&self) -> &'static str { "GenerateKeys" } - async fn execute(self) -> CliTypedResult { - let account_key = key::GenerateKey::generate_ed25519_in_memory(); - let consensus_key = key::GenerateKey::generate_ed25519_in_memory(); - // Start network key based off of the account key, we can update it later - let network_key = - x25519::PrivateKey::from_ed25519_private_bytes(&account_key.to_bytes()).unwrap(); + async fn execute(self) -> CliTypedResult> { let keys_file = self.output_dir.join(PRIVATE_KEYS_FILE); + let validator_file = self.output_dir.join(VALIDATOR_FILE); + let vfn_file = self.output_dir.join(VFN_FILE); + check_if_file_exists(keys_file.as_path(), self.prompt_options)?; + check_if_file_exists(validator_file.as_path(), self.prompt_options)?; + check_if_file_exists(vfn_file.as_path(), self.prompt_options)?; + + let account_key = ConfigKey::new(key::GenerateKey::generate_ed25519_in_memory()); + let consensus_key = ConfigKey::new(key::GenerateKey::generate_ed25519_in_memory()); + let validator_network_key = ConfigKey::new(key::GenerateKey::generate_x25519_in_memory()?); + let full_node_network_key = ConfigKey::new(key::GenerateKey::generate_x25519_in_memory()?); let account_address = AuthenticationKey::ed25519(&account_key.public_key()).derived_address(); - let config = KeysAndAccount { + // Build these for use later as node identity + let validator_blob = IdentityBlob { + account_address: Some(account_address), + account_key: Some(account_key.private_key()), + consensus_key: Some(consensus_key.private_key()), + network_key: validator_network_key.private_key(), + }; + let vfn_blob = IdentityBlob { + account_address: Some(account_address), + account_key: None, + consensus_key: None, + network_key: full_node_network_key.private_key(), + }; + + let config = PrivateIdentity { account_address, - account_key, - consensus_key, - network_key, + account_key: account_key.private_key(), + consensus_key: consensus_key.private_key(), + full_node_network_key: full_node_network_key.private_key(), + validator_network_key: validator_network_key.private_key(), }; + // Create the directory if it doesn't exist if !self.output_dir.exists() || !self.output_dir.is_dir() { std::fs::create_dir(&self.output_dir) .map_err(|e| CliError::IO(self.output_dir.to_str().unwrap().to_string(), e))? @@ -62,19 +88,27 @@ impl CliCommand for GenerateKeys { write_to_file( keys_file.as_path(), - "private_keys.yaml", + PRIVATE_KEYS_FILE, to_yaml(&config)?.as_bytes(), )?; - Ok(keys_file) + write_to_file( + validator_file.as_path(), + VALIDATOR_FILE, + to_yaml(&validator_blob)?.as_bytes(), + )?; + write_to_file(vfn_file.as_path(), VFN_FILE, to_yaml(&vfn_blob)?.as_bytes())?; + Ok(vec![keys_file, validator_file, vfn_file]) } } +/// Type for serializing private keys file #[derive(Deserialize, Serialize)] -pub struct KeysAndAccount { +pub struct PrivateIdentity { account_address: AccountAddress, account_key: Ed25519PrivateKey, consensus_key: Ed25519PrivateKey, - network_key: x25519::PrivateKey, + full_node_network_key: x25519::PrivateKey, + validator_network_key: x25519::PrivateKey, } /// Set ValidatorConfiguration for a single validator in the git repository @@ -108,19 +142,21 @@ impl CliCommand<()> for SetValidatorConfiguration { async fn execute(self) -> CliTypedResult<()> { let private_keys_path = self.keys_dir.join(PRIVATE_KEYS_FILE); let bytes = read_from_file(private_keys_path.as_path())?; - let key_files: KeysAndAccount = + let key_files: PrivateIdentity = from_yaml(&String::from_utf8(bytes).map_err(CliError::from)?)?; let account_address = key_files.account_address; let account_key = key_files.account_key.public_key(); let consensus_key = key_files.consensus_key.public_key(); - let network_key = key_files.network_key.public_key(); + let validator_network_key = key_files.validator_network_key.public_key(); + let full_node_network_key = key_files.full_node_network_key.public_key(); let credentials = ValidatorConfiguration { account_address, consensus_key, account_key, - network_key, + validator_network_key, validator_host: self.validator_host, + full_node_network_key, full_node_host: self.full_node_host, stake_amount: self.stake_amount, };