From 89fe2606d3de8f279b926643940a3e90732de8bc Mon Sep 17 00:00:00 2001 From: "Raymond E. Pasco" Date: Tue, 27 Jun 2023 14:19:55 -0400 Subject: [PATCH 1/6] apps: add namadac epoch-sleep Add the epoch-sleep helper, which queries the current epoch and then sleeps until the epoch is greater (polling once a second). This can replace the multiple namadac invocations used in e2e tests. --- apps/src/bin/namada-client/cli.rs | 7 ++++++ apps/src/lib/cli.rs | 28 +++++++++++++++++++++ apps/src/lib/client/rpc.rs | 15 +++++++++++ tests/src/e2e/ledger_tests.rs | 42 +++++++++++++++++++++++++++++++ 4 files changed, 92 insertions(+) diff --git a/apps/src/bin/namada-client/cli.rs b/apps/src/bin/namada-client/cli.rs index bf9921a527..1b1514ad52 100644 --- a/apps/src/bin/namada-client/cli.rs +++ b/apps/src/bin/namada-client/cli.rs @@ -307,6 +307,13 @@ pub async fn main() -> Result<()> { let args = args.to_sdk(&mut ctx); rpc::query_protocol_parameters(&client, args).await; } + Sub::EpochSleep(EpochSleep(args)) => { + wait_until_node_is_synched(&args.ledger_address).await; + let client = + HttpClient::new(args.ledger_address.clone()).unwrap(); + let args = args.to_sdk(&mut ctx); + rpc::epoch_sleep(&client, args).await; + } } } cli::NamadaClient::WithoutContext(cmd, global_args) => match cmd { diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index b3078037ff..a060688b28 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -181,6 +181,9 @@ pub mod cmds { .subcommand(QueryProposal::def().display_order(3)) .subcommand(QueryProposalResult::def().display_order(3)) .subcommand(QueryProtocolParameters::def().display_order(3)) + // wait until next epoch (can't be in utils, needs the ledger + // address) + .subcommand(EpochSleep::def().display_order(4)) // Utils .subcommand(Utils::def().display_order(5)) } @@ -223,6 +226,7 @@ pub mod cmds { Self::parse_with_ctx(matches, QueryProposalResult); let query_protocol_parameters = Self::parse_with_ctx(matches, QueryProtocolParameters); + let epoch_sleep = Self::parse_with_ctx(matches, EpochSleep); let utils = SubCmd::parse(matches).map(Self::WithoutContext); tx_custom .or(tx_transfer) @@ -251,6 +255,7 @@ pub mod cmds { .or(query_proposal) .or(query_proposal_result) .or(query_protocol_parameters) + .or(epoch_sleep) .or(utils) } } @@ -315,6 +320,7 @@ pub mod cmds { QueryProposal(QueryProposal), QueryProposalResult(QueryProposalResult), QueryProtocolParameters(QueryProtocolParameters), + EpochSleep(EpochSleep), } #[allow(clippy::large_enum_variant)] @@ -1581,6 +1587,28 @@ pub mod cmds { } } + #[derive(Clone, Debug)] + pub struct EpochSleep(pub args::Query); + + impl SubCmd for EpochSleep { + const CMD: &'static str = "epoch-sleep"; + + fn parse(matches: &ArgMatches) -> Option { + matches + .subcommand_matches(Self::CMD) + .map(|matches| Self(args::Query::parse(matches))) + } + + fn def() -> App { + App::new(Self::CMD) + .about( + "Query for the current epoch, then sleep until the next \ + epoch.", + ) + .add_args::>() + } + } + #[derive(Clone, Debug)] pub enum Utils { JoinNetwork(JoinNetwork), diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 8aa466420e..7d9643d2a6 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -1828,6 +1828,21 @@ pub async fn query_result( } } +pub async fn epoch_sleep( + client: &C, + _args: args::Query, +) { + let start_epoch = query_and_print_epoch(client).await; + loop { + tokio::time::sleep(Duration::from_secs(1)).await; + let current_epoch = query_epoch(client).await; + if current_epoch > start_epoch { + println!("Reached epoch {}", current_epoch); + break; + } + } +} + pub async fn get_proposal_votes( client: &C, epoch: Epoch, diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 7c82dc84d3..a42742312f 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -4399,6 +4399,48 @@ fn implicit_account_reveal_pk() -> Result<()> { Ok(()) } +#[test] +fn test_epoch_sleep() -> Result<()> { + // Use slightly longer epochs to give us time to sleep + let test = setup::network( + |genesis| { + let parameters = ParametersConfig { + epochs_per_year: epochs_per_year_from_min_duration(30), + min_num_of_blocks: 1, + ..genesis.parameters + }; + GenesisConfig { + parameters, + ..genesis + } + }, + None, + )?; + + // 1. Run the ledger node + let mut ledger = + run_as!(test, Who::Validator(0), Bin::Node, &["ledger"], Some(40))?; + wait_for_wasm_pre_compile(&mut ledger)?; + + let _bg_ledger = ledger.background(); + + let validator_one_rpc = get_actor_rpc(&test, &Who::Validator(0)); + + // 2. Query the current epoch + let start_epoch = get_epoch(&test, &validator_one_rpc).unwrap(); + + // 3. Use epoch-sleep to sleep for an epoch + let args = ["epoch-sleep", "--node", &validator_one_rpc]; + let mut client = run!(test, Bin::Client, &args, None)?; + client.assert_success(); + + // 4. Confirm the current epoch is larger + let current_epoch = get_epoch(&test, &validator_one_rpc).unwrap(); + assert!(current_epoch > start_epoch); + + Ok(()) +} + /// Prepare proposal data in the test's temp dir from the given source address. /// This can be submitted with "init-proposal" command. fn prepare_proposal_data( From e7cc5e3a59ef47139ee9ffda749c90219496e7fd Mon Sep 17 00:00:00 2001 From: "Raymond E. Pasco" Date: Tue, 27 Jun 2023 16:20:08 -0400 Subject: [PATCH 2/6] tests/e2e: change epoch_sleep to use namadac epoch-sleep Instead of manually querying, we now epoch-sleep until the next epoch, limiting the number of clients invoked to 1. Also update the epoch sleep e2e test to check the return value (parsed from the output of epoch-sleep). --- tests/src/e2e/helpers.rs | 37 ++++++++++++++++++++++------------- tests/src/e2e/ledger_tests.rs | 5 +++++ 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/tests/src/e2e/helpers.rs b/tests/src/e2e/helpers.rs index 60dc119051..19a3f9a373 100644 --- a/tests/src/e2e/helpers.rs +++ b/tests/src/e2e/helpers.rs @@ -358,20 +358,29 @@ pub fn epoch_sleep( ledger_address: &str, timeout_secs: u64, ) -> Result { - let old_epoch = get_epoch(test, ledger_address)?; - let start = Instant::now(); - let loop_timeout = Duration::new(timeout_secs, 0); - loop { - if Instant::now().duration_since(start) > loop_timeout { - panic!("Timed out waiting for the next epoch"); - } - let epoch = get_epoch(test, ledger_address)?; - if epoch > old_epoch { - break Ok(epoch); - } else { - sleep(10); - } - } + let mut find = run!( + test, + Bin::Client, + &["epoch-sleep", "--node", ledger_address], + Some(timeout_secs) + )?; + parse_reached_epoch(&mut find) +} + +pub fn parse_reached_epoch(find: &mut NamadaCmd) -> Result { + let (unread, matched) = find.exp_regex("Reached epoch .*")?; + let epoch_str = strip_trailing_newline(&matched) + .trim() + .rsplit_once(' ') + .unwrap() + .1; + let epoch = u64::from_str(epoch_str).map_err(|e| { + eyre!(format!( + "Epoch: {} parsed from {}, Error: {}\n\nOutput: {}", + epoch_str, matched, e, unread + )) + })?; + Ok(Epoch(epoch)) } /// Wait for txs and VPs WASM compilations to finish. This is useful to avoid a diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index a42742312f..f08b08377f 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -39,6 +39,7 @@ use super::helpers::{ use super::setup::get_all_wasms_hashes; use crate::e2e::helpers::{ epoch_sleep, find_address, find_bonded_stake, get_actor_rpc, get_epoch, + parse_reached_epoch, }; use crate::e2e::setup::{self, default_port_offset, sleep, Bin, Who}; use crate::{run, run_as}; @@ -4432,11 +4433,15 @@ fn test_epoch_sleep() -> Result<()> { // 3. Use epoch-sleep to sleep for an epoch let args = ["epoch-sleep", "--node", &validator_one_rpc]; let mut client = run!(test, Bin::Client, &args, None)?; + let reached_epoch = parse_reached_epoch(&mut client)?; client.assert_success(); // 4. Confirm the current epoch is larger + // possibly badly, we assume we get here within 30 seconds of the last step + // should be fine haha (future debuggers: sorry) let current_epoch = get_epoch(&test, &validator_one_rpc).unwrap(); assert!(current_epoch > start_epoch); + assert_eq!(current_epoch, reached_epoch); Ok(()) } From 5c34343875f286acec040646a2187c80fa6606ac Mon Sep 17 00:00:00 2001 From: "Raymond E. Pasco" Date: Tue, 27 Jun 2023 16:21:51 -0400 Subject: [PATCH 3/6] tests/e2e: replace PoS test epoch waits with epoch-sleeps PoS hammered the node as fast as possible with epoch queries in its tests to wait for a specified epoch. Instead, epoch-sleep repeatedly until the target epoch has been reached. This could be improved further by adding an --until parameter to epoch-sleep. --- tests/src/e2e/ledger_tests.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index f08b08377f..30c71afa70 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -1993,7 +1993,7 @@ fn pos_bonds() -> Result<()> { delegation_withdrawable_epoch ); } - let epoch = get_epoch(&test, &validator_one_rpc)?; + let epoch = epoch_sleep(&test, &validator_one_rpc, 40)?; if epoch >= delegation_withdrawable_epoch { break; } @@ -2186,7 +2186,7 @@ fn pos_rewards() -> Result<()> { if Instant::now().duration_since(start) > loop_timeout { panic!("Timed out waiting for epoch: {}", wait_epoch); } - let epoch = get_epoch(&test, &validator_zero_rpc)?; + let epoch = epoch_sleep(&test, &validator_zero_rpc, 40)?; if dbg!(epoch) >= wait_epoch { break; } @@ -2268,7 +2268,7 @@ fn test_bond_queries() -> Result<()> { if Instant::now().duration_since(start) > loop_timeout { panic!("Timed out waiting for epoch: {}", 1); } - let epoch = get_epoch(&test, &validator_one_rpc)?; + let epoch = epoch_sleep(&test, &validator_one_rpc, 40)?; if epoch >= Epoch(4) { break; } @@ -2325,7 +2325,7 @@ fn test_bond_queries() -> Result<()> { if Instant::now().duration_since(start) > loop_timeout { panic!("Timed out waiting for epoch: {}", 7); } - let epoch = get_epoch(&test, &validator_one_rpc)?; + let epoch = epoch_sleep(&test, &validator_one_rpc, 40)?; if epoch >= Epoch(7) { break; } @@ -2565,7 +2565,7 @@ fn pos_init_validator() -> Result<()> { if Instant::now().duration_since(start) > loop_timeout { panic!("Timed out waiting for epoch: {}", earliest_update_epoch); } - let epoch = get_epoch(&test, &non_validator_rpc)?; + let epoch = epoch_sleep(&test, &non_validator_rpc, 40)?; if epoch >= earliest_update_epoch { break; } From 532659d159889be26b1e6711e498f96d3862c84f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 11 Jul 2023 11:26:04 +0100 Subject: [PATCH 4/6] apps: move epoch-sleep client cmd under utils --- apps/src/bin/namada-client/cli.rs | 16 +++++++++------- apps/src/lib/cli.rs | 10 ++++------ tests/src/e2e/helpers.rs | 2 +- tests/src/e2e/ledger_tests.rs | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/apps/src/bin/namada-client/cli.rs b/apps/src/bin/namada-client/cli.rs index 1b1514ad52..c0db01fdf9 100644 --- a/apps/src/bin/namada-client/cli.rs +++ b/apps/src/bin/namada-client/cli.rs @@ -307,13 +307,6 @@ pub async fn main() -> Result<()> { let args = args.to_sdk(&mut ctx); rpc::query_protocol_parameters(&client, args).await; } - Sub::EpochSleep(EpochSleep(args)) => { - wait_until_node_is_synched(&args.ledger_address).await; - let client = - HttpClient::new(args.ledger_address.clone()).unwrap(); - let args = args.to_sdk(&mut ctx); - rpc::epoch_sleep(&client, args).await; - } } } cli::NamadaClient::WithoutContext(cmd, global_args) => match cmd { @@ -336,6 +329,15 @@ pub async fn main() -> Result<()> { Utils::DefaultBaseDir(DefaultBaseDir(args)) => { utils::default_base_dir(global_args, args) } + Utils::EpochSleep(EpochSleep(args)) => { + let mut ctx = cli::Context::new(global_args) + .expect("expected to construct a context"); + let ledger_address = args.ledger_address.clone(); + wait_until_node_is_synched(&ledger_address).await; + let client = HttpClient::new(ledger_address).unwrap(); + let args = args.to_sdk(&mut ctx); + rpc::epoch_sleep(&client, args).await; + } }, } Ok(()) diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index a060688b28..d9dc843381 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -181,9 +181,6 @@ pub mod cmds { .subcommand(QueryProposal::def().display_order(3)) .subcommand(QueryProposalResult::def().display_order(3)) .subcommand(QueryProtocolParameters::def().display_order(3)) - // wait until next epoch (can't be in utils, needs the ledger - // address) - .subcommand(EpochSleep::def().display_order(4)) // Utils .subcommand(Utils::def().display_order(5)) } @@ -226,7 +223,6 @@ pub mod cmds { Self::parse_with_ctx(matches, QueryProposalResult); let query_protocol_parameters = Self::parse_with_ctx(matches, QueryProtocolParameters); - let epoch_sleep = Self::parse_with_ctx(matches, EpochSleep); let utils = SubCmd::parse(matches).map(Self::WithoutContext); tx_custom .or(tx_transfer) @@ -255,7 +251,6 @@ pub mod cmds { .or(query_proposal) .or(query_proposal_result) .or(query_protocol_parameters) - .or(epoch_sleep) .or(utils) } } @@ -320,7 +315,6 @@ pub mod cmds { QueryProposal(QueryProposal), QueryProposalResult(QueryProposalResult), QueryProtocolParameters(QueryProtocolParameters), - EpochSleep(EpochSleep), } #[allow(clippy::large_enum_variant)] @@ -1617,6 +1611,7 @@ pub mod cmds { InitGenesisValidator(InitGenesisValidator), PkToTmAddress(PkToTmAddress), DefaultBaseDir(DefaultBaseDir), + EpochSleep(EpochSleep), } impl SubCmd for Utils { @@ -1635,12 +1630,14 @@ pub mod cmds { SubCmd::parse(matches).map(Self::PkToTmAddress); let default_base_dir = SubCmd::parse(matches).map(Self::DefaultBaseDir); + let epoch_sleep = SubCmd::parse(matches).map(Self::EpochSleep); join_network .or(fetch_wasms) .or(init_network) .or(init_genesis) .or(pk_to_tm_address) .or(default_base_dir) + .or(epoch_sleep) }) } @@ -1653,6 +1650,7 @@ pub mod cmds { .subcommand(InitGenesisValidator::def()) .subcommand(PkToTmAddress::def()) .subcommand(DefaultBaseDir::def()) + .subcommand(EpochSleep::def()) .setting(AppSettings::SubcommandRequiredElseHelp) } } diff --git a/tests/src/e2e/helpers.rs b/tests/src/e2e/helpers.rs index 19a3f9a373..0634cdac1c 100644 --- a/tests/src/e2e/helpers.rs +++ b/tests/src/e2e/helpers.rs @@ -361,7 +361,7 @@ pub fn epoch_sleep( let mut find = run!( test, Bin::Client, - &["epoch-sleep", "--node", ledger_address], + &["utils", "epoch-sleep", "--node", ledger_address], Some(timeout_secs) )?; parse_reached_epoch(&mut find) diff --git a/tests/src/e2e/ledger_tests.rs b/tests/src/e2e/ledger_tests.rs index 30c71afa70..070ee0092a 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -4431,7 +4431,7 @@ fn test_epoch_sleep() -> Result<()> { let start_epoch = get_epoch(&test, &validator_one_rpc).unwrap(); // 3. Use epoch-sleep to sleep for an epoch - let args = ["epoch-sleep", "--node", &validator_one_rpc]; + let args = ["utils", "epoch-sleep", "--node", &validator_one_rpc]; let mut client = run!(test, Bin::Client, &args, None)?; let reached_epoch = parse_reached_epoch(&mut client)?; client.assert_success(); From ff692abfd798dab01d9ba3aeb39e9c71aed599bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 11 Jul 2023 12:36:55 +0100 Subject: [PATCH 5/6] ci/test/e2e: add new test --- .github/workflows/scripts/e2e.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/scripts/e2e.json b/.github/workflows/scripts/e2e.json index df2b75369c..80d1a8f6cf 100644 --- a/.github/workflows/scripts/e2e.json +++ b/.github/workflows/scripts/e2e.json @@ -19,6 +19,7 @@ "e2e::ledger_tests::test_namada_shuts_down_if_tendermint_dies": 2, "e2e::ledger_tests::test_genesis_validators": 14, "e2e::ledger_tests::test_node_connectivity_and_consensus": 28, + "e2e::ledger_tests::test_epoch_sleep": 12, "e2e::wallet_tests::wallet_address_cmds": 1, "e2e::wallet_tests::wallet_encrypted_key_cmds": 1, "e2e::wallet_tests::wallet_encrypted_key_cmds_env_var": 1, From 45d0813e88e87922dc10d31877b984fe9825a7a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Zemanovi=C4=8D?= Date: Tue, 11 Jul 2023 11:29:41 +0100 Subject: [PATCH 6/6] changelog: add #1621 --- .changelog/unreleased/improvements/1621-utils-next-epoch.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changelog/unreleased/improvements/1621-utils-next-epoch.md diff --git a/.changelog/unreleased/improvements/1621-utils-next-epoch.md b/.changelog/unreleased/improvements/1621-utils-next-epoch.md new file mode 100644 index 0000000000..5f663a8e00 --- /dev/null +++ b/.changelog/unreleased/improvements/1621-utils-next-epoch.md @@ -0,0 +1,2 @@ +- Added a command to wait for the next epoch: `client utils epoch-sleep`. + ([\#1621](https://github.com/anoma/namada/pull/1621)) \ No newline at end of file