diff --git a/apps/src/bin/namada-client/cli.rs b/apps/src/bin/namada-client/cli.rs index 609588045d..11e35fa789 100644 --- a/apps/src/bin/namada-client/cli.rs +++ b/apps/src/bin/namada-client/cli.rs @@ -433,6 +433,17 @@ pub async fn main() -> Result<()> { let args = args.to_sdk(&mut ctx); rpc::query_protocol_parameters(&client, args).await; } + Sub::EpochSleep(EpochSleep(mut args)) => { + let client = HttpClient::new(utils::take_config_address( + &mut args.ledger_address, + )) + .unwrap(); + wait_until_node_is_synched(&client).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 06d6f3f406..b585ac4fa0 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -238,6 +238,9 @@ pub mod cmds { .subcommand(QueryProposal::def().display_order(4)) .subcommand(QueryProposalResult::def().display_order(4)) .subcommand(QueryProtocolParameters::def().display_order(4)) + // 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)) } @@ -284,6 +287,7 @@ pub mod cmds { Self::parse_with_ctx(matches, QueryProtocolParameters); let add_to_eth_bridge_pool = Self::parse_with_ctx(matches, AddToEthBridgePool); + let epoch_sleep = Self::parse_with_ctx(matches, EpochSleep); let utils = SubCmd::parse(matches).map(Self::WithoutContext); tx_custom .or(tx_transfer) @@ -314,6 +318,7 @@ pub mod cmds { .or(query_proposal) .or(query_proposal_result) .or(query_protocol_parameters) + .or(epoch_sleep) .or(utils) } } @@ -381,6 +386,7 @@ pub mod cmds { QueryProposal(QueryProposal), QueryProposalResult(QueryProposalResult), QueryProtocolParameters(QueryProtocolParameters), + EpochSleep(EpochSleep), } #[allow(clippy::large_enum_variant)] @@ -1678,6 +1684,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 44955967d0..2b0b71650e 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -2030,6 +2030,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(core::time::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 ca7bd974c7..cfddcb2442 100644 --- a/tests/src/e2e/ledger_tests.rs +++ b/tests/src/e2e/ledger_tests.rs @@ -4663,6 +4663,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(