diff --git a/tools/config/src/legacy_config.rs b/tools/config/src/legacy_config.rs index 9eb2d1bad..c5971dce0 100644 --- a/tools/config/src/legacy_config.rs +++ b/tools/config/src/legacy_config.rs @@ -51,7 +51,12 @@ pub async fn wizard( let client = Client::new(url); if client.get_index().await.is_ok() { - address = client.lookup_originating_address(authkey).await?; + // look for actual address (i.e. may have rotated key or be an OG account) + // Should not abort if account cannot be found. + match client.lookup_originating_address(authkey).await { + Ok(a) => address = a, + Err(_) => println!("INFO: could not find this address or authkey on chain. Maybe it has never been initialized? Have someone send a transaction to it."), + }; } let cfg = AppCfg::init_app_configs(authkey, address, config_dir, chain_name, Some(np))?; diff --git a/tools/txs/src/lib.rs b/tools/txs/src/lib.rs index b036d99e6..15255e153 100644 --- a/tools/txs/src/lib.rs +++ b/tools/txs/src/lib.rs @@ -6,5 +6,5 @@ pub mod tower; pub mod transfer; pub mod txs_cli; pub mod txs_cli_upgrade; +pub mod txs_cli_user; pub mod txs_cli_vals; -// pub mod generate_transaction; diff --git a/tools/txs/src/txs_cli.rs b/tools/txs/src/txs_cli.rs index cd1e2358b..240e7ae96 100644 --- a/tools/txs/src/txs_cli.rs +++ b/tools/txs/src/txs_cli.rs @@ -1,4 +1,5 @@ use crate::txs_cli_upgrade::UpgradeTxs; +use crate::txs_cli_user::UserTxs; use crate::txs_cli_vals::ValidatorTxs; use crate::{publish::encode_publish_payload, submit_transaction::Sender}; use std::path::PathBuf; @@ -68,6 +69,9 @@ pub struct TxsCli { #[derive(clap::Subcommand)] pub enum TxsSub { + #[clap(subcommand)] + /// User account utils + User(UserTxs), #[clap(subcommand)] /// Validator configuration transactions Validator(ValidatorTxs), @@ -187,6 +191,7 @@ impl TxsCli { }) => send.generic(function_id, ty_args, args).await, Some(TxsSub::Validator(val_txs)) => val_txs.run(&mut send).await, Some(TxsSub::Upgrade(upgrade_txs)) => upgrade_txs.run(&mut send).await, + Some(TxsSub::User(user_txs)) => user_txs.run(&mut send).await, _ => { println!( "\nI'm searching, though I don't succeed\n diff --git a/tools/txs/src/txs_cli_user.rs b/tools/txs/src/txs_cli_user.rs new file mode 100644 index 000000000..c0409f963 --- /dev/null +++ b/tools/txs/src/txs_cli_user.rs @@ -0,0 +1,111 @@ +//! Validator subcommands + +use crate::submit_transaction::Sender; +use diem::common::types::RotationProofChallenge; +use diem_sdk::crypto::{PrivateKey, SigningKey, ValidCryptoMaterialStringExt}; +use diem_types::{ + account_address::AccountAddress, account_config::CORE_CODE_ADDRESS, + transaction::TransactionPayload, +}; +use libra_cached_packages::libra_stdlib::account_rotate_authentication_key; +use libra_types::{ + exports::{AuthenticationKey, Ed25519PrivateKey}, + type_extensions::client_ext::ClientExt, +}; +use libra_wallet::account_keys::get_keys_from_prompt; + +#[derive(clap::Subcommand)] +pub enum UserTxs { + RotateKey(RotateKeyTx), +} + +#[derive(clap::Args)] +pub struct RotateKeyTx { + #[clap(short, long)] + /// The new authkey to be used + new_private_key: Option, // Dev NOTE: account address has the same bytes as AuthKey +} + +impl UserTxs { + pub async fn run(&self, sender: &mut Sender) -> anyhow::Result<()> { + match &self { + UserTxs::RotateKey(tx) => match tx.run(sender).await { + Ok(_) => println!("SUCCESS: privated key rotated"), + Err(e) => { + println!("ERROR: could not rotate private key, message: {}", e); + } + }, + } + + Ok(()) + } +} + +impl RotateKeyTx { + pub async fn run(&self, sender: &mut Sender) -> anyhow::Result<()> { + let user_account: AccountAddress = sender.local_account.address(); + + let new_private_key = if let Some(pk) = &self.new_private_key { + Ed25519PrivateKey::from_encoded_string(pk)? + } else { + let legacy = get_keys_from_prompt()?; + legacy.child_0_owner.pri_key + }; + + let seq = sender.client().get_sequence_number(user_account).await?; + let payload = rotate_key( + user_account, + sender.local_account.private_key().to_owned(), + sender.local_account.authentication_key(), + seq, + new_private_key, + )?; + + sender.sign_submit_wait(payload).await?; + Ok(()) + } +} + +/// create the TransactionPayload for a key rotation (a signed rotation challenge) +pub fn rotate_key( + sender_address: AccountAddress, + current_private_key: Ed25519PrivateKey, + auth_key: AuthenticationKey, + sequence_number: u64, + new_private_key: Ed25519PrivateKey, +) -> anyhow::Result { + // form a rotation proof challence. See account.move + let rotation_proof = RotationProofChallenge { + account_address: CORE_CODE_ADDRESS, + module_name: "account".to_string(), + struct_name: "RotationProofChallenge".to_string(), + sequence_number, + originator: sender_address, + current_auth_key: AccountAddress::from_bytes(auth_key)?, + new_public_key: new_private_key.public_key().to_bytes().to_vec(), + }; + + // get the bytes of the challenge + let rotation_msg = bcs::to_bytes(&rotation_proof)?; + + // Signs the struct using both the current private key and the next private key + let rotation_proof_signed_by_current_private_key = + current_private_key.sign_arbitrary_message(&rotation_msg); + let rotation_proof_signed_by_new_private_key = + new_private_key.sign_arbitrary_message(&rotation_msg); + + let payload = account_rotate_authentication_key( + 0, + // Existing public key + current_private_key.public_key().to_bytes().to_vec(), + 0, + // New public key + new_private_key.public_key().to_bytes().to_vec(), + rotation_proof_signed_by_current_private_key + .to_bytes() + .to_vec(), + rotation_proof_signed_by_new_private_key.to_bytes().to_vec(), + ); + + Ok(payload) +}