From 5372bd27ecdbc1e7a81e6afde5d8416861f41de3 Mon Sep 17 00:00:00 2001 From: David Wolinsky Date: Tue, 1 Sep 2020 21:08:58 -0700 Subject: [PATCH] [management][breaking] Add support for a distinct treasury root key Management now supports uploading a distinct treasury root key. Also updated the layout to map to the reality that libra and treasury root will only have a single owner. Because of smoke tests reliance on treasury and libra root being the same... management tests unfortunately do as well (see initialize in storage_helper) --- config/global-constants/src/lib.rs | 1 + config/management/genesis/src/command.rs | 27 ++++++++++++-- .../management/genesis/src/config_builder.rs | 12 ++++--- config/management/genesis/src/genesis.rs | 12 +++++-- config/management/genesis/src/key.rs | 13 +++++++ config/management/genesis/src/layout.rs | 9 +++-- .../management/genesis/src/storage_helper.rs | 33 ++++++++++++++++- secure/storage/src/crypto_storage.rs | 2 +- testsuite/cluster-test/src/cluster_builder.rs | 31 ++++++++++++++-- testsuite/cluster-test/src/genesis_helper.rs | 35 +++++++++++++++++++ 10 files changed, 159 insertions(+), 16 deletions(-) diff --git a/config/global-constants/src/lib.rs b/config/global-constants/src/lib.rs index 2a5eed5df7717..7a1171ae28e6e 100644 --- a/config/global-constants/src/lib.rs +++ b/config/global-constants/src/lib.rs @@ -13,6 +13,7 @@ pub const CONSENSUS_KEY: &str = "consensus"; pub const EXECUTION_KEY: &str = "execution"; pub const FULLNODE_NETWORK_KEY: &str = "fullnode_network"; pub const LIBRA_ROOT_KEY: &str = "libra_root"; +pub const TREASURY_COMPLIANCE_KEY: &str = "treasury_compliance"; pub const OPERATOR_ACCOUNT: &str = "operator_account"; pub const OPERATOR_KEY: &str = "operator"; pub const OWNER_ACCOUNT: &str = "owner_account"; diff --git a/config/management/genesis/src/command.rs b/config/management/genesis/src/command.rs index 7a892f058627f..f7667fd1148cb 100644 --- a/config/management/genesis/src/command.rs +++ b/config/management/genesis/src/command.rs @@ -24,6 +24,8 @@ pub enum Command { SetLayout(crate::layout::SetLayout), #[structopt(about = "Sets the validator operator chosen by the owner")] SetOperator(crate::validator_operator::ValidatorOperator), + #[structopt(about = "Submits an Ed25519PublicKey for the treasury root")] + TreasuryComplianceKey(crate::key::TreasuryComplianceKey), #[structopt(about = "Constructs and signs a ValidatorConfig")] ValidatorConfig(crate::validator_config::ValidatorConfig), #[structopt(about = "Verifies and prints the current configuration state")] @@ -40,6 +42,7 @@ pub enum CommandName { OwnerKey, SetLayout, SetOperator, + TreasuryComplianceKey, ValidatorConfig, Verify, } @@ -55,6 +58,7 @@ impl From<&Command> for CommandName { Command::OwnerKey(_) => CommandName::OwnerKey, Command::SetLayout(_) => CommandName::SetLayout, Command::SetOperator(_) => CommandName::SetOperator, + Command::TreasuryComplianceKey(_) => CommandName::TreasuryComplianceKey, Command::ValidatorConfig(_) => CommandName::ValidatorConfig, Command::Verify(_) => CommandName::Verify, } @@ -72,6 +76,7 @@ impl std::fmt::Display for CommandName { CommandName::OwnerKey => "owner-key", CommandName::SetLayout => "set-layout", CommandName::SetOperator => "set-operator", + CommandName::TreasuryComplianceKey => "treasury-compliance-key", CommandName::ValidatorConfig => "validator-config", CommandName::Verify => "verify", }; @@ -92,6 +97,9 @@ impl Command { Command::OwnerKey(_) => self.owner_key().unwrap().to_string(), Command::SetLayout(_) => self.set_layout().unwrap().to_string(), Command::SetOperator(_) => format!("{:?}", self.set_operator().unwrap()), + Command::TreasuryComplianceKey(_) => { + self.treasury_compliance_key().unwrap().to_string() + } Command::ValidatorConfig(_) => format!("{:?}", self.validator_config().unwrap()), Command::Verify(_) => self.verify().unwrap(), } @@ -133,6 +141,14 @@ impl Command { execute_command!(self, Command::SetOperator, CommandName::SetOperator) } + pub fn treasury_compliance_key(self) -> Result { + execute_command!( + self, + Command::TreasuryComplianceKey, + CommandName::TreasuryComplianceKey + ) + } + pub fn validator_config(self) -> Result { execute_command!(self, Command::ValidatorConfig, CommandName::ValidatorConfig) } @@ -189,7 +205,8 @@ pub mod tests { let layout_text = "\ operators = [\"operator_alice_shared\", \"operator_bob_shared\", \"operator_carol_shared\"]\n\ owners = [\"alice_shared\", \"bob_shared\", \"carol_shared\"]\n\ - libra_root = [\"dave_shared\"]\n\ + libra_root = \"dave_shared\"\n\ + treasury_compliance = \"dave_shared\"\n\ "; let temppath = libra_temppath::TempPath::new(); @@ -203,11 +220,14 @@ pub mod tests { .set_layout(temppath.path().to_str().unwrap(), constants::COMMON_NS) .unwrap(); - // Step 2) Upload the libra root key: + // Step 2) Upload the root keys: helper.initialize(dave_ns.into()); helper .libra_root_key(dave_ns, &(dave_ns.to_string() + shared)) .unwrap(); + helper + .treasury_compliance_key(dave_ns, &(dave_ns.to_string() + shared)) + .unwrap(); // Step 3) Upload each owner key: for ns in [alice_ns, bob_ns, carol_ns].iter() { @@ -290,7 +310,8 @@ pub mod tests { let layout_text = "\ operators = [\"alice\", \"bob\"]\n\ owners = [\"carol\"]\n\ - libra_root = [\"dave\"]\n\ + libra_root = \"dave\"\n\ + treasury_compliance = \"other_dave\"\n\ "; file.write_all(&layout_text.to_string().into_bytes()) .unwrap(); diff --git a/config/management/genesis/src/config_builder.rs b/config/management/genesis/src/config_builder.rs index 6d2b3004c27a4..16c729c94e6bb 100644 --- a/config/management/genesis/src/config_builder.rs +++ b/config/management/genesis/src/config_builder.rs @@ -58,7 +58,8 @@ impl> ValidatorBuilder { /// Association uploads the validator layout to shared storage. fn create_layout(&self) { let mut layout = Layout::default(); - layout.libra_root = vec![LIBRA_ROOT_SHARED_NS.into()]; + layout.libra_root = LIBRA_ROOT_SHARED_NS.into(); + layout.treasury_compliance = LIBRA_ROOT_SHARED_NS.into(); layout.owners = (0..self.num_validators) .map(|i| (i.to_string() + OWNER_SHARED_NS)) .collect(); @@ -71,12 +72,15 @@ impl> ValidatorBuilder { common_storage.set(LAYOUT, layout_value).unwrap(); } - /// Association initializes its account and the libra root key. - fn create_libra_root(&self) { + /// Root initializes libra root and treasury root keys. + fn create_root(&self) { self.storage_helper.initialize(LIBRA_ROOT_NS.into()); self.storage_helper .libra_root_key(LIBRA_ROOT_NS, LIBRA_ROOT_SHARED_NS) .unwrap(); + self.storage_helper + .treasury_compliance_key(LIBRA_ROOT_NS, LIBRA_ROOT_SHARED_NS) + .unwrap(); } /// Generate owner key locally and upload to shared storage. @@ -191,7 +195,7 @@ impl> ValidatorBuilder { impl> BuildSwarm for ValidatorBuilder { fn build_swarm(&self) -> anyhow::Result<(Vec, Ed25519PrivateKey)> { self.create_layout(); - self.create_libra_root(); + self.create_root(); let libra_root_key = self .storage_helper .storage(LIBRA_ROOT_NS.into()) diff --git a/config/management/genesis/src/genesis.rs b/config/management/genesis/src/genesis.rs index 301f15a2c8d1f..b85ac9870ed4c 100644 --- a/config/management/genesis/src/genesis.rs +++ b/config/management/genesis/src/genesis.rs @@ -41,12 +41,13 @@ impl Genesis { pub fn execute(self) -> Result { let layout = self.layout()?; let libra_root_key = self.libra_root_key(&layout)?; + let treasury_compliance_key = self.treasury_compliance_key(&layout)?; let operator_assignments = self.operator_assignments(&layout)?; let operator_registrations = self.operator_registrations(&layout)?; let genesis = vm_genesis::encode_genesis_transaction( - libra_root_key.clone(), libra_root_key, + treasury_compliance_key, &operator_assignments, &operator_registrations, // TODO: swap back by 8/15 @@ -73,7 +74,7 @@ impl Genesis { /// only supports a single libra root key. pub fn libra_root_key(&self, layout: &Layout) -> Result { let config = self.config()?; - let storage = config.shared_backend_with_namespace(layout.libra_root[0].clone()); + let storage = config.shared_backend_with_namespace(layout.libra_root.clone()); storage.ed25519_key(LIBRA_ROOT_KEY) } @@ -134,4 +135,11 @@ impl Genesis { Ok(registrations) } + + /// Retrieves the treasury root key from the remote storage. + pub fn treasury_compliance_key(&self, layout: &Layout) -> Result { + let config = self.config()?; + let storage = config.shared_backend_with_namespace(layout.libra_root.clone()); + storage.ed25519_key(libra_global_constants::TREASURY_COMPLIANCE_KEY) + } } diff --git a/config/management/genesis/src/key.rs b/config/management/genesis/src/key.rs index 7802298223583..8ec7c9ac734ff 100644 --- a/config/management/genesis/src/key.rs +++ b/config/management/genesis/src/key.rs @@ -119,6 +119,19 @@ impl OwnerKey { } } +#[derive(Debug, StructOpt)] +pub struct TreasuryComplianceKey { + #[structopt(flatten)] + key: Key, +} + +impl TreasuryComplianceKey { + pub fn execute(self) -> Result { + self.key + .submit_key(libra_global_constants::TREASURY_COMPLIANCE_KEY, None) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/config/management/genesis/src/layout.rs b/config/management/genesis/src/layout.rs index bf114ce04a690..d373c34224cb8 100644 --- a/config/management/genesis/src/layout.rs +++ b/config/management/genesis/src/layout.rs @@ -19,7 +19,8 @@ use structopt::StructOpt; pub struct Layout { pub operators: Vec, pub owners: Vec, - pub libra_root: Vec, + pub libra_root: String, + pub treasury_compliance: String, } impl Layout { @@ -81,7 +82,8 @@ mod tests { let contents = "\ operators = [\"alice\", \"bob\"]\n\ owners = [\"carol\"]\n\ - libra_root = [\"dave\"]\n\ + libra_root = \"dave\"\n\ + treasury_compliance = \"other_dave\"\n\ "; let layout = Layout::parse(contents).unwrap(); @@ -90,6 +92,7 @@ mod tests { vec!["alice".to_string(), "bob".to_string()] ); assert_eq!(layout.owners, vec!["carol".to_string()]); - assert_eq!(layout.libra_root, vec!["dave".to_string()]); + assert_eq!(layout.libra_root, "dave"); + assert_eq!(layout.treasury_compliance, "other_dave"); } } diff --git a/config/management/genesis/src/storage_helper.rs b/config/management/genesis/src/storage_helper.rs index 6d0dfd7184c89..e929f48846637 100644 --- a/config/management/genesis/src/storage_helper.rs +++ b/config/management/genesis/src/storage_helper.rs @@ -8,7 +8,7 @@ use consensus_types::safety_data::SafetyData; use libra_crypto::ed25519::Ed25519PublicKey; use libra_global_constants::{ CONSENSUS_KEY, EXECUTION_KEY, FULLNODE_NETWORK_KEY, LIBRA_ROOT_KEY, OPERATOR_KEY, OWNER_KEY, - SAFETY_DATA, VALIDATOR_NETWORK_KEY, WAYPOINT, + SAFETY_DATA, TREASURY_COMPLIANCE_KEY, VALIDATOR_NETWORK_KEY, WAYPOINT, }; use libra_management::{error::Error, secure_backend::DISK}; use libra_network_address::NetworkAddress; @@ -47,6 +47,11 @@ impl StorageHelper { // Initialize all keys in storage storage.create_key(LIBRA_ROOT_KEY).unwrap(); + // TODO(davidiw) use distinct keys in tests for treasury and libra root keys + let libra_root_key = storage.export_private_key(LIBRA_ROOT_KEY).unwrap(); + storage + .import_private_key(TREASURY_COMPLIANCE_KEY, libra_root_key) + .unwrap(); storage.create_key(CONSENSUS_KEY).unwrap(); storage.create_key(EXECUTION_KEY).unwrap(); storage.create_key(FULLNODE_NETWORK_KEY).unwrap(); @@ -234,6 +239,32 @@ impl StorageHelper { command.set_operator() } + pub fn treasury_compliance_key( + &self, + validator_ns: &str, + shared_ns: &str, + ) -> Result { + let args = format!( + " + libra-genesis-tool + treasury-compliance-key + --validator-backend backend={backend};\ + path={path};\ + namespace={validator_ns} + --shared-backend backend={backend};\ + path={path};\ + namespace={shared_ns} + ", + backend = DISK, + path = self.path_string(), + validator_ns = validator_ns, + shared_ns = shared_ns, + ); + + let command = Command::from_iter(args.split_whitespace()); + command.treasury_compliance_key() + } + pub fn validator_config( &self, owner_name: &str, diff --git a/secure/storage/src/crypto_storage.rs b/secure/storage/src/crypto_storage.rs index 80d459a3ad7a0..dfe3d8d801aea 100644 --- a/secure/storage/src/crypto_storage.rs +++ b/secure/storage/src/crypto_storage.rs @@ -21,7 +21,7 @@ pub trait CryptoStorage { /// not used correctly. As this is purely a testing API, there is no defined behavior for /// importing a key for a given name if that name already exists. It only exists to allow /// Libra to be run in test environments where a set of deterministic keys must be generated. - fn import_private_key(&mut self, _name: &str, _key: Ed25519PrivateKey) -> Result<(), Error>; + fn import_private_key(&mut self, name: &str, key: Ed25519PrivateKey) -> Result<(), Error>; /// Returns the Ed25519 private key stored at 'name' and identified by 'version', which is the /// corresponding public key. This may fail even if the 'named' key exists but the version is diff --git a/testsuite/cluster-test/src/cluster_builder.rs b/testsuite/cluster-test/src/cluster_builder.rs index 221a127066d20..1023c9d1baa52 100644 --- a/testsuite/cluster-test/src/cluster_builder.rs +++ b/testsuite/cluster-test/src/cluster_builder.rs @@ -26,7 +26,8 @@ use consensus_types::safety_data::SafetyData; use libra_genesis_tool::layout::Layout; use libra_global_constants::{ CONSENSUS_KEY, EXECUTION_KEY, FULLNODE_NETWORK_KEY, LIBRA_ROOT_KEY, OPERATOR_KEY, OWNER_KEY, - SAFETY_DATA, VALIDATOR_NETWORK_ADDRESS_KEYS, VALIDATOR_NETWORK_KEY, WAYPOINT, + SAFETY_DATA, TREASURY_COMPLIANCE_KEY, VALIDATOR_NETWORK_ADDRESS_KEYS, VALIDATOR_NETWORK_KEY, + WAYPOINT, }; use libra_network_address::NetworkAddress; use libra_secure_storage::{CryptoStorage, KVStorage, Storage, VaultStorage}; @@ -373,6 +374,21 @@ impl ClusterBuilder { vault_storage.create_key(LIBRA_ROOT_KEY).map_err(|e| { format_err!("Failed to create {}__{} : {}", pod_name, LIBRA_ROOT_KEY, e) })?; + let key = vault_storage + .export_private_key(LIBRA_ROOT_KEY) + .map_err(|e| { + format_err!("Failed to export {}__{} : {}", pod_name, LIBRA_ROOT_KEY, e) + })?; + vault_storage + .import_private_key(TREASURY_COMPLIANCE_KEY, key) + .map_err(|e| { + format_err!( + "Failed to import {}__{} : {}", + pod_name, + TREASURY_COMPLIANCE_KEY, + e + ) + })?; } let keys = vec![ OWNER_KEY, @@ -421,7 +437,8 @@ impl ClusterBuilder { let layout = Layout { owners: owners.clone(), operators: owners, - libra_root: vec![LIBRA_ROOT_NS.to_string()], + libra_root: LIBRA_ROOT_NS.to_string(), + treasury_compliance: LIBRA_ROOT_NS.to_string(), }; let layout_path = "/tmp/layout.yaml"; write!( @@ -459,6 +476,16 @@ impl ClusterBuilder { ) .await .map_err(|e| format_err!("Failed to libra_root_key : {}", e))?; + genesis_helper + .treasury_compliance_key( + VAULT_BACKEND, + format!("http://{}:{}", vault_nodes[0].internal_ip, VAULT_PORT).as_str(), + token_path, + LIBRA_ROOT_NS, + LIBRA_ROOT_NS, + ) + .await + .map_err(|e| format_err!("Failed to libra_root_key : {}", e))?; for (i, node) in vault_nodes.iter().enumerate() { let pod_name = validator_pod_name(i as u32); diff --git a/testsuite/cluster-test/src/genesis_helper.rs b/testsuite/cluster-test/src/genesis_helper.rs index 7ef0913f3eecc..257ed87e9a89f 100644 --- a/testsuite/cluster-test/src/genesis_helper.rs +++ b/testsuite/cluster-test/src/genesis_helper.rs @@ -146,6 +146,41 @@ impl GenesisHelper { .expect("tokio spawn_blocking runtime error") } + pub async fn treasury_compliance_key( + &self, + validator_backend: &str, + server: &str, + token_path: &str, + validator_ns: &str, + shared_ns: &str, + ) -> Result { + let args = format!( + " + libra-genesis-tool + treasury-compliance-key + --validator-backend backend={validator_backend};\ + server={server};\ + token={token_path};\ + namespace={validator_ns} + --shared-backend backend={backend};\ + path={path};\ + namespace={shared_ns} + ", + backend = DISK, + validator_backend = validator_backend, + server = server, + token_path = token_path, + path = self.path, + validator_ns = validator_ns, + shared_ns = shared_ns, + ); + + let command = Command::from_iter(args.split_whitespace()); + spawn_blocking(|| command.treasury_compliance_key()) + .await + .expect("tokio spawn_blocking runtime error") + } + pub async fn validator_config( &self, owner_name: &str,