From d6ada0fa78798acf446f6cab6e37e30aea3d42d8 Mon Sep 17 00:00:00 2001 From: sirouk <8901571+sirouk@users.noreply.github.com> Date: Mon, 2 Sep 2024 02:51:14 -0400 Subject: [PATCH] [move/rust] libra-framework patch 7.0.3 & AppCfg private key for testsuite (#313) Co-authored-by: xyz --- Cargo.lock | 1 + .../src/libra_framework_sdk_builder.rs | 1 - .../sources/diem_governance.move | 2 +- .../sources/ol_sources/address_utils.move | 3 + .../sources/ol_sources/epoch_boundary.move | 14 +- .../sources/ol_sources/vouch.move | 2 +- .../libra-framework/sources/randomness.move | 21 ++- framework/releases/head.mrb | Bin 890377 -> 890723 bytes smoke-tests/src/configure_validator.rs | 56 +++++--- testsuites/twin/Makefile | 122 ++++++++++++++++++ testsuites/twin/src/runner.rs | 4 +- testsuites/twin/src/setup.rs | 19 ++- tools/query/Cargo.toml | 1 + tools/query/src/query_cli.rs | 36 +++++- tools/query/src/query_type.rs | 30 ++--- tools/query/tests/query.rs | 4 +- tools/query/tests/view.rs | 2 +- tools/rescue/tests/support/mod.rs | 3 +- tools/txs/src/txs_cli.rs | 14 +- tools/txs/tests/community_wallet.rs | 3 +- tools/txs/tests/gov_script.rs | 3 +- tools/txs/tests/onboard_validator.rs | 3 +- tools/txs/tests/publish.rs | 3 +- tools/txs/tests/transfer.rs | 9 +- types/src/core_types/app_cfg.rs | 20 +-- upgrade-tests/tests/support/mod.rs | 3 +- 26 files changed, 280 insertions(+), 99 deletions(-) create mode 100644 testsuites/twin/Makefile diff --git a/Cargo.lock b/Cargo.lock index 3b2f07491..37e0cbb17 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5745,6 +5745,7 @@ dependencies = [ "libra-types", "serde_json", "tokio", + "url", ] [[package]] diff --git a/framework/cached-packages/src/libra_framework_sdk_builder.rs b/framework/cached-packages/src/libra_framework_sdk_builder.rs index 3c5903005..a3bae1d52 100644 --- a/framework/cached-packages/src/libra_framework_sdk_builder.rs +++ b/framework/cached-packages/src/libra_framework_sdk_builder.rs @@ -13,7 +13,6 @@ #![allow(dead_code)] #![allow(unused_imports)] #![allow(clippy::too_many_arguments)] - use diem_types::{ account_address::AccountAddress, transaction::{EntryFunction, TransactionPayload}, diff --git a/framework/libra-framework/sources/diem_governance.move b/framework/libra-framework/sources/diem_governance.move index ba0e5c2f1..404c77604 100644 --- a/framework/libra-framework/sources/diem_governance.move +++ b/framework/libra-framework/sources/diem_governance.move @@ -423,7 +423,7 @@ module diem_framework::diem_governance { } #[view] - // is the proposal complete and executed? + // how many votes on the proposal public fun get_votes(proposal_id: u64): (u128, u128) { voting::get_votes(@diem_framework, proposal_id) } diff --git a/framework/libra-framework/sources/ol_sources/address_utils.move b/framework/libra-framework/sources/ol_sources/address_utils.move index dd696932a..4125aa66e 100644 --- a/framework/libra-framework/sources/ol_sources/address_utils.move +++ b/framework/libra-framework/sources/ol_sources/address_utils.move @@ -33,6 +33,9 @@ module ol_framework::address_utils { // Shuffle addresses with the same values to ensure randomness position public fun shuffle_duplicates(addresses: &mut vector
, values: &vector) { + // belt and suspenders, if migration didn't happen. + // assert!(randomness::is_init(), error::invalid_state(ERANDOM_INIT_ERROR)); + assert!(vector::length(addresses) == vector::length(values), error::invalid_argument(EDIFFERENT_LENGTH)); let len = vector::length(values); let i = 0; diff --git a/framework/libra-framework/sources/ol_sources/epoch_boundary.move b/framework/libra-framework/sources/ol_sources/epoch_boundary.move index b4f65c337..f16de9b44 100644 --- a/framework/libra-framework/sources/ol_sources/epoch_boundary.move +++ b/framework/libra-framework/sources/ol_sources/epoch_boundary.move @@ -43,7 +43,7 @@ module diem_framework::epoch_boundary { const ETRIGGER_EPOCH_UNAUTHORIZED: u64 = 1; /// Epoch is not ready for reconfiguration const ETRIGGER_NOT_READY: u64 = 2; - /// Epoch number mismat + /// Epoch number mismatch const ENOT_SAME_EPOCH: u64 = 3; /////// Constants //////// @@ -254,20 +254,22 @@ module diem_framework::epoch_boundary { } #[view] - /// check to see if the epoch Boundary Bit is true + /// check to see if the epoch BoundaryBit is true public fun can_trigger(): bool acquires BoundaryBit { let state = borrow_global_mut(@ol_framework); assert!(state.ready, ETRIGGER_NOT_READY); - assert!(state.closing_epoch == reconfiguration::get_current_epoch(), + // greater than, in case there is an epoch change due to an epoch bump in + // testnet Twin tools, or a rescue operation. + assert!(state.closing_epoch <= reconfiguration::get_current_epoch(), ENOT_SAME_EPOCH); true } // This function handles the necessary migrations that occur at the epoch boundary // when new modules or structures are added by chain upgrades. - fun migrate_data(root: &signer) { - randomness::initialize(root); - migrations::execute(root); + fun migrate_data(framework: &signer) { + randomness::initialize(framework); + migrations::execute(framework); } // Contains all of 0L's business logic for end of epoch. diff --git a/framework/libra-framework/sources/ol_sources/vouch.move b/framework/libra-framework/sources/ol_sources/vouch.move index 1f7473db9..573278b4e 100644 --- a/framework/libra-framework/sources/ol_sources/vouch.move +++ b/framework/libra-framework/sources/ol_sources/vouch.move @@ -11,7 +11,7 @@ module ol_framework::vouch { use diem_framework::system_addresses; use diem_framework::transaction_fee; - use diem_std::debug::print; + use diem_std::debug::print; friend diem_framework::genesis; friend ol_framework::proof_of_fee; diff --git a/framework/libra-framework/sources/randomness.move b/framework/libra-framework/sources/randomness.move index 7fab83a78..682d14076 100644 --- a/framework/libra-framework/sources/randomness.move +++ b/framework/libra-framework/sources/randomness.move @@ -37,7 +37,7 @@ module diem_framework::randomness { friend diem_framework::block; friend ol_framework::musical_chairs; - const DST: vector = b"ALL_YOUR_BASE"; + const INIT_SEED: vector = b"all your base are belong to us"; const MAX_U256: u256 = 115792089237316195423570985008687907853269984665640564039457584007913129639935; @@ -68,12 +68,17 @@ module diem_framework::randomness { move_to(framework, PerBlockRandomness { epoch: 0, round: 0, - seed: option::none(), + seed: option::some(INIT_SEED), seq: 0, }); } } + #[view] + public fun is_init(): bool { + exists(@diem_framework) + } + #[test_only] public fun initialize_for_testing(framework: &signer) acquires PerBlockRandomness { initialize(framework); @@ -98,11 +103,16 @@ module diem_framework::randomness { // public facing API. // assert!(is_unbiasable(), E_API_USE_IS_BIASIBLE); - let input = DST; let randomness = borrow_global_mut(@diem_framework); - let seed = *option::borrow(&randomness.seed); - vector::append(&mut input, seed); + // belt and suspenders if something didn't initialize + let input = if (option::is_some(&randomness.seed)) { + *option::borrow(&randomness.seed) + } else { + INIT_SEED + }; + + // 0L NOTE: these native APIs dont exist in 0L V7. // get_transaction_hash() doesnt exist. So different than vendor, // we will always increment a seed based on the block hash. @@ -110,6 +120,7 @@ module diem_framework::randomness { // transaction. // we will add the script hash of the entry function as a placeholder // though this will likely not be adding much entropy. + vector::append(&mut input, transaction_context::get_script_hash()); diff --git a/framework/releases/head.mrb b/framework/releases/head.mrb index eb3be61ff495d5de019046d24e7fb37ee44eb770..d0e20fa1107afdd1f6a9b8bfcc8c311a53a3564f 100644 GIT binary patch delta 34998 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 JN4dVU{{XV%5EuXe delta 34636 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, ) -> anyhow::Result<(LocalAccount, AppCfg)> { - let info = swarm.diem_public_info_for_node(nth); - let url: Url = info.url().parse().unwrap(); + // TODO: unclear why public info needs to be a mutable borrow + let node = swarm + .validators() + .nth(nth) + .expect("could not get nth validator"); + let url = node.rest_api_endpoint(); - let node = swarm.validators().next().unwrap(); - let np = NetworkPlaylist::new(Some(url), Some(diem_types::chain_id::NamedChain::TESTING)); + let dir = dir_opt.unwrap_or(node.config_path().parent().unwrap().to_owned()); + + let chain_name = NamedChain::from_chain_id(&swarm.chain_id()).ok(); + let np = NetworkPlaylist::new(Some(url), chain_name); + let cfg_key = node.account_private_key().as_ref().unwrap(); + let prikey = cfg_key.private_key(); + let pubkey = prikey.public_key(); let mut app_cfg = AppCfg::init_app_configs( - AuthenticationKey::ed25519(&node.account_private_key().as_ref().unwrap().public_key()), + AuthenticationKey::ed25519(&pubkey), node.peer_id(), Some(dir), Some(np.chain_name), Some(np), - ) - .unwrap(); - - let pri_key = node - .account_private_key() - .as_ref() - .expect("could not get pri_key") - .private_key(); - let auth = AuthenticationKey::ed25519(&pri_key.public_key()); + )?; + let profile = app_cfg .get_profile_mut(None) .expect("could not get profile"); - profile.set_private_key(&pri_key); - let local_account = LocalAccount::new(auth.derived_address(), pri_key, 0); + profile.set_private_key(&prikey); + + let local_account = LocalAccount::new(profile.account, prikey, 0); Ok((local_account, app_cfg)) } + +/// helper to save libra-cli config files for each of the validators in +/// their local temp folder (alongside validator.yaml) +pub fn save_cli_config_all(swarm: &mut LocalSwarm) -> anyhow::Result<()> { + let len = swarm.validators().count(); + for i in 0..len { + // a libra-cli-config file will be created at the temp swarm + // directory of the node + let (_, app_cfg) = init_val_config_files(swarm, i, None)?; + let _file = app_cfg.save_file()?; + } + Ok(()) +} diff --git a/testsuites/twin/Makefile b/testsuites/twin/Makefile new file mode 100644 index 000000000..d18fedd2e --- /dev/null +++ b/testsuites/twin/Makefile @@ -0,0 +1,122 @@ +# NOTE: you'll need to have a db of a fullnode already synced +# twin tool will make a copy of this +# THE DB WILL NOT BE WRITTEN TO + +# TMP_DIR = /tmp/.tmpCu3Rxh/ + +ifndef DB_DIR +DB_DIR=$$HOME/.libra/data/db +endif + +ifndef UPGRADE_SCRIPT_PATH +UPGRADE_SCRIPT_PATH = $$HOME/upgrade-six/ +endif + +ifndef FRAMEWORK_SOURCE_PATH +FRAMEWORK_SOURCE_PATH = $$HOME/libra-framework/framework +endif + +ifndef DIEM_FORGE_NODE_BIN_PATH +DIEM_FORGE_NODE_BIN_PATH = $$HOME/.cargo/bin/libra +endif + +PROPOSAL_ID = 6 + +##### INSTRUCTIONS + +# Grab the essentials: +# sudo apt update +# sudo apt install -y git build-essential cmake clang llvm libgmp-dev pkg-config libssl-dev lld libpq-dev +# curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + +# 1. use an up to date libra-cli binary +# > cargo build --release -p libra --locked +# > cp target/release/libra ~/.cargo/bin/ + +# 2. Make sure you have a fullnode that is already syced, you'll need the DB_DIR of it +# if you are starting fresh use: +# > libra config fullnode-init +# > libra node +# check height while syncing +# > watch -n 5 'curl -s 127.0.0.1:9101/metrics | grep diem_state_sync_version' + +# 2. compile the new libra-framework MOVE code with: +# > make upgrade-script +# note the defaults for input: FRAMEWORK_SOURCE_PATH and and output: UPGRADE_SCRIPT_PATH + +# 3. start a Twin swarm locally +# > make start-twin +# NOTE: the output `temp files found at: /tmp/<......> ` +# A local Twin of mainnet is now running on your machine. + +# 4. export that temp dir to your path +# > export TMP_DIR=/tmp/<......> + +# Now your can do transactions as the new random validators +# 5. check validator set +# > make view-vals + +# 6. try to tigger epoch +# > make tx-epoch +# NOTE: this usually should fail unless enough time has passed. + +# 7. Send the full upgrade e2e +# > make upgrade-ceremony + +# 8. check the state of the proposal +# > make view-state + +# start twin with three validators +start-twin: + cargo run -p libra-twin-tests -- -d ${DB_DIR} -c 3 + + +######### UPGRADE SCRIPT GENERATION +upgrade-script: move-build-framework move-build-script + +move-build-framework: + cd ${FRAMEWORK_SOURCE_PATH} && libra move framework release + +move-build-script: + libra move framework upgrade --core-modules libra-framework --output-dir ${UPGRADE_SCRIPT_PATH} --framework-local-dir ${FRAMEWORK_SOURCE_PATH} + +######## EPOCH TRIGGER +tx-epoch: + libra txs -c ${TMP_DIR}/0/libra-cli-config.yaml governance epoch-boundary + + +######## UPGRADE TRANSACTIONS +upgrade-ceremony: tx-propose tx-vote tx-resolve + +tx-propose: + libra txs -c ${TMP_DIR}/0/libra-cli-config.yaml governance propose -d ${UPGRADE_SCRIPT_PATH}/1-libra-framework -m https://tbd.com + +tx-vote: + libra txs -c ${TMP_DIR}/0/libra-cli-config.yaml governance vote -i ${PROPOSAL_ID} + libra txs -c ${TMP_DIR}/1/libra-cli-config.yaml governance vote -i ${PROPOSAL_ID} + libra txs -c ${TMP_DIR}/2/libra-cli-config.yaml governance vote -i ${PROPOSAL_ID} + +tx-resolve: + libra txs -c ${TMP_DIR}/0/libra-cli-config.yaml --tx-profile critical governance resolve -i ${PROPOSAL_ID} -d ${UPGRADE_SCRIPT_PATH}/1-libra-framework + +#### VIEW STATE OF UPGRADE PROPOSALS +view-state: + libra query -c ${TMP_DIR}/0/libra-cli-config.yaml view -f 0x1::diem_governance::get_proposal_state -a ${PROPOSAL_ID} + +view-resolve: + libra query -c ${TMP_DIR}/0/libra-cli-config.yaml view -f 0x1::diem_governance::get_can_resolve -a ${PROPOSAL_ID} + +view-vals: + libra query -c ${TMP_DIR}/0/libra-cli-config.yaml view -f 0x1::stake::get_current_validators + + +######## OTHER +debug-keys: + cat ${TMP_DIR}/0/private-identity.yaml + cat ${TMP_DIR}/1/private-identity.yaml + cat ${TMP_DIR}/2/private-identity.yaml + +help-tx-bid-shuffle: + libra txs -c ${TMP_DIR}/0/libra-cli-config.yaml validator pof -b 0.3 -e 1000 + libra txs -c ${TMP_DIR}/1/libra-cli-config.yaml validator pof -b 0.4 -e 1000 + libra txs -c ${TMP_DIR}/2/libra-cli-config.yaml validator pof -b 0.5 -e 1000 diff --git a/testsuites/twin/src/runner.rs b/testsuites/twin/src/runner.rs index 4699c7a5c..c7705ce28 100644 --- a/testsuites/twin/src/runner.rs +++ b/testsuites/twin/src/runner.rs @@ -1,7 +1,6 @@ use clap::{self, Parser}; use libra_smoke_tests::libra_smoke::LibraSmoke; use std::{fs, path::PathBuf}; - /// Twin of the network #[derive(Parser)] @@ -29,6 +28,9 @@ impl Twin { let num_validators = self.count_vals.unwrap_or(1); let mut smoke = LibraSmoke::new(Some(num_validators), None).await?; + // save_cli_config_all(&mut smoke.swarm)?; + + // thread::sleep(Duration::from_secs(60)); Twin::make_twin_swarm(&mut smoke, Some(db_path), true).await?; Ok(()) diff --git a/testsuites/twin/src/setup.rs b/testsuites/twin/src/setup.rs index 024e0e72a..93bd0272f 100644 --- a/testsuites/twin/src/setup.rs +++ b/testsuites/twin/src/setup.rs @@ -7,7 +7,7 @@ use diem_types::{ waypoint::Waypoint, }; use fs_extra::dir; -use libra_smoke_tests::libra_smoke::LibraSmoke; +use libra_smoke_tests::{configure_validator, libra_smoke::LibraSmoke}; use libra_txs::txs_cli_vals::ValidatorTxs; use smoke_test::test_utils::{MAX_CONNECTIVITY_WAIT_SECS, MAX_HEALTHY_WAIT_SECS}; use std::{ @@ -221,6 +221,7 @@ impl Twin { } /// Apply the rescue blob to the swarm db + /// returns the temp directory of the swarm pub async fn make_twin_swarm( smoke: &mut LibraSmoke, reference_db: Option, @@ -270,7 +271,7 @@ impl Twin { let temp_path = temp.path(); assert!(temp_path.exists()); let temp_db_path = Self::temp_backup_db(&reference_db, temp_path)?; - dbg!(&temp_db_path); + assert!(temp_db_path.exists()); println!("2. Create a rescue blob from the reference db"); @@ -305,7 +306,9 @@ impl Twin { .wait_for_all_nodes_to_catchup_to_version(start_version + 10, Duration::from_secs(30)) .await?; - let cli_tools = smoke.first_account_app_cfg()?; + // place a libra-cli-config.yaml in the home dir of the swarm vals + // helps test the cli tools + configure_validator::save_cli_config_all(&mut smoke.swarm)?; let duration_upgrade = start_upgrade.elapsed(); println!( @@ -313,18 +316,22 @@ impl Twin { duration_upgrade ); + let temp_dir = smoke.swarm.dir(); + println!("temp files found at: {}", temp_dir.display()); + if keep_running { dialoguer::Confirm::new() .with_prompt("swarm will keep running in background. Would you like to exit?") .interact()?; + // NOTE: all validators will stop when the LibraSmoke goes out of context. + // but since it's borrowed in this function you should assume it will continue until the caller goes out of scope. } - // NOTE: all validators will stop when the LibraSmoke goes out of context. - Ok(cli_tools.workspace.node_home) + + Ok(temp_dir.to_owned()) } /// Extract the credentials of the random validator async fn extract_credentials(marlon_node: &LocalNode) -> anyhow::Result { - println!("extracting swarm validator credentials"); // get the necessary values from the current db let account = marlon_node.config().get_peer_id().unwrap(); diff --git a/tools/query/Cargo.toml b/tools/query/Cargo.toml index 1d46f9288..4724b433b 100644 --- a/tools/query/Cargo.toml +++ b/tools/query/Cargo.toml @@ -21,6 +21,7 @@ indoc = { workspace = true } libra-types = { workspace = true } serde_json = { workspace = true } tokio = { workspace = true } +url = { workspace = true } [dev-dependencies] hex = { workspace = true } diff --git a/tools/query/src/query_cli.rs b/tools/query/src/query_cli.rs index 00a789fad..b4263880c 100644 --- a/tools/query/src/query_cli.rs +++ b/tools/query/src/query_cli.rs @@ -1,7 +1,14 @@ +use std::path::PathBuf; + use crate::query_type::QueryType; + use anyhow::Result; use clap::Parser; +use libra_types::{ + core_types::app_cfg::AppCfg, exports::Client, type_extensions::client_ext::ClientExt, +}; use serde_json; +use url::Url; #[derive(Parser)] #[clap(name = env!("CARGO_PKG_NAME"), author, version, about, long_about = None, arg_required_else_help = true)] @@ -10,13 +17,38 @@ pub struct QueryCli { #[clap(subcommand)] /// what to query subcommand: QueryType, + + /// optional, path to the libra cli config file + #[clap(short, long)] + pub config_path: Option, + + /// optional, URL of the upstream node to send tx to, including port + /// Otherwise will default to what is in the config file + #[clap(short, long)] + pub url: Option, } impl QueryCli { pub async fn run(&self) -> Result<()> { - // TODO: get client from configs + // Query requires a URL for upstream + // the user should set one explicitly + // Otherwise the tool will try to fetch the libra config from the + // usual location: ~/.libra + // The user can set an alternative path the the config, + // which is useful in testnets. + + // Initialize client + let client = if let Some(u) = &self.url { + Client::new(u.clone()) + } else if let Some(p) = &self.config_path { + let app_cfg = AppCfg::load(Some(p.to_owned()))?; + let (c, _) = Client::from_libra_config(&app_cfg, None).await?; + c + } else { + Client::default().await? + }; - let res = self.subcommand.query_to_json(None).await?; + let res = self.subcommand.query_to_json(&client).await?; let pretty_json = serde_json::to_string_pretty(&res)?; println!("{}", pretty_json); diff --git a/tools/query/src/query_type.rs b/tools/query/src/query_type.rs index 778c2d3db..f6645b6a7 100644 --- a/tools/query/src/query_type.rs +++ b/tools/query/src/query_type.rs @@ -128,15 +128,10 @@ pub enum QueryType { } impl QueryType { - pub async fn query_to_json(&self, client_opt: Option) -> Result { - let client = match client_opt { - Some(c) => c, - None => Client::default().await?, - }; - + pub async fn query_to_json(&self, client: &Client) -> Result { match self { QueryType::Balance { account } => { - let res = get_account_balance_libra(&client, *account).await?; + let res = get_account_balance_libra(client, *account).await?; Ok(json!(res.scaled())) } QueryType::View { @@ -145,12 +140,12 @@ impl QueryType { args, } => { let res = - get_view(&client, function_id, type_args.to_owned(), args.to_owned()).await?; + get_view(client, function_id, type_args.to_owned(), args.to_owned()).await?; let json = json!({ "body": res }); Ok(json) } QueryType::Epoch => { - let num = get_epoch(&client).await?; + let num = get_epoch(client).await?; let json = json!({ "epoch": num, }); @@ -178,7 +173,7 @@ impl QueryType { } } QueryType::ValConfig { account } => { - let res = get_val_config(&client, *account).await?; + let res = get_val_config(client, *account).await?; // make this readable, turn the network address into a string Ok(json!({ @@ -189,7 +184,7 @@ impl QueryType { })) } QueryType::BlockHeight => { - let height = get_height(&client).await?; + let height = get_height(client).await?; Ok(json!({ "BlockHeight": height })) } QueryType::Events { @@ -197,8 +192,7 @@ impl QueryType { withdrawn_or_deposited, seq_start, } => { - let res = - get_events(&client, *account, *withdrawn_or_deposited, *seq_start).await?; + let res = get_events(client, *account, *withdrawn_or_deposited, *seq_start).await?; Ok(json!({ "events": res })) } QueryType::Txs { @@ -208,7 +202,7 @@ impl QueryType { txs_type, } => { let res: Vec = get_transactions( - &client, + client, *account, *txs_height, *txs_count, @@ -228,21 +222,21 @@ impl QueryType { Ok(json!({ "transactions": prune_res })) } QueryType::ComWalletMigrated { account } => { - let res = is_community_wallet_migrated(&client, *account).await?; + let res = is_community_wallet_migrated(client, *account).await?; Ok(json!({ "migrated": res })) } QueryType::ComWalletSigners { account } => { // Wont work at the moment as there is no community wallet that with governace structure - let _res = community_wallet_signers(&client, *account).await?; + let _res = community_wallet_signers(client, *account).await?; Ok(json!({ "signers": "None"})) } QueryType::ComWalletPendTransactions { account } => { // Wont work at the moment as there is no community wallet migrated - let _res = community_wallet_scheduled_transactions(&client, *account).await?; + let _res = community_wallet_scheduled_transactions(client, *account).await?; Ok(json!({ "pending_transactions": "None" })) } QueryType::Annotate { account } => { - let dbgger = DiemDebugger::rest_client(client)?; + let dbgger = DiemDebugger::rest_client(client.clone())?; let version = dbgger.get_latest_version().await?; let blob = dbgger .annotate_account_state_at_version(account.to_owned(), version) diff --git a/tools/query/tests/query.rs b/tools/query/tests/query.rs index 7eb49b421..dee3f67bf 100644 --- a/tools/query/tests/query.rs +++ b/tools/query/tests/query.rs @@ -13,7 +13,7 @@ async fn libra_query_test() { let c = s.client(); let q = QueryType::Balance { account: val_acct }; - match q.query_to_json(Some(c.to_owned())).await { + match q.query_to_json(&c).await { Ok(v) => { println!("v: {:?}", v); let b: LibraBalanceDisplay = serde_json::from_value(v).unwrap(); @@ -38,7 +38,7 @@ async fn account_annotate_test() { let c = s.client(); let q = QueryType::Annotate { account: val_acct }; - let res = q.query_to_json(Some(c)).await.unwrap(); + let res = q.query_to_json(&c).await.unwrap(); println!("{:#}", &res.as_str().unwrap()); assert!(res.as_str().unwrap().contains("drop")); } diff --git a/tools/query/tests/view.rs b/tools/query/tests/view.rs index fae52115a..917989c8b 100644 --- a/tools/query/tests/view.rs +++ b/tools/query/tests/view.rs @@ -15,7 +15,7 @@ async fn libra_view_test() { type_args: None, args: None, }; - match q.query_to_json(Some(c.to_owned())).await { + match q.query_to_json(&c).await { Ok(v) => { println!("v: {:?}", v); } diff --git a/tools/rescue/tests/support/mod.rs b/tools/rescue/tests/support/mod.rs index 2da940e71..ca8b368c7 100644 --- a/tools/rescue/tests/support/mod.rs +++ b/tools/rescue/tests/support/mod.rs @@ -126,8 +126,7 @@ pub async fn upgrade_multiple_impl( let d = diem_temppath::TempPath::new(); let (_, _app_cfg) = - configure_validator::init_val_config_files(&mut smoke.swarm, 0, d.path().to_owned()) - .await + configure_validator::init_val_config_files(&mut smoke.swarm, 0, Some(d.path().to_owned())) .context("could not init validator config")?; // This step should fail. The view function does not yet exist in the system address. diff --git a/tools/txs/src/txs_cli.rs b/tools/txs/src/txs_cli.rs index 99b3c3783..9712783c1 100644 --- a/tools/txs/src/txs_cli.rs +++ b/tools/txs/src/txs_cli.rs @@ -26,7 +26,7 @@ pub struct TxsCli { #[clap(subcommand)] pub subcommand: Option, - /// optional, path to the config file + /// optional, path to the libra cli config file #[clap(short, long)] pub config_path: Option, @@ -138,20 +138,26 @@ pub enum TxsSub { impl TxsCli { /// Executes the transaction CLI command based on parsed arguments. pub async fn run(&self) -> Result<()> { + // Load application configuration + let app_cfg = AppCfg::load(self.config_path.clone())?; + let profile = app_cfg.get_profile(None)?; + // Determine private key based on CLI options or prompts let pri_key = if let Some(pk) = &self.test_private_key { + println!("using private key from cli args --test-private-key"); Ed25519PrivateKey::from_encoded_string(pk)? } else if let Some(m) = &self.mnemonic { + println!("using private key from cli args --mnemonic"); let legacy = get_keys_from_mnem(m.to_string())?; legacy.child_0_owner.pri_key + } else if let Ok(p) = profile.borrow_private_key() { + println!("use private key from test libra-cli-config.yaml"); + p.to_owned() } else { let legacy = get_keys_from_prompt()?; legacy.child_0_owner.pri_key }; - // Load application configuration - let app_cfg = AppCfg::load(self.config_path.clone())?; - // Determine chain ID and URL for client let chain_name = self.chain_id.unwrap_or(app_cfg.workspace.default_chain_id); let url = if let Some(u) = self.url.as_ref() { diff --git a/tools/txs/tests/community_wallet.rs b/tools/txs/tests/community_wallet.rs index cbb6c8b5a..d69201132 100644 --- a/tools/txs/tests/community_wallet.rs +++ b/tools/txs/tests/community_wallet.rs @@ -1527,8 +1527,7 @@ async fn setup_environment() -> (LibraSmoke, TempPath, AccountAddress, String, A .await .expect("Could not start libra smoke"); - configure_validator::init_val_config_files(&mut s.swarm, 0, dir.path().to_owned()) - .await + configure_validator::init_val_config_files(&mut s.swarm, 0, Some(dir.path().to_owned())) .expect("Could not initialize validator config"); let account_address = "0x029633a96b0c0e81cc26cf2baefdbd479dab7161fbd066ca3be850012342cdee"; diff --git a/tools/txs/tests/gov_script.rs b/tools/txs/tests/gov_script.rs index ca3454670..be127587b 100644 --- a/tools/txs/tests/gov_script.rs +++ b/tools/txs/tests/gov_script.rs @@ -24,8 +24,7 @@ async fn smoke_gov_script() { .expect("could not start libra smoke"); let (_, _app_cfg) = - configure_validator::init_val_config_files(&mut s.swarm, 0, d.path().to_owned()) - .await + configure_validator::init_val_config_files(&mut s.swarm, 0, Some(d.path().to_owned())) .expect("could not init validator config"); let this_path = PathBuf::from_str(env!("CARGO_MANIFEST_DIR")).unwrap(); diff --git a/tools/txs/tests/onboard_validator.rs b/tools/txs/tests/onboard_validator.rs index 0874da43f..00f2decf5 100644 --- a/tools/txs/tests/onboard_validator.rs +++ b/tools/txs/tests/onboard_validator.rs @@ -34,8 +34,7 @@ async fn smoke_onboard_validator() -> anyhow::Result<()> { .expect("could not start libra smoke"); let (_, _app_cfg) = - configure_validator::init_val_config_files(&mut s.swarm, 0, d.path().to_owned()) - .await + configure_validator::init_val_config_files(&mut s.swarm, 0, Some(d.path().to_owned())) .expect("could not init validator config"); // 1. CREATE THE ACCOUNT diff --git a/tools/txs/tests/publish.rs b/tools/txs/tests/publish.rs index d5dbcff99..55530b34f 100644 --- a/tools/txs/tests/publish.rs +++ b/tools/txs/tests/publish.rs @@ -20,8 +20,7 @@ async fn smoke_publish() { .expect("could not start libra smoke"); let (_, _app_cfg) = - configure_validator::init_val_config_files(&mut s.swarm, 0, d.path().to_owned()) - .await + configure_validator::init_val_config_files(&mut s.swarm, 0, Some(d.path().to_owned())) .expect("could not init validator config"); // 1) the genesis validator to build and publish a fixture Move module ("tests/fixtures/test_publish"). diff --git a/tools/txs/tests/transfer.rs b/tools/txs/tests/transfer.rs index 5f6547d49..0f17fcfe1 100644 --- a/tools/txs/tests/transfer.rs +++ b/tools/txs/tests/transfer.rs @@ -22,8 +22,7 @@ async fn smoke_transfer_existing_account() { .expect("could not start libra smoke"); let (_, _app_cfg) = - configure_validator::init_val_config_files(&mut s.swarm, 0, d.path().to_owned()) - .await + configure_validator::init_val_config_files(&mut s.swarm, 0, Some(d.path().to_owned())) .expect("could not init validator config"); // let s = LibraSmoke::new(Some(2)).await.expect("can't start swarm"); @@ -62,8 +61,7 @@ async fn smoke_transfer_create_account() -> Result<(), anyhow::Error> { .expect("could not start libra smoke"); let (_, _app_cfg) = - configure_validator::init_val_config_files(&mut s.swarm, 0, d.path().to_owned()) - .await + configure_validator::init_val_config_files(&mut s.swarm, 0, Some(d.path().to_owned())) .expect("could not init validator config"); let client = s.client(); @@ -110,8 +108,7 @@ async fn smoke_transfer_estimate() { .expect("could not start libra smoke"); let (_, _app_cfg) = - configure_validator::init_val_config_files(&mut s.swarm, 0, d.path().to_owned()) - .await + configure_validator::init_val_config_files(&mut s.swarm, 0, Some(d.path().to_owned())) .expect("could not init validator config"); // case 2. Account does not yet exist. diff --git a/types/src/core_types/app_cfg.rs b/types/src/core_types/app_cfg.rs index 567974763..5efe8a1dd 100644 --- a/types/src/core_types/app_cfg.rs +++ b/types/src/core_types/app_cfg.rs @@ -76,6 +76,7 @@ impl AppCfg { let mut default_config = AppCfg::default(); let profile = Profile::new(authkey, account); + default_config.workspace.default_profile = Some(profile.nickname.to_owned()); default_config.user_profiles = vec![profile]; default_config.workspace.node_home = config_path.unwrap_or_else(global_config_dir); @@ -138,7 +139,10 @@ impl AppCfg { let yaml = serde_yaml::to_string(&self)?; let home_path = &self.workspace.node_home.clone(); // create home path if doesn't exist, usually only in dev/ci environments. - fs::create_dir_all(home_path)?; + if !home_path.exists() { + fs::create_dir_all(home_path)?; + } + let toml_path = home_path.join(CONFIG_FILE_NAME); let mut file = fs::File::create(&toml_path)?; file.write_all(yaml.as_bytes())?; @@ -192,7 +196,7 @@ impl AppCfg { let p = self.user_profiles.get(idx).context("no profile at index")?; // The privilege to use this software depends on the user upholding a code of conduct and taking the pledge. Totally cool if you don't want to, but you'll need to write your own tools. if !p.check_has_pledge(0) { - println!("user profile has not taken 'Protect the Game' pledge, exiting."); + println!("user profile has not taken 'Protect the Game' pledge. Future releases will exit here."); } Ok(p) } @@ -383,9 +387,7 @@ pub struct Profile { pub account: AccountAddress, /// Miner Authorization Key for 0L Blockchain. Note: not the same as public key, nor account. pub auth_key: AuthenticationKey, - /// Private key only for use with testing - /// Note: skip_serializing so that it is never saved to disk. - #[serde(skip_serializing)] + /// Danger: private key only for use with testing test_private_key: Option, /// nickname for this profile pub nickname: String, @@ -399,16 +401,8 @@ pub struct Profile { pub locale: Option, /// An opportunity for the Miner to write a message on their genesis block. pub statement: String, - /// Pledges the user took pub pledges: Option>, - // NOTE: V7: deprecated - // Deprecation: : /// ip address of this node. May be different from transaction URL. - // pub ip: Ipv4Addr, - - // V7.0.3 deprecated - // Deprecation: /// Other nodes to connect for fallback connections - // pub upstream_nodes: Option>, } impl Default for Profile { diff --git a/upgrade-tests/tests/support/mod.rs b/upgrade-tests/tests/support/mod.rs index 3a82ed418..304beb0a8 100644 --- a/upgrade-tests/tests/support/mod.rs +++ b/upgrade-tests/tests/support/mod.rs @@ -25,8 +25,7 @@ pub async fn upgrade_multiple_impl( let d = diem_temppath::TempPath::new(); let (_, _app_cfg) = - configure_validator::init_val_config_files(&mut s.swarm, 0, d.path().to_owned()) - .await + configure_validator::init_val_config_files(&mut s.swarm, 0, Some(d.path().to_owned())) .context("could not init validator config")?; // This step should fail. The view function does not yet exist in the system address.