From 9a9b65a62ea65da56524a17f54cc319523faf8f7 Mon Sep 17 00:00:00 2001 From: sirouk <8901571+sirouk@users.noreply.github.com> Date: Wed, 1 Nov 2023 16:47:59 -0400 Subject: [PATCH] [tools] fix network address registration for validators (#74) Co-authored-by: 0o-de-lally <1364012+0o-de-lally@users.noreply.github.com> --- Cargo.lock | 2 + .../sources/ol_sources/jail.move | 8 ++- .../sources/ol_sources/vouch.move | 8 +++ tools/config/Cargo.toml | 1 + tools/config/src/config_cli.rs | 55 ++++++++++++++++++- tools/query/Cargo.toml | 1 + tools/query/src/query_type.rs | 11 +--- tools/txs/src/txs_cli.rs | 2 + tools/txs/src/txs_cli_vals.rs | 13 +++-- tools/txs/tests/encode_net_addr.rs | 24 ++++++++ tools/txs/tests/fixtures/operator.yaml | 12 ++++ tools/wallet/src/account_keys.rs | 10 +++- tools/wallet/src/keys.rs | 39 ++++++++++--- tools/wallet/src/lib.rs | 1 + tools/wallet/src/utils.rs | 18 ++++-- tools/wallet/src/validator_files.rs | 1 + tools/wallet/src/wallet_cli.rs | 47 +++++----------- tools/wallet/src/whoami.rs | 31 +++++++++++ 18 files changed, 219 insertions(+), 65 deletions(-) create mode 100644 tools/txs/tests/encode_net_addr.rs create mode 100644 tools/txs/tests/fixtures/operator.yaml create mode 100644 tools/wallet/src/whoami.rs diff --git a/Cargo.lock b/Cargo.lock index e4f68f7ca..6db852fda 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5791,6 +5791,7 @@ dependencies = [ "libra-types", "libra-wallet", "reqwest", + "serde_json", "tokio", "url", ] @@ -5857,6 +5858,7 @@ dependencies = [ "anyhow", "clap 4.4.2", "diem-sdk", + "hex", "indoc", "libra-smoke-tests", "libra-types", diff --git a/framework/libra-framework/sources/ol_sources/jail.move b/framework/libra-framework/sources/ol_sources/jail.move index 8b3869564..1176dd3da 100644 --- a/framework/libra-framework/sources/ol_sources/jail.move +++ b/framework/libra-framework/sources/ol_sources/jail.move @@ -49,6 +49,11 @@ module ol_framework::jail { /// Validator is misconfigured cannot unjail. const EVALIDATOR_CONFIG: u64 = 1; + /// You are not a validator in the current set, you can't unjail anyone. + const EVOUCHER_NOT_IN_SET: u64 = 2; + + /// You not actually a valid voucher for this user. Did it expire? + const EU_NO_VOUCHER: u64 = 3; struct Jail has key { is_jailed: bool, @@ -120,12 +125,13 @@ module ol_framework::jail { error::invalid_state(EVALIDATOR_CONFIG), ); let voucher = signer::address_of(sender); + assert!(vouch::is_valid_voucher_for(voucher, addr), EU_NO_VOUCHER); let current_set = stake::get_current_validators(); let (vouchers_in_set, _) = vouch::true_friends_in_list(addr, ¤t_set); let (is_found, _idx) = vector::index_of(&vouchers_in_set, &voucher); - assert!(is_found, 100103); + assert!(is_found, EVOUCHER_NOT_IN_SET); unjail(addr); } diff --git a/framework/libra-framework/sources/ol_sources/vouch.move b/framework/libra-framework/sources/ol_sources/vouch.move index a42c05cbf..275731c8c 100644 --- a/framework/libra-framework/sources/ol_sources/vouch.move +++ b/framework/libra-framework/sources/ol_sources/vouch.move @@ -149,6 +149,14 @@ module ol_framework::vouch { filtered_ancestry } + #[view] + /// check if the user is in fact a valid voucher + public fun is_valid_voucher_for(voucher: address, recipient: address):bool + acquires MyVouches { + let list = true_friends(recipient); + vector::contains(&list, &voucher) + } + fun is_not_expired(voucher: address, state: &MyVouches): bool { let (found, i) = vector::index_of(&state.my_buddies, &voucher); diff --git a/tools/config/Cargo.toml b/tools/config/Cargo.toml index 07f5b61ee..6f44f7256 100644 --- a/tools/config/Cargo.toml +++ b/tools/config/Cargo.toml @@ -22,6 +22,7 @@ diem-types = { workspace = true } libra-types = { workspace = true } libra-wallet = { workspace = true } reqwest = { workspace = true } +serde_json = { workspace = true } tokio = { workspace = true } url = { workspace = true } diff --git a/tools/config/src/config_cli.rs b/tools/config/src/config_cli.rs index 938e3eceb..2a1fdd4ab 100644 --- a/tools/config/src/config_cli.rs +++ b/tools/config/src/config_cli.rs @@ -1,6 +1,6 @@ use crate::host::initialize_validator_configs; use crate::{legacy_config, make_profile}; -use anyhow::Result; +use anyhow::{Context, Result}; use clap::Parser; use libra_types::exports::AccountAddress; use libra_types::exports::AuthenticationKey; @@ -9,6 +9,8 @@ use libra_types::exports::NamedChain; use libra_types::global_config_dir; use libra_types::legacy_types::app_cfg::{self, AppCfg}; use libra_types::type_extensions::client_ext::ClientExt; +use libra_wallet::utils::read_operator_file; +use libra_wallet::validator_files::OPERATOR_FILE; use std::path::PathBuf; use url::Url; @@ -83,7 +85,11 @@ enum ConfigSub { workspace: bool, }, /// Generate validators' config file - ValidatorInit {}, + ValidatorInit { + /// check the files generated + #[clap(short, long, default_value = "false")] + check: bool, + }, } impl ConfigCli { @@ -160,7 +166,50 @@ impl ConfigCli { Ok(()) } - Some(ConfigSub::ValidatorInit {}) => { + Some(ConfigSub::ValidatorInit { check }) => { + if *check { + let home_dir = self.path.clone().unwrap_or_else(global_config_dir); + + let public_keys_file = home_dir.join(OPERATOR_FILE); + + let public_identity = read_operator_file(public_keys_file.as_path())?; + println!("validator public credentials:"); + println!( + "{}", + serde_json::to_string_pretty(&public_identity).unwrap() + ); + + println!("network addresses:"); + let validator_net = public_identity.validator_host; + let net_addr = validator_net + .as_network_address(public_identity.validator_network_public_key)?; + println!( + "validator: {}", + serde_json::to_string_pretty(&net_addr).unwrap() + ); + + if let Some(fn_host) = public_identity.full_node_host { + let net_addr_fn = fn_host.as_network_address( + public_identity.full_node_network_public_key.context( + "expected a full_node_network_public_key in operator.yaml", + )?, + )?; + + println!( + "vfn: {}", + serde_json::to_string_pretty(&net_addr_fn).unwrap() + ); + } else { + println!("WARN: no config information found for Validator Full Node (VFN)") + } + + println!( + "\nNOTE: to check if this matches your mnemonic try `libra wallet whoami`" + ); + + return Ok(()); + } + let data_path = global_config_dir(); if !&data_path.exists() { println!( diff --git a/tools/query/Cargo.toml b/tools/query/Cargo.toml index dc6490459..2ec4c1bb0 100644 --- a/tools/query/Cargo.toml +++ b/tools/query/Cargo.toml @@ -20,3 +20,4 @@ tokio = { workspace = true } [dev-dependencies] libra-smoke-tests = { workspace = true } +hex = { workspace = true } \ No newline at end of file diff --git a/tools/query/src/query_type.rs b/tools/query/src/query_type.rs index 0c973e49c..0b32ab199 100644 --- a/tools/query/src/query_type.rs +++ b/tools/query/src/query_type.rs @@ -2,7 +2,7 @@ use crate::{ account_queries::{get_account_balance_libra, get_tower_state, get_val_config}, query_view::get_view, }; -use anyhow::{bail, Result}; +use anyhow::{bail, Context, Result}; use diem_sdk::{rest_client::Client, types::account_address::AccountAddress}; use indoc::indoc; use libra_types::exports::AuthenticationKey; @@ -127,11 +127,6 @@ pub enum QueryType { // /// what event sequence number to start querying from, if DB does not have all. // seq_start: Option, // }, - // /// get the validator's on-chain configuration, including network discovery addresses - // ValConfig { - // /// the account of the validator - // account: AccountAddress, - // }, } impl QueryType { @@ -192,8 +187,8 @@ impl QueryType { // make this readable, turn the network address into a string Ok(json!({ "consensus_public_key": res.consensus_public_key, - "validator_network_addresses": res.validator_network_addresses().unwrap(), - "fullnode_network_addresses": res.fullnode_network_addresses().unwrap(), + "validator_network_addresses": res.validator_network_addresses().context("can't BCS decode the validator network address")?, + "fullnode_network_addresses": res.validator_network_addresses().context("can't BCS decode the fullnode network address")?, "validator_index": res.validator_index, })) } diff --git a/tools/txs/src/txs_cli.rs b/tools/txs/src/txs_cli.rs index 1f6f648b5..cd1e2358b 100644 --- a/tools/txs/src/txs_cli.rs +++ b/tools/txs/src/txs_cli.rs @@ -69,8 +69,10 @@ pub struct TxsCli { #[derive(clap::Subcommand)] pub enum TxsSub { #[clap(subcommand)] + /// Validator configuration transactions Validator(ValidatorTxs), #[clap(subcommand)] + /// Network upgrade transactions Upgrade(UpgradeTxs), /// Transfer coins between accounts. Transferring can also be used to create accounts. Transfer { diff --git a/tools/txs/src/txs_cli_vals.rs b/tools/txs/src/txs_cli_vals.rs index 220c5bfa6..e7c6eeb99 100644 --- a/tools/txs/src/txs_cli_vals.rs +++ b/tools/txs/src/txs_cli_vals.rs @@ -17,6 +17,7 @@ use libra_wallet::validator_files::OPERATOR_FILE; #[derive(clap::Subcommand)] pub enum ValidatorTxs { + /// txs proof-of-fee settings Pof { #[clap(short, long)] /// the percentage of the nominal reward you will bid to join the validator set. Numbers can include three decimal places: 1.234 is 123.4%. Note this is the maximum precision allowed in the bid (i.e. one decimal of a percent). Numbers with more decimals will be truncated (not rounded) @@ -28,11 +29,13 @@ pub enum ValidatorTxs { /// eliminates the bid. There are only a limited amount of retractions that can happen in an epoch. retract: bool, }, + /// jail and unjail transactions Jail { #[clap(short, long)] /// you are a voucher for a validator which is jailed. you are un-jailing this validator after checking that they are able to join again. unjail_acct: AccountAddress, }, + /// vouch transactions Vouch { #[clap(short, long)] /// This is an account that you are vouching for. They may not be a validator account. @@ -41,11 +44,13 @@ pub enum ValidatorTxs { /// If you are revoking the vouch for the account specified here. revoke: bool, }, + /// register as a validator Register { #[clap(short('f'), long)] /// optional, Path to files with registration files operator_file: Option, }, + /// update validator configurations Update { #[clap(short('f'), long)] /// optional, Path to files with registration files @@ -118,8 +123,8 @@ impl ValidatorTxs { ValidatorUniverseRegisterValidator { consensus_pubkey: oc.consensus_public_key.to_bytes().to_vec(), proof_of_possession: oc.consensus_proof_of_possession.to_bytes().to_vec(), - network_addresses: bcs::to_bytes(&val_net_protocol)?, - fullnode_addresses: bcs::to_bytes(&vfn_fullnode_protocol)?, + network_addresses: bcs::to_bytes(&vec![val_net_protocol])?, + fullnode_addresses: bcs::to_bytes(&vec![vfn_fullnode_protocol])?, } } ValidatorTxs::Update { operator_file } => { @@ -146,8 +151,8 @@ impl ValidatorTxs { StakeUpdateNetworkAndFullnodeAddresses { validator_address: oc.operator_account_address.into(), - new_network_addresses: bcs::to_bytes(&val_net_protocol)?, - new_fullnode_addresses: bcs::to_bytes(&vfn_fullnode_protocol)?, + new_network_addresses: bcs::to_bytes(&vec![val_net_protocol])?, + new_fullnode_addresses: bcs::to_bytes(&vec![vfn_fullnode_protocol])?, } } }; diff --git a/tools/txs/tests/encode_net_addr.rs b/tools/txs/tests/encode_net_addr.rs new file mode 100644 index 000000000..4bdf5fed1 --- /dev/null +++ b/tools/txs/tests/encode_net_addr.rs @@ -0,0 +1,24 @@ +use std::path::Path; + +use diem_genesis::config::OperatorConfiguration; +use diem_types::network_address::NetworkAddress; + +#[test] +fn encode_net_addr() -> anyhow::Result<()> { + let file = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/operator.yaml"); + + let yaml_str = std::fs::read_to_string(file)?; + + let oc: OperatorConfiguration = serde_yaml::from_str(&yaml_str)?; + + let val_net_protocol = oc + .validator_host + .as_network_address(oc.validator_network_public_key)?; + let enc = bcs::to_bytes(&val_net_protocol)?; + + // dbg!(&hex::encode(&enc)); + let dec: NetworkAddress = bcs::from_bytes(&enc)?; + assert!(dec == val_net_protocol); + + Ok(()) +} diff --git a/tools/txs/tests/fixtures/operator.yaml b/tools/txs/tests/fixtures/operator.yaml new file mode 100644 index 000000000..af6a0208e --- /dev/null +++ b/tools/txs/tests/fixtures/operator.yaml @@ -0,0 +1,12 @@ +operator_account_address: 40534327a3cfe4e93faf210b9b70e5958236a2ca6fb70eaa9d9ef351fdc2cf75 +operator_account_public_key: "0x8f73437282e51577421450e528cc91a36e3257199bb5bd4cd16c670355789cee" +consensus_public_key: "0xb0d9d43e8f0e0d8939f11529d91a3acf39b5bbfd09e102491696f804c69bd786f7865939b326b94c3131b6666acc70eb" +consensus_proof_of_possession: "0x8c788101bc1ef2f7ed7d5b663dc3031d5484f17792402ca9de033e727b037dca4bdba1c16f789e160ea23ab791c5ba3a0e8a1808279e9deca0976dd01011377661a494760cc5b560fba510d1e2e620d5de0d82305ce20d2d0ac59469d8b73f47" +validator_network_public_key: "0x64d495a02294e7a7a4f3e6c3be8c9a9f813dfd1e86a2127a2a43ddc9313bc263" +validator_host: + host: 134.209.32.159 + port: 6180 +full_node_network_public_key: "0x737f357ddc2d6fa10a4020846898083bc18a3cb5ea80ba08db8bb0095509c973" +full_node_host: + host: 134.209.32.159 + port: 6182 \ No newline at end of file diff --git a/tools/wallet/src/account_keys.rs b/tools/wallet/src/account_keys.rs index ee4357314..21df6b439 100644 --- a/tools/wallet/src/account_keys.rs +++ b/tools/wallet/src/account_keys.rs @@ -167,8 +167,14 @@ impl KeyChain { Ok(()) } - pub fn display(&self) { - eprintln!("{}", serde_json::to_string_pretty(&self).unwrap()); + pub fn display(&self, display_private: bool) { + if display_private { + eprintln!("{}", serde_json::to_string_pretty(&self).unwrap()); + } else { + let owner = &self.child_0_owner; + println!("owner account: {}", owner.account); + // TODO: include more keys to derive + } } } diff --git a/tools/wallet/src/keys.rs b/tools/wallet/src/keys.rs index 67941e916..f03ff6251 100644 --- a/tools/wallet/src/keys.rs +++ b/tools/wallet/src/keys.rs @@ -73,6 +73,37 @@ pub fn refresh_validator_files( PrivateIdentity, PublicIdentity, KeyChain, +)> { + let (validator_blob, vfn_blob, private_identity, public_identity, legacy_keys) = + make_validator_keys(mnem, keep_legacy_addr)?; + + save_val_files( + output_opt, + &validator_blob, + &vfn_blob, + &private_identity, + &public_identity, + )?; + + Ok(( + validator_blob, + vfn_blob, + private_identity, + public_identity, + legacy_keys, + )) +} + +/// create all the validator key structs from mnemonic +pub fn make_validator_keys( + mnem: Option, + keep_legacy_addr: bool, +) -> anyhow::Result<( + IdentityBlob, + IdentityBlob, + PrivateIdentity, + PublicIdentity, + KeyChain, )> { let mut legacy_keys = if let Some(m) = mnem { get_keys_from_mnem(m)? @@ -88,14 +119,6 @@ pub fn refresh_validator_files( let (validator_blob, vfn_blob, private_identity, public_identity) = generate_key_objects_from_legacy(&legacy_keys)?; - save_val_files( - output_opt, - &validator_blob, - &vfn_blob, - &private_identity, - &public_identity, - )?; - Ok(( validator_blob, vfn_blob, diff --git a/tools/wallet/src/lib.rs b/tools/wallet/src/lib.rs index ae0fb09c5..a8e037247 100644 --- a/tools/wallet/src/lib.rs +++ b/tools/wallet/src/lib.rs @@ -10,3 +10,4 @@ pub mod load_keys; pub mod utils; pub mod validator_files; pub mod wallet_cli; +pub mod whoami; diff --git a/tools/wallet/src/utils.rs b/tools/wallet/src/utils.rs index 0ad001cae..4432a7a7d 100644 --- a/tools/wallet/src/utils.rs +++ b/tools/wallet/src/utils.rs @@ -4,7 +4,7 @@ use anyhow::{anyhow, bail}; use dialoguer::Confirm; -use diem_genesis::keys::PublicIdentity; +use diem_genesis::{config::OperatorConfiguration, keys::PublicIdentity}; use serde::{de::DeserializeOwned, Serialize}; #[cfg(unix)] use std::os::unix::fs::OpenOptionsExt; @@ -25,11 +25,12 @@ pub type CliTypedResult = Result; /// Checks if a file exists, being overridden by `PromptOptions` pub fn check_if_file_exists(file: &Path) -> CliTypedResult<()> { if file.exists() { - let o: Option<&str> = option_env!("LIBRA_CI"); - if o.is_some() { - // TODO: how to make tests always overwrite? - println!("LIBRA_CI is set, overwriting {:?}", file.as_os_str()); - return Ok(()); + if let Some(env) = option_env!("LIBRA_CI") { + if !env.is_empty() { + // TODO: how to make tests always overwrite? + println!("LIBRA_CI is set, overwriting {:?}", file.as_os_str()); + return Ok(()); + } } prompt_yes_with_override(&format!( @@ -117,3 +118,8 @@ pub fn read_public_identity_file(public_identity_file: &Path) -> CliTypedResult< let bytes = read_from_file(public_identity_file)?; from_yaml(&String::from_utf8(bytes).map_err(|e| anyhow!(e))?) } + +pub fn read_operator_file(public_identity_file: &Path) -> CliTypedResult { + let bytes = read_from_file(public_identity_file)?; + from_yaml(&String::from_utf8(bytes).map_err(|e| anyhow!(e))?) +} diff --git a/tools/wallet/src/validator_files.rs b/tools/wallet/src/validator_files.rs index c30081bcd..c432e7a37 100644 --- a/tools/wallet/src/validator_files.rs +++ b/tools/wallet/src/validator_files.rs @@ -13,6 +13,7 @@ use crate::{ pub const OPERATOR_FILE: &str = "operator.yaml"; pub const OWNER_FILE: &str = "owner.yaml"; +pub const VALIDATOR_IDENTITY_FILE: &str = "validator-identity.yaml"; // copied from crate/diem/src/genesis/keys.rs pub struct SetValidatorConfiguration { diff --git a/tools/wallet/src/wallet_cli.rs b/tools/wallet/src/wallet_cli.rs index 458ea7217..ab4a98fb3 100644 --- a/tools/wallet/src/wallet_cli.rs +++ b/tools/wallet/src/wallet_cli.rs @@ -1,4 +1,4 @@ -use crate::account_keys::{self, get_keys_from_prompt}; +use crate::{account_keys, whoami::who_am_i}; use anyhow::Result; use clap::{Args, Parser, Subcommand}; @@ -26,47 +26,28 @@ enum WalletSub { output_dir: Option, }, /// Use the legacy key derivation scheme - // TODO: call this 'WalletArgs' - Legacy(LegArgs), - // TODO: add WhoAmI to display the wallet info. + Legacy, + /// use mnemonic to see what account keys are generated + Whoami(WhoamiOpts), } #[derive(Args, Debug)] -struct LegArgs { - /// display private keys and authentication keys - #[clap(short, long)] - display: bool, - - #[clap(short, long)] - /// save legacy keyscheme private keys to file - output_path: Option, - /// generate new keys and mnemonic in legacy format. It's not clear why you need this besides for testing. Note: these are not useful for creating a validator. - #[clap(short, long)] - keygen: bool, +struct WhoamiOpts { + /// show the validator configurations + #[clap(short('v'), long, default_value = "false")] + show_validator: bool, } impl WalletCli { pub async fn run(&self) -> Result<()> { match &self.command { - WalletSub::Legacy(args) => { - if !args.display && args.output_path.is_none() { - println!("pass --display to show keys and/or --output-path to save keys"); - return Ok(()); - } - - let l = if args.keygen { - account_keys::legacy_keygen(true)? - } else { - get_keys_from_prompt()? - }; - - if let Some(dir) = &args.output_path { - l.save_keys(dir)?; - } + WalletSub::Whoami(args) => { + who_am_i(args.show_validator)?; + } + WalletSub::Legacy => { + println!("this command will generate legacy keys and addresses from v5 addresses. You should only be using this for testing or debugging purposes"); - if args.display { - l.display(); - } + account_keys::legacy_keygen(true)?; } WalletSub::Keygen { mnemonic, diff --git a/tools/wallet/src/whoami.rs b/tools/wallet/src/whoami.rs new file mode 100644 index 000000000..3299e3240 --- /dev/null +++ b/tools/wallet/src/whoami.rs @@ -0,0 +1,31 @@ +use crate::keys::make_validator_keys; +use dialoguer::Confirm; + +// given a mnemonic what are all the settings which could be expected +pub fn who_am_i(show_validator: bool) -> anyhow::Result<()> { + let keep_legacy_address = Confirm::new() + .with_prompt("Is this a legacy (v5 or prior) address?") + .interact()?; + + // NOTE: we use the validator keygen so that we can optionally show that + // info + // the owner key will derive to the same. + let (_validator_blob, _vfn_blob, _private_identity, public_identity, _legacy_keys) = + make_validator_keys(None, keep_legacy_address)?; + + if show_validator { + println!("validator public credentials:"); + println!( + "{}", + serde_json::to_string_pretty(&public_identity).unwrap() + ); + } else { + println!("owner address: {}", &public_identity.account_address); + println!( + "owner authentication key: {}", + &public_identity.account_public_key + ); + } + + Ok(()) +}