From 3e651c940d730c610fc57c659a58e8daac0c1761 Mon Sep 17 00:00:00 2001 From: satan Date: Mon, 24 Jul 2023 17:00:05 +0200 Subject: [PATCH 01/28] [feat]: Refactored the masp integration tests to no longer need the nightly feature --- Cargo.lock | 10 +- apps/src/lib/cli.rs | 2 +- apps/src/lib/cli/utils.rs | 51 ----- apps/src/lib/client/rpc.rs | 5 +- .../lib/node/ledger/shell/testing/client.rs | 12 +- .../lib/node/ledger/shell/testing/utils.rs | 197 ++++++++++++++++++ shared/src/types/io.rs | 9 + tests/src/integration.rs | 1 - tests/src/integration/masp.rs | 3 +- tests/src/integration/utils.rs | 83 -------- tests/src/lib.rs | 2 - wasm/checksums.json | 38 ++-- 12 files changed, 240 insertions(+), 173 deletions(-) delete mode 100644 tests/src/integration/utils.rs diff --git a/Cargo.lock b/Cargo.lock index 9517b5cf7b..5512620733 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3608,7 +3608,7 @@ checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", - "rustix 0.37.13", + "rustix 0.37.1", "windows-sys 0.48.0", ] @@ -6022,16 +6022,16 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.13" +version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79bef90eb6d984c72722595b5b1348ab39275a5e5123faca6863bf07d75a4e0" +checksum = "d4790277f605573dd24b6751701e0823582a63c7cafc095e427e6c66e45dd75e" dependencies = [ "bitflags 1.2.1", "errno", "io-lifetimes", "libc", "linux-raw-sys 0.3.7", - "windows-sys 0.48.0", + "windows-sys 0.45.0", ] [[package]] @@ -6887,7 +6887,7 @@ dependencies = [ "cfg-if 1.0.0", "fastrand", "redox_syscall 0.3.5", - "rustix 0.37.13", + "rustix 0.37.1", "windows-sys 0.45.0", ] diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 12927cca13..3fa2cdd8ed 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -16,7 +16,7 @@ pub mod wallet; use clap::{ArgGroup, ArgMatches, ColorChoice}; use color_eyre::eyre::Result; use utils::*; -pub use utils::{dispatch_prompt, safe_exit, Cmd, TESTIN}; +pub use utils::{safe_exit, Cmd}; pub use self::context::Context; use crate::cli::api::CliIo; diff --git a/apps/src/lib/cli/utils.rs b/apps/src/lib/cli/utils.rs index c35298976c..73d144596c 100644 --- a/apps/src/lib/cli/utils.rs +++ b/apps/src/lib/cli/utils.rs @@ -6,7 +6,6 @@ use std::str::FromStr; use clap::{ArgAction, ArgMatches}; use color_eyre::eyre::Result; -use lazy_static::lazy_static; use super::args; use super::context::{Context, FromContext}; @@ -342,53 +341,3 @@ pub fn safe_exit(_: i32) -> ! { panic!("Test failed because the client exited unexpectedly.") } - -lazy_static! { - /// A replacement for stdin in testing. - pub static ref TESTIN: std::sync::Arc>> = - std::sync::Arc::new(std::sync::Mutex::new(vec![])); -} - -/// A generic function for displaying a prompt to users and reading -/// in their response. -fn prompt_aux(mut reader: R, mut writer: W, question: &str) -> String -where - R: std::io::Read, - W: Write, -{ - write!(&mut writer, "{}", question).expect("Unable to write"); - writer.flush().unwrap(); - let mut s = String::new(); - reader.read_to_string(&mut s).expect("Unable to read"); - s -} - -/// A function that chooses how to dispatch prompts -/// to users. There is a hierarchy of feature flags -/// that determines this. If no flags are set, -/// the question is printed to stdout and response -/// read from stdin. -pub fn dispatch_prompt(question: impl AsRef) -> String { - if cfg!(feature = "testing") { - prompt_aux( - TESTIN.lock().unwrap().as_slice(), - std::io::stdout(), - question.as_ref(), - ) - } else { - prompt_aux( - std::io::stdin().lock(), - std::io::stdout(), - question.as_ref(), - ) - } -} - -#[macro_export] -/// A convenience macro for formatting the user prompt before -/// forwarding it to the `[dispatch_prompt]` method. -macro_rules! prompt { - ($($arg:tt)*) => {{ - $crate::cli::dispatch_prompt(format!("{}", format_args!($($arg)*))) - }} -} diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 7ab2c3a03a..87f8666a88 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -48,13 +48,12 @@ use namada::types::masp::{BalanceOwner, ExtendedViewingKey, PaymentAddress}; use namada::types::storage::{BlockHeight, BlockResults, Epoch, Key, KeySeg}; use namada::types::token::{Change, MaspDenom}; use namada::types::{storage, token}; -use namada::{display, display_line, edisplay}; +use namada::{display, display_line, edisplay, prompt}; use tokio::time::Instant; use crate::cli::{self, args}; use crate::facade::tendermint::merkle::proof::Proof; use crate::facade::tendermint_rpc::error::Error as TError; -use crate::prompt; use crate::wallet::CliWalletUtils; /// Query the status of a given transaction. @@ -456,7 +455,7 @@ pub async fn query_pinned_balance< } // If a suitable viewing key was not found, then demand it from the user if balance == pinned_error { - let vk_str = prompt!("Enter the viewing key for {}: ", owner); + let vk_str = prompt!(IO, "Enter the viewing key for {}: ", owner); let fvk = match ExtendedViewingKey::from_str(vk_str.trim()) { Ok(fvk) => fvk, _ => { diff --git a/apps/src/lib/node/ledger/shell/testing/client.rs b/apps/src/lib/node/ledger/shell/testing/client.rs index 0f27a2f6f1..1aa35ca321 100644 --- a/apps/src/lib/node/ledger/shell/testing/client.rs +++ b/apps/src/lib/node/ledger/shell/testing/client.rs @@ -3,14 +3,14 @@ use std::ops::ControlFlow; use clap::Command as App; use eyre::Report; use namada::types::control_flow::Halt; -use namada::types::io::{DefaultIo, Io}; +use namada::types::io::Io; use tendermint_config::net::Address as TendermintAddress; use super::node::MockNode; use crate::cli::api::{CliApi, CliClient}; use crate::cli::args::Global; use crate::cli::{args, cmds, Cmd, Context, NamadaClient}; -use crate::node::ledger::shell::testing::utils::Bin; +use crate::node::ledger::shell::testing::utils::{Bin, TestingIo}; pub fn run( node: &MockNode, @@ -25,7 +25,7 @@ pub fn run( wasm_dir: Some(locked.wasm_dir.clone()), } }; - let ctx = Context::new::(global.clone())?; + let ctx = Context::new::(global.clone())?; let rt = tokio::runtime::Runtime::new().unwrap(); match who { @@ -47,7 +47,7 @@ pub fn run( NamadaClient::WithoutContext(sub_cmd, global) } }; - rt.block_on(CliApi::::handle_client_command( + rt.block_on(CliApi::::handle_client_command( Some(node), cmd, )) @@ -60,7 +60,7 @@ pub fn run( let cmd = cmds::NamadaWallet::parse(&matches) .expect("Could not parse wallet command"); - CliApi::::handle_wallet_command(cmd, ctx) + CliApi::::handle_wallet_command(cmd, ctx) } Bin::Relayer => { args.insert(0, "relayer"); @@ -69,7 +69,7 @@ pub fn run( let matches = app.get_matches_from(args.clone()); let cmd = cmds::NamadaRelayer::parse(&matches) .expect("Could not parse wallet command"); - rt.block_on(CliApi::::handle_relayer_command( + rt.block_on(CliApi::::handle_relayer_command( Some(node), cmd, )) diff --git a/apps/src/lib/node/ledger/shell/testing/utils.rs b/apps/src/lib/node/ledger/shell/testing/utils.rs index e66ead21e7..95da7efb85 100644 --- a/apps/src/lib/node/ledger/shell/testing/utils.rs +++ b/apps/src/lib/node/ledger/shell/testing/utils.rs @@ -1,5 +1,7 @@ use std::path::{Path, PathBuf}; +use lazy_static::lazy_static; +use namada::types::io::{prompt_aux, read_aux, Io}; use tempfile::tempdir; /// Namada binaries @@ -46,3 +48,198 @@ impl Default for TestDir { Self::new() } } + +/// The max number of bytes that the is currently remembered from stdout while +/// testing. +const TESTOUT_BUF_SIZE: usize = 100_000; + +lazy_static! { + /// A replacement for stdout in testing. The maximum number of bytes + /// it holds is limited to prevent memory issues. + pub static ref TESTOUT: std::sync::Arc>> = + std::sync::Arc::new(std::sync::Mutex::new(FixedBuffer::new(TESTOUT_BUF_SIZE))); +} + +lazy_static! { + /// A replacement for stdin in testing. + pub static ref TESTIN: std::sync::Arc>> = + std::sync::Arc::new(std::sync::Mutex::new(vec![])); +} + +pub struct TestingIo; + +impl Io for TestingIo { + fn print(output: impl AsRef) { + let mut testout = TESTOUT.lock().unwrap(); + testout.append(output.as_ref().as_bytes().to_vec()); + print!("{}", output.as_ref()); + } + + fn println(output: impl AsRef) { + let mut testout = TESTOUT.lock().unwrap(); + let mut bytes = output.as_ref().as_bytes().to_vec(); + bytes.extend_from_slice("\n".as_bytes()); + testout.append(bytes); + println!("{}", output.as_ref()); + } + + fn write( + _: W, + output: impl AsRef, + ) -> std::io::Result<()> { + Self::print(output); + Ok(()) + } + + fn writeln( + _: W, + output: impl AsRef, + ) -> std::io::Result<()> { + Self::println(output); + Ok(()) + } + + fn eprintln(output: impl AsRef) { + let mut testout = TESTOUT.lock().unwrap(); + let mut bytes = output.as_ref().as_bytes().to_vec(); + bytes.extend_from_slice("\n".as_bytes()); + testout.append(bytes); + eprintln!("{}", output.as_ref()); + } + + fn read() -> std::io::Result { + read_aux(TESTIN.lock().unwrap().as_slice()) + } + + fn prompt(question: impl AsRef) -> String { + prompt_aux( + TESTIN.lock().unwrap().as_slice(), + std::io::stdout(), + question.as_ref(), + ) + } +} + +/// Test helper that captures stdout of +/// a process. +pub struct CapturedOutput { + pub output: String, + pub result: T, + input: String, +} + +impl CapturedOutput { + pub fn with_input(input: String) -> Self { + Self { + output: "".to_string(), + result: (), + input, + } + } +} + +impl CapturedOutput { + /// Run a client command and capture + /// the output to the mocked stdout. + pub fn of(func: F) -> Self + where + F: FnOnce() -> T, + { + let mut capture = Self { + output: Default::default(), + result: func(), + input: Default::default(), + }; + capture.output = TESTOUT.lock().unwrap().read_string(); + capture + } + + /// Run a client command with input to the mocked stdin and capture + /// the output to the mocked stdout + pub fn run(&self, func: F) -> CapturedOutput + where + F: FnOnce() -> U, + { + { + // write the input to the mocked stdin + let mut buf = TESTIN.lock().unwrap(); + buf.clear(); + buf.extend_from_slice(self.input.as_bytes()); + } + CapturedOutput::of(func) + } + + /// Check if the captured output contains the regex. + pub fn matches(&self, needle: regex::Regex) -> bool { + needle.captures(&self.output).is_some() + } + + /// Check if the captured output contains the string. + pub fn contains(&self, needle: &str) -> bool { + let needle = regex::Regex::new(needle).unwrap(); + self.matches(needle) + } +} + +/// A buffer with a max size. Drops elements from the front on +/// size overflow. +pub struct FixedBuffer { + inner: Vec, + max_size: usize, +} + +impl FixedBuffer { + fn new(max_size: usize) -> Self { + Self { + inner: vec![], + max_size, + } + } + + /// Remove the first `size` elements from the buffer. + fn roll(&mut self, size: usize) { + self.inner = self.inner[size..].to_vec(); + } + + /// Add data to the end of the buffer, deleting from the + /// front as necessary. + fn append(&mut self, mut other: Vec) { + // if new data exceeds max size, take the tail. + if other.len() > self.max_size { + self.inner = other[(other.len() - self.max_size)..].to_vec(); + return; + } + // check if appending the data overflows buffer + let free_space = self.max_size - self.inner.len(); + if other.len() > free_space { + // delete the minimum amount of data from the front of the buffer + // to fit new data. + self.roll(other.len() - free_space); + } + self.inner.append(&mut other); + } +} + +impl FixedBuffer { + /// Read the inner buffer out to string + pub fn read_string(&mut self) -> String { + let mut fresh = vec![]; + std::mem::swap(&mut fresh, &mut self.inner); + String::from_utf8(fresh).unwrap() + } +} + +#[cfg(test)] +mod testing { + use super::*; + + #[test] + fn test_buffer() { + let mut buffer = FixedBuffer::::new(10); + buffer.inner = (1u64..=9_u64).collect(); + buffer.append(vec![10, 11, 12, 13, 14, 15]); + assert_eq!(buffer.inner, (6u64..=15_u64).collect::>()); + buffer.append((20u64..=40_u64).collect()); + assert_eq!(buffer.inner, (31u64..=40_u64).collect::>()); + } +} diff --git a/shared/src/types/io.rs b/shared/src/types/io.rs index 3f130887da..6e1b0e8c3c 100644 --- a/shared/src/types/io.rs +++ b/shared/src/types/io.rs @@ -115,3 +115,12 @@ macro_rules! edisplay { <$io>::eprintln(format_args!($($args)*).to_string()) }; } + +#[macro_export] +/// A convenience macro for formatting the user prompt before +/// forwarding it to the [`Io::prompt`] method. +macro_rules! prompt { + ($io:ty,$($arg:tt)*) => {{ + <$io>::prompt(format!("{}", format_args!($($arg)*))) + }} +} diff --git a/tests/src/integration.rs b/tests/src/integration.rs index 8642e0e03c..1a7c84dbfb 100644 --- a/tests/src/integration.rs +++ b/tests/src/integration.rs @@ -1,3 +1,2 @@ mod masp; mod setup; -mod utils; diff --git a/tests/src/integration/masp.rs b/tests/src/integration/masp.rs index b8f9c47ed5..841bdb01c8 100644 --- a/tests/src/integration/masp.rs +++ b/tests/src/integration/masp.rs @@ -5,7 +5,7 @@ use color_eyre::owo_colors::OwoColorize; use namada::types::io::DefaultIo; use namada_apps::client::tx::CLIShieldedUtils; use namada_apps::node::ledger::shell::testing::client::run; -use namada_apps::node::ledger::shell::testing::utils::Bin; +use namada_apps::node::ledger::shell::testing::utils::{Bin, CapturedOutput}; use namada_core::types::address::{btc, eth, masp_rewards}; use namada_core::types::token; use namada_core::types::token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; @@ -17,7 +17,6 @@ use crate::e2e::setup::constants::{ AC_PAYMENT_ADDRESS, AC_VIEWING_KEY, ALBERT, A_SPENDING_KEY, BB_PAYMENT_ADDRESS, BERTHA, BTC, B_SPENDING_KEY, CHRISTEL, ETH, MASP, NAM, }; -use crate::integration::utils::CapturedOutput; /// In this test we verify that users of the MASP receive the correct rewards /// for leaving their assets in the pool for varying periods of time. diff --git a/tests/src/integration/utils.rs b/tests/src/integration/utils.rs deleted file mode 100644 index f626a001ee..0000000000 --- a/tests/src/integration/utils.rs +++ /dev/null @@ -1,83 +0,0 @@ -use std::fs::File; -use std::path::PathBuf; -use std::sync::Arc; - -struct TempFile(PathBuf); -impl TempFile { - fn new(path: PathBuf) -> (Self, File) { - let f = File::create(&path).unwrap(); - (Self(path), f) - } -} - -impl Drop for TempFile { - fn drop(&mut self) { - _ = std::fs::remove_file(&self.0); - } -} - -/// Test helper that captures stdout of -/// a process. -pub struct CapturedOutput { - pub output: String, - pub result: T, - input: String, -} - -impl CapturedOutput { - pub fn with_input(input: String) -> Self { - Self { - output: "".to_string(), - result: (), - input, - } - } -} - -impl CapturedOutput { - /// Run a client command and capture - /// the output to the mocked stdout. - pub(crate) fn of(func: F) -> Self - where - F: FnOnce() -> T, - { - std::io::set_output_capture(Some(Default::default())); - let mut capture = Self { - output: Default::default(), - result: func(), - input: Default::default(), - }; - let captured = std::io::set_output_capture(None); - let captured = captured.unwrap(); - let captured = Arc::try_unwrap(captured).unwrap(); - let captured = captured.into_inner().unwrap(); - capture.output = String::from_utf8(captured).unwrap(); - capture - } - - /// Run a client command with input to the mocked stdin and capture - /// the output to the mocked stdout - pub fn run(&self, func: F) -> CapturedOutput - where - F: FnOnce() -> U, - { - { - // write the input to the mocked stdin - let mut buf = namada_apps::cli::TESTIN.lock().unwrap(); - buf.clear(); - buf.extend_from_slice(self.input.as_bytes()); - } - CapturedOutput::of(func) - } - - /// Check if the captured output contains the regex. - pub fn matches(&self, needle: regex::Regex) -> bool { - needle.captures(&self.output).is_some() - } - - /// Check if the captured output contains the string. - pub fn contains(&self, needle: &str) -> bool { - let needle = regex::Regex::new(needle).unwrap(); - self.matches(needle) - } -} diff --git a/tests/src/lib.rs b/tests/src/lib.rs index 7e35947477..1d9958a30a 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -1,4 +1,3 @@ -#![cfg_attr(test, feature(internal_output_capture))] //! Namada integrations and WASM tests and testing helpers. #![doc(html_favicon_url = "https://dev.namada.net/master/favicon.png")] @@ -13,7 +12,6 @@ pub use vm_host_env::{ibc, tx, vp}; #[cfg(test)] mod e2e; #[cfg(test)] -#[allow(dead_code)] mod integration; pub mod native_vp; pub mod storage; diff --git a/wasm/checksums.json b/wasm/checksums.json index 71bcf7c9ef..adf06b5afe 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,21 +1,21 @@ { - "tx_bond.wasm": "tx_bond.e9201ea2342459a86343e5e6db1400b23d0843fe4407f6f759c65dc63395b5cd.wasm", - "tx_bridge_pool.wasm": "tx_bridge_pool.c1b7457cc85bfb4ca141a3f1157700be2d0f672aec21a2a7883a6264e8775cc1.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.c91a746933ecb67daf41d77d6cb7810277dfb661a72e6f4726cee7300f750cde.wasm", - "tx_ibc.wasm": "tx_ibc.917a7c8ad4138679eb467e8a18b0234d6c3ba56802a728186d7fcd047e0c0c4b.wasm", - "tx_init_account.wasm": "tx_init_account.6fc0c6ebbf933befd27f8515f0d439814ad8ea2732ec62271fe5ec4da177340e.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.0dd098e0962ba8b3fac93eefae0524959125ce334edf05e05edbdf4d38ec9069.wasm", - "tx_init_validator.wasm": "tx_init_validator.88f4d2b5cc358e8e9a53512314450161b8ab123a54ccbbbc44c6272fc3496ee2.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.4d4243a995611cb5c53bb7ad9657dd6112f8f3e309b1497c4af2763dcff5f0e7.wasm", - "tx_transfer.wasm": "tx_transfer.c6d4ac6e8311a75f199a0ca009f2b80c1ef107eeadfe7bbab94ff6453f075954.wasm", - "tx_unbond.wasm": "tx_unbond.00df2be4c4eaa94bec27f4bb89ccb96f7725cfdc4af2c965473ec1e9679f0989.wasm", - "tx_unjail_validator.wasm": "tx_unjail_validator.96081388ae0cb289df7fb48b37a5f640c867205f19c9c44a2af4f2f9598c4a27.wasm", - "tx_update_vp.wasm": "tx_update_vp.a1b0a203c05769a90c63711ef3ff922c5ba4b82f74511ade2a446ead47a2913b.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.9b4103f524a21e45d39361afb349e60a5010d94db5bf5ed5a8f678ec9c0df1b8.wasm", - "tx_withdraw.wasm": "tx_withdraw.37ffd46d73cc8cb67c14df42389b18fff03cd0c77861e2d9fc64ac486a13f65c.wasm", - "vp_implicit.wasm": "vp_implicit.e16621fd592ef6ad906b820762959dec1224e5a2e6917e8d8f0258601ed8dadd.wasm", - "vp_masp.wasm": "vp_masp.6aee2c99eba0b680198c985aabb1c6be877538ae338e36e9164c60685eec9334.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.1a78846b5aca8ce6a34c258dec573fec7d247ae07c09dd7b92196b2bd0a8d1dd.wasm", - "vp_user.wasm": "vp_user.e0200e6274839583464001363bfce2137c8a0a9191b242a9c34c32e31b525a04.wasm", - "vp_validator.wasm": "vp_validator.3a23a0a5629de85f2029543d3fc3d9d3fd58473280859b4984a9a25723c1ca88.wasm" + "tx_bond.wasm": "tx_bond.3d9341191f3059b478435c52ec0db4b19fe9d2b7a60ac7371287fb15552dbe3b.wasm", + "tx_bridge_pool.wasm": "tx_bridge_pool.0d8d4de6222aeb5b0cb80fa39b1ce1ee9e2f50115ffdaa2a1e79eabdddeb6bfe.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.349170accac147e8b4b77eb10ded0421936176b09d8913246235db0c5a4fac83.wasm", + "tx_ibc.wasm": "tx_ibc.5ddb7f3032d8b59446486a7fb20bac8c43c5a002f6e9b3309ff2e01048b98c7f.wasm", + "tx_init_account.wasm": "tx_init_account.f254fa819c85694d01f440132690ac54b3a8911a5eca9ccb49bc6171cf219017.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.1cf4ac19672bc368ff1e8ea0fd31507ac4a9a33c1b21ff80d85de8b36c6bce66.wasm", + "tx_init_validator.wasm": "tx_init_validator.bd5aa6513bf890e4591413fe6a4345dc28a0767ebc2fad2279e851fb1373091a.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.8f252e6fd0ab74be7f7afb4dbe4ba2b3d441011fd57ce5ccd06fe24109728019.wasm", + "tx_transfer.wasm": "tx_transfer.cdb2edbaa152608e2ae6d96a586955b911b74cc097228fee2bb5768f5f6db0ea.wasm", + "tx_unbond.wasm": "tx_unbond.0e6319063d74311a42a405435c15aa867894fc0a7ae209c08fc5fa9eee4013c5.wasm", + "tx_unjail_validator.wasm": "tx_unjail_validator.24e345f69f62fc8b75c475eee66e3252dc06a5468411f6770096b1b8ced8f022.wasm", + "tx_update_vp.wasm": "tx_update_vp.86123368b829488aface2804afc397a8c26873e9f319e3183fb211b292239e1d.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.80586902bae368088edfdef22a6fcc25aee8779620fd48eabbe1f3f11754d701.wasm", + "tx_withdraw.wasm": "tx_withdraw.5648dac90b0f25c94d60fb39dfed6120f6eed8794e4c3dbf554af36adf7bafc3.wasm", + "vp_implicit.wasm": "vp_implicit.18f2b20fa776493a0182b05e95b3eb0676be702dc0a4d39a09cb9ab51d116146.wasm", + "vp_masp.wasm": "vp_masp.b07f344c2d760ee69dfcef1ae4e703f63c7c6594a377a48550ef576f9fcaca96.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.9e3c9d133e52d4b762b9fc7d3bcc1e7d0ad2bc4d1754d390ed31555471f184d5.wasm", + "vp_user.wasm": "vp_user.df279909cb2de9d5125bbfe8b34b14ff3b924ed7db61dcc93cbb06f2f3f29509.wasm", + "vp_validator.wasm": "vp_validator.e3ed90196c676b73bcfa2f4142e2831b306eef56bde73243b2d50ddc5ab8e2b2.wasm" } \ No newline at end of file From b80a9bd34e961dc9f13144b3e5bfdbd93fc92021 Mon Sep 17 00:00:00 2001 From: satan Date: Mon, 24 Jul 2023 17:00:05 +0200 Subject: [PATCH 02/28] [feat]: Refactored the masp integration tests to no longer need the nightly feature --- Cargo.lock | 10 +- apps/src/lib/cli.rs | 2 +- apps/src/lib/cli/utils.rs | 51 ----- apps/src/lib/client/rpc.rs | 5 +- .../lib/node/ledger/shell/testing/client.rs | 12 +- .../lib/node/ledger/shell/testing/utils.rs | 197 ++++++++++++++++++ shared/src/types/io.rs | 9 + tests/src/integration.rs | 1 - tests/src/integration/masp.rs | 3 +- tests/src/integration/utils.rs | 83 -------- tests/src/lib.rs | 2 - wasm/checksums.json | 38 ++-- 12 files changed, 240 insertions(+), 173 deletions(-) delete mode 100644 tests/src/integration/utils.rs diff --git a/Cargo.lock b/Cargo.lock index 9517b5cf7b..5512620733 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3608,7 +3608,7 @@ checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", - "rustix 0.37.13", + "rustix 0.37.1", "windows-sys 0.48.0", ] @@ -6022,16 +6022,16 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.13" +version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79bef90eb6d984c72722595b5b1348ab39275a5e5123faca6863bf07d75a4e0" +checksum = "d4790277f605573dd24b6751701e0823582a63c7cafc095e427e6c66e45dd75e" dependencies = [ "bitflags 1.2.1", "errno", "io-lifetimes", "libc", "linux-raw-sys 0.3.7", - "windows-sys 0.48.0", + "windows-sys 0.45.0", ] [[package]] @@ -6887,7 +6887,7 @@ dependencies = [ "cfg-if 1.0.0", "fastrand", "redox_syscall 0.3.5", - "rustix 0.37.13", + "rustix 0.37.1", "windows-sys 0.45.0", ] diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 12927cca13..3fa2cdd8ed 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -16,7 +16,7 @@ pub mod wallet; use clap::{ArgGroup, ArgMatches, ColorChoice}; use color_eyre::eyre::Result; use utils::*; -pub use utils::{dispatch_prompt, safe_exit, Cmd, TESTIN}; +pub use utils::{safe_exit, Cmd}; pub use self::context::Context; use crate::cli::api::CliIo; diff --git a/apps/src/lib/cli/utils.rs b/apps/src/lib/cli/utils.rs index c35298976c..73d144596c 100644 --- a/apps/src/lib/cli/utils.rs +++ b/apps/src/lib/cli/utils.rs @@ -6,7 +6,6 @@ use std::str::FromStr; use clap::{ArgAction, ArgMatches}; use color_eyre::eyre::Result; -use lazy_static::lazy_static; use super::args; use super::context::{Context, FromContext}; @@ -342,53 +341,3 @@ pub fn safe_exit(_: i32) -> ! { panic!("Test failed because the client exited unexpectedly.") } - -lazy_static! { - /// A replacement for stdin in testing. - pub static ref TESTIN: std::sync::Arc>> = - std::sync::Arc::new(std::sync::Mutex::new(vec![])); -} - -/// A generic function for displaying a prompt to users and reading -/// in their response. -fn prompt_aux(mut reader: R, mut writer: W, question: &str) -> String -where - R: std::io::Read, - W: Write, -{ - write!(&mut writer, "{}", question).expect("Unable to write"); - writer.flush().unwrap(); - let mut s = String::new(); - reader.read_to_string(&mut s).expect("Unable to read"); - s -} - -/// A function that chooses how to dispatch prompts -/// to users. There is a hierarchy of feature flags -/// that determines this. If no flags are set, -/// the question is printed to stdout and response -/// read from stdin. -pub fn dispatch_prompt(question: impl AsRef) -> String { - if cfg!(feature = "testing") { - prompt_aux( - TESTIN.lock().unwrap().as_slice(), - std::io::stdout(), - question.as_ref(), - ) - } else { - prompt_aux( - std::io::stdin().lock(), - std::io::stdout(), - question.as_ref(), - ) - } -} - -#[macro_export] -/// A convenience macro for formatting the user prompt before -/// forwarding it to the `[dispatch_prompt]` method. -macro_rules! prompt { - ($($arg:tt)*) => {{ - $crate::cli::dispatch_prompt(format!("{}", format_args!($($arg)*))) - }} -} diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 56696fb022..00435eee31 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -48,13 +48,12 @@ use namada::types::masp::{BalanceOwner, ExtendedViewingKey, PaymentAddress}; use namada::types::storage::{BlockHeight, BlockResults, Epoch, Key, KeySeg}; use namada::types::token::{Change, MaspDenom}; use namada::types::{storage, token}; -use namada::{display, display_line, edisplay_line}; +use namada::{display, display_line, edisplay_lin, prompt}; use tokio::time::Instant; use crate::cli::{self, args}; use crate::facade::tendermint::merkle::proof::Proof; use crate::facade::tendermint_rpc::error::Error as TError; -use crate::prompt; use crate::wallet::CliWalletUtils; /// Query the status of a given transaction. @@ -456,7 +455,7 @@ pub async fn query_pinned_balance< } // If a suitable viewing key was not found, then demand it from the user if balance == pinned_error { - let vk_str = prompt!("Enter the viewing key for {}: ", owner); + let vk_str = prompt!(IO, "Enter the viewing key for {}: ", owner); let fvk = match ExtendedViewingKey::from_str(vk_str.trim()) { Ok(fvk) => fvk, _ => { diff --git a/apps/src/lib/node/ledger/shell/testing/client.rs b/apps/src/lib/node/ledger/shell/testing/client.rs index 0f27a2f6f1..1aa35ca321 100644 --- a/apps/src/lib/node/ledger/shell/testing/client.rs +++ b/apps/src/lib/node/ledger/shell/testing/client.rs @@ -3,14 +3,14 @@ use std::ops::ControlFlow; use clap::Command as App; use eyre::Report; use namada::types::control_flow::Halt; -use namada::types::io::{DefaultIo, Io}; +use namada::types::io::Io; use tendermint_config::net::Address as TendermintAddress; use super::node::MockNode; use crate::cli::api::{CliApi, CliClient}; use crate::cli::args::Global; use crate::cli::{args, cmds, Cmd, Context, NamadaClient}; -use crate::node::ledger::shell::testing::utils::Bin; +use crate::node::ledger::shell::testing::utils::{Bin, TestingIo}; pub fn run( node: &MockNode, @@ -25,7 +25,7 @@ pub fn run( wasm_dir: Some(locked.wasm_dir.clone()), } }; - let ctx = Context::new::(global.clone())?; + let ctx = Context::new::(global.clone())?; let rt = tokio::runtime::Runtime::new().unwrap(); match who { @@ -47,7 +47,7 @@ pub fn run( NamadaClient::WithoutContext(sub_cmd, global) } }; - rt.block_on(CliApi::::handle_client_command( + rt.block_on(CliApi::::handle_client_command( Some(node), cmd, )) @@ -60,7 +60,7 @@ pub fn run( let cmd = cmds::NamadaWallet::parse(&matches) .expect("Could not parse wallet command"); - CliApi::::handle_wallet_command(cmd, ctx) + CliApi::::handle_wallet_command(cmd, ctx) } Bin::Relayer => { args.insert(0, "relayer"); @@ -69,7 +69,7 @@ pub fn run( let matches = app.get_matches_from(args.clone()); let cmd = cmds::NamadaRelayer::parse(&matches) .expect("Could not parse wallet command"); - rt.block_on(CliApi::::handle_relayer_command( + rt.block_on(CliApi::::handle_relayer_command( Some(node), cmd, )) diff --git a/apps/src/lib/node/ledger/shell/testing/utils.rs b/apps/src/lib/node/ledger/shell/testing/utils.rs index e66ead21e7..95da7efb85 100644 --- a/apps/src/lib/node/ledger/shell/testing/utils.rs +++ b/apps/src/lib/node/ledger/shell/testing/utils.rs @@ -1,5 +1,7 @@ use std::path::{Path, PathBuf}; +use lazy_static::lazy_static; +use namada::types::io::{prompt_aux, read_aux, Io}; use tempfile::tempdir; /// Namada binaries @@ -46,3 +48,198 @@ impl Default for TestDir { Self::new() } } + +/// The max number of bytes that the is currently remembered from stdout while +/// testing. +const TESTOUT_BUF_SIZE: usize = 100_000; + +lazy_static! { + /// A replacement for stdout in testing. The maximum number of bytes + /// it holds is limited to prevent memory issues. + pub static ref TESTOUT: std::sync::Arc>> = + std::sync::Arc::new(std::sync::Mutex::new(FixedBuffer::new(TESTOUT_BUF_SIZE))); +} + +lazy_static! { + /// A replacement for stdin in testing. + pub static ref TESTIN: std::sync::Arc>> = + std::sync::Arc::new(std::sync::Mutex::new(vec![])); +} + +pub struct TestingIo; + +impl Io for TestingIo { + fn print(output: impl AsRef) { + let mut testout = TESTOUT.lock().unwrap(); + testout.append(output.as_ref().as_bytes().to_vec()); + print!("{}", output.as_ref()); + } + + fn println(output: impl AsRef) { + let mut testout = TESTOUT.lock().unwrap(); + let mut bytes = output.as_ref().as_bytes().to_vec(); + bytes.extend_from_slice("\n".as_bytes()); + testout.append(bytes); + println!("{}", output.as_ref()); + } + + fn write( + _: W, + output: impl AsRef, + ) -> std::io::Result<()> { + Self::print(output); + Ok(()) + } + + fn writeln( + _: W, + output: impl AsRef, + ) -> std::io::Result<()> { + Self::println(output); + Ok(()) + } + + fn eprintln(output: impl AsRef) { + let mut testout = TESTOUT.lock().unwrap(); + let mut bytes = output.as_ref().as_bytes().to_vec(); + bytes.extend_from_slice("\n".as_bytes()); + testout.append(bytes); + eprintln!("{}", output.as_ref()); + } + + fn read() -> std::io::Result { + read_aux(TESTIN.lock().unwrap().as_slice()) + } + + fn prompt(question: impl AsRef) -> String { + prompt_aux( + TESTIN.lock().unwrap().as_slice(), + std::io::stdout(), + question.as_ref(), + ) + } +} + +/// Test helper that captures stdout of +/// a process. +pub struct CapturedOutput { + pub output: String, + pub result: T, + input: String, +} + +impl CapturedOutput { + pub fn with_input(input: String) -> Self { + Self { + output: "".to_string(), + result: (), + input, + } + } +} + +impl CapturedOutput { + /// Run a client command and capture + /// the output to the mocked stdout. + pub fn of(func: F) -> Self + where + F: FnOnce() -> T, + { + let mut capture = Self { + output: Default::default(), + result: func(), + input: Default::default(), + }; + capture.output = TESTOUT.lock().unwrap().read_string(); + capture + } + + /// Run a client command with input to the mocked stdin and capture + /// the output to the mocked stdout + pub fn run(&self, func: F) -> CapturedOutput + where + F: FnOnce() -> U, + { + { + // write the input to the mocked stdin + let mut buf = TESTIN.lock().unwrap(); + buf.clear(); + buf.extend_from_slice(self.input.as_bytes()); + } + CapturedOutput::of(func) + } + + /// Check if the captured output contains the regex. + pub fn matches(&self, needle: regex::Regex) -> bool { + needle.captures(&self.output).is_some() + } + + /// Check if the captured output contains the string. + pub fn contains(&self, needle: &str) -> bool { + let needle = regex::Regex::new(needle).unwrap(); + self.matches(needle) + } +} + +/// A buffer with a max size. Drops elements from the front on +/// size overflow. +pub struct FixedBuffer { + inner: Vec, + max_size: usize, +} + +impl FixedBuffer { + fn new(max_size: usize) -> Self { + Self { + inner: vec![], + max_size, + } + } + + /// Remove the first `size` elements from the buffer. + fn roll(&mut self, size: usize) { + self.inner = self.inner[size..].to_vec(); + } + + /// Add data to the end of the buffer, deleting from the + /// front as necessary. + fn append(&mut self, mut other: Vec) { + // if new data exceeds max size, take the tail. + if other.len() > self.max_size { + self.inner = other[(other.len() - self.max_size)..].to_vec(); + return; + } + // check if appending the data overflows buffer + let free_space = self.max_size - self.inner.len(); + if other.len() > free_space { + // delete the minimum amount of data from the front of the buffer + // to fit new data. + self.roll(other.len() - free_space); + } + self.inner.append(&mut other); + } +} + +impl FixedBuffer { + /// Read the inner buffer out to string + pub fn read_string(&mut self) -> String { + let mut fresh = vec![]; + std::mem::swap(&mut fresh, &mut self.inner); + String::from_utf8(fresh).unwrap() + } +} + +#[cfg(test)] +mod testing { + use super::*; + + #[test] + fn test_buffer() { + let mut buffer = FixedBuffer::::new(10); + buffer.inner = (1u64..=9_u64).collect(); + buffer.append(vec![10, 11, 12, 13, 14, 15]); + assert_eq!(buffer.inner, (6u64..=15_u64).collect::>()); + buffer.append((20u64..=40_u64).collect()); + assert_eq!(buffer.inner, (31u64..=40_u64).collect::>()); + } +} diff --git a/shared/src/types/io.rs b/shared/src/types/io.rs index 72904ac114..7cf9bd94e9 100644 --- a/shared/src/types/io.rs +++ b/shared/src/types/io.rs @@ -115,3 +115,12 @@ macro_rules! edisplay_line { <$io>::eprintln(format_args!($($args)*).to_string()) }; } + +#[macro_export] +/// A convenience macro for formatting the user prompt before +/// forwarding it to the [`Io::prompt`] method. +macro_rules! prompt { + ($io:ty,$($arg:tt)*) => {{ + <$io>::prompt(format!("{}", format_args!($($arg)*))) + }} +} diff --git a/tests/src/integration.rs b/tests/src/integration.rs index 8642e0e03c..1a7c84dbfb 100644 --- a/tests/src/integration.rs +++ b/tests/src/integration.rs @@ -1,3 +1,2 @@ mod masp; mod setup; -mod utils; diff --git a/tests/src/integration/masp.rs b/tests/src/integration/masp.rs index b8f9c47ed5..841bdb01c8 100644 --- a/tests/src/integration/masp.rs +++ b/tests/src/integration/masp.rs @@ -5,7 +5,7 @@ use color_eyre::owo_colors::OwoColorize; use namada::types::io::DefaultIo; use namada_apps::client::tx::CLIShieldedUtils; use namada_apps::node::ledger::shell::testing::client::run; -use namada_apps::node::ledger::shell::testing::utils::Bin; +use namada_apps::node::ledger::shell::testing::utils::{Bin, CapturedOutput}; use namada_core::types::address::{btc, eth, masp_rewards}; use namada_core::types::token; use namada_core::types::token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; @@ -17,7 +17,6 @@ use crate::e2e::setup::constants::{ AC_PAYMENT_ADDRESS, AC_VIEWING_KEY, ALBERT, A_SPENDING_KEY, BB_PAYMENT_ADDRESS, BERTHA, BTC, B_SPENDING_KEY, CHRISTEL, ETH, MASP, NAM, }; -use crate::integration::utils::CapturedOutput; /// In this test we verify that users of the MASP receive the correct rewards /// for leaving their assets in the pool for varying periods of time. diff --git a/tests/src/integration/utils.rs b/tests/src/integration/utils.rs deleted file mode 100644 index f626a001ee..0000000000 --- a/tests/src/integration/utils.rs +++ /dev/null @@ -1,83 +0,0 @@ -use std::fs::File; -use std::path::PathBuf; -use std::sync::Arc; - -struct TempFile(PathBuf); -impl TempFile { - fn new(path: PathBuf) -> (Self, File) { - let f = File::create(&path).unwrap(); - (Self(path), f) - } -} - -impl Drop for TempFile { - fn drop(&mut self) { - _ = std::fs::remove_file(&self.0); - } -} - -/// Test helper that captures stdout of -/// a process. -pub struct CapturedOutput { - pub output: String, - pub result: T, - input: String, -} - -impl CapturedOutput { - pub fn with_input(input: String) -> Self { - Self { - output: "".to_string(), - result: (), - input, - } - } -} - -impl CapturedOutput { - /// Run a client command and capture - /// the output to the mocked stdout. - pub(crate) fn of(func: F) -> Self - where - F: FnOnce() -> T, - { - std::io::set_output_capture(Some(Default::default())); - let mut capture = Self { - output: Default::default(), - result: func(), - input: Default::default(), - }; - let captured = std::io::set_output_capture(None); - let captured = captured.unwrap(); - let captured = Arc::try_unwrap(captured).unwrap(); - let captured = captured.into_inner().unwrap(); - capture.output = String::from_utf8(captured).unwrap(); - capture - } - - /// Run a client command with input to the mocked stdin and capture - /// the output to the mocked stdout - pub fn run(&self, func: F) -> CapturedOutput - where - F: FnOnce() -> U, - { - { - // write the input to the mocked stdin - let mut buf = namada_apps::cli::TESTIN.lock().unwrap(); - buf.clear(); - buf.extend_from_slice(self.input.as_bytes()); - } - CapturedOutput::of(func) - } - - /// Check if the captured output contains the regex. - pub fn matches(&self, needle: regex::Regex) -> bool { - needle.captures(&self.output).is_some() - } - - /// Check if the captured output contains the string. - pub fn contains(&self, needle: &str) -> bool { - let needle = regex::Regex::new(needle).unwrap(); - self.matches(needle) - } -} diff --git a/tests/src/lib.rs b/tests/src/lib.rs index 7e35947477..1d9958a30a 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -1,4 +1,3 @@ -#![cfg_attr(test, feature(internal_output_capture))] //! Namada integrations and WASM tests and testing helpers. #![doc(html_favicon_url = "https://dev.namada.net/master/favicon.png")] @@ -13,7 +12,6 @@ pub use vm_host_env::{ibc, tx, vp}; #[cfg(test)] mod e2e; #[cfg(test)] -#[allow(dead_code)] mod integration; pub mod native_vp; pub mod storage; diff --git a/wasm/checksums.json b/wasm/checksums.json index 71bcf7c9ef..adf06b5afe 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,21 +1,21 @@ { - "tx_bond.wasm": "tx_bond.e9201ea2342459a86343e5e6db1400b23d0843fe4407f6f759c65dc63395b5cd.wasm", - "tx_bridge_pool.wasm": "tx_bridge_pool.c1b7457cc85bfb4ca141a3f1157700be2d0f672aec21a2a7883a6264e8775cc1.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.c91a746933ecb67daf41d77d6cb7810277dfb661a72e6f4726cee7300f750cde.wasm", - "tx_ibc.wasm": "tx_ibc.917a7c8ad4138679eb467e8a18b0234d6c3ba56802a728186d7fcd047e0c0c4b.wasm", - "tx_init_account.wasm": "tx_init_account.6fc0c6ebbf933befd27f8515f0d439814ad8ea2732ec62271fe5ec4da177340e.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.0dd098e0962ba8b3fac93eefae0524959125ce334edf05e05edbdf4d38ec9069.wasm", - "tx_init_validator.wasm": "tx_init_validator.88f4d2b5cc358e8e9a53512314450161b8ab123a54ccbbbc44c6272fc3496ee2.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.4d4243a995611cb5c53bb7ad9657dd6112f8f3e309b1497c4af2763dcff5f0e7.wasm", - "tx_transfer.wasm": "tx_transfer.c6d4ac6e8311a75f199a0ca009f2b80c1ef107eeadfe7bbab94ff6453f075954.wasm", - "tx_unbond.wasm": "tx_unbond.00df2be4c4eaa94bec27f4bb89ccb96f7725cfdc4af2c965473ec1e9679f0989.wasm", - "tx_unjail_validator.wasm": "tx_unjail_validator.96081388ae0cb289df7fb48b37a5f640c867205f19c9c44a2af4f2f9598c4a27.wasm", - "tx_update_vp.wasm": "tx_update_vp.a1b0a203c05769a90c63711ef3ff922c5ba4b82f74511ade2a446ead47a2913b.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.9b4103f524a21e45d39361afb349e60a5010d94db5bf5ed5a8f678ec9c0df1b8.wasm", - "tx_withdraw.wasm": "tx_withdraw.37ffd46d73cc8cb67c14df42389b18fff03cd0c77861e2d9fc64ac486a13f65c.wasm", - "vp_implicit.wasm": "vp_implicit.e16621fd592ef6ad906b820762959dec1224e5a2e6917e8d8f0258601ed8dadd.wasm", - "vp_masp.wasm": "vp_masp.6aee2c99eba0b680198c985aabb1c6be877538ae338e36e9164c60685eec9334.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.1a78846b5aca8ce6a34c258dec573fec7d247ae07c09dd7b92196b2bd0a8d1dd.wasm", - "vp_user.wasm": "vp_user.e0200e6274839583464001363bfce2137c8a0a9191b242a9c34c32e31b525a04.wasm", - "vp_validator.wasm": "vp_validator.3a23a0a5629de85f2029543d3fc3d9d3fd58473280859b4984a9a25723c1ca88.wasm" + "tx_bond.wasm": "tx_bond.3d9341191f3059b478435c52ec0db4b19fe9d2b7a60ac7371287fb15552dbe3b.wasm", + "tx_bridge_pool.wasm": "tx_bridge_pool.0d8d4de6222aeb5b0cb80fa39b1ce1ee9e2f50115ffdaa2a1e79eabdddeb6bfe.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.349170accac147e8b4b77eb10ded0421936176b09d8913246235db0c5a4fac83.wasm", + "tx_ibc.wasm": "tx_ibc.5ddb7f3032d8b59446486a7fb20bac8c43c5a002f6e9b3309ff2e01048b98c7f.wasm", + "tx_init_account.wasm": "tx_init_account.f254fa819c85694d01f440132690ac54b3a8911a5eca9ccb49bc6171cf219017.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.1cf4ac19672bc368ff1e8ea0fd31507ac4a9a33c1b21ff80d85de8b36c6bce66.wasm", + "tx_init_validator.wasm": "tx_init_validator.bd5aa6513bf890e4591413fe6a4345dc28a0767ebc2fad2279e851fb1373091a.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.8f252e6fd0ab74be7f7afb4dbe4ba2b3d441011fd57ce5ccd06fe24109728019.wasm", + "tx_transfer.wasm": "tx_transfer.cdb2edbaa152608e2ae6d96a586955b911b74cc097228fee2bb5768f5f6db0ea.wasm", + "tx_unbond.wasm": "tx_unbond.0e6319063d74311a42a405435c15aa867894fc0a7ae209c08fc5fa9eee4013c5.wasm", + "tx_unjail_validator.wasm": "tx_unjail_validator.24e345f69f62fc8b75c475eee66e3252dc06a5468411f6770096b1b8ced8f022.wasm", + "tx_update_vp.wasm": "tx_update_vp.86123368b829488aface2804afc397a8c26873e9f319e3183fb211b292239e1d.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.80586902bae368088edfdef22a6fcc25aee8779620fd48eabbe1f3f11754d701.wasm", + "tx_withdraw.wasm": "tx_withdraw.5648dac90b0f25c94d60fb39dfed6120f6eed8794e4c3dbf554af36adf7bafc3.wasm", + "vp_implicit.wasm": "vp_implicit.18f2b20fa776493a0182b05e95b3eb0676be702dc0a4d39a09cb9ab51d116146.wasm", + "vp_masp.wasm": "vp_masp.b07f344c2d760ee69dfcef1ae4e703f63c7c6594a377a48550ef576f9fcaca96.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.9e3c9d133e52d4b762b9fc7d3bcc1e7d0ad2bc4d1754d390ed31555471f184d5.wasm", + "vp_user.wasm": "vp_user.df279909cb2de9d5125bbfe8b34b14ff3b924ed7db61dcc93cbb06f2f3f29509.wasm", + "vp_validator.wasm": "vp_validator.e3ed90196c676b73bcfa2f4142e2831b306eef56bde73243b2d50ddc5ab8e2b2.wasm" } \ No newline at end of file From e35ed2e10d530fa164fde3ef1906d37b448971f8 Mon Sep 17 00:00:00 2001 From: satan Date: Mon, 24 Jul 2023 23:50:54 +0200 Subject: [PATCH 03/28] [chore]: rebasing on bat/generic-io --- apps/src/lib/client/rpc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 00435eee31..6862ee2a39 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -48,7 +48,7 @@ use namada::types::masp::{BalanceOwner, ExtendedViewingKey, PaymentAddress}; use namada::types::storage::{BlockHeight, BlockResults, Epoch, Key, KeySeg}; use namada::types::token::{Change, MaspDenom}; use namada::types::{storage, token}; -use namada::{display, display_line, edisplay_lin, prompt}; +use namada::{display, display_line, edisplay_line, prompt}; use tokio::time::Instant; use crate::cli::{self, args}; From 79a953f3225ab992bfbdabd560c53c3a6185c2d7 Mon Sep 17 00:00:00 2001 From: satan Date: Mon, 24 Jul 2023 17:00:05 +0200 Subject: [PATCH 04/28] [feat]: Refactored the masp integration tests to no longer need the nightly feature --- Cargo.lock | 10 +- apps/src/lib/cli.rs | 2 +- apps/src/lib/cli/utils.rs | 51 ----- apps/src/lib/client/rpc.rs | 5 +- .../lib/node/ledger/shell/testing/client.rs | 12 +- .../lib/node/ledger/shell/testing/utils.rs | 197 ++++++++++++++++++ shared/src/types/io.rs | 9 + tests/src/integration.rs | 1 - tests/src/integration/masp.rs | 3 +- tests/src/integration/utils.rs | 83 -------- tests/src/lib.rs | 2 - wasm/checksums.json | 38 ++-- 12 files changed, 240 insertions(+), 173 deletions(-) delete mode 100644 tests/src/integration/utils.rs diff --git a/Cargo.lock b/Cargo.lock index 9517b5cf7b..5512620733 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3608,7 +3608,7 @@ checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", - "rustix 0.37.13", + "rustix 0.37.1", "windows-sys 0.48.0", ] @@ -6022,16 +6022,16 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.13" +version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79bef90eb6d984c72722595b5b1348ab39275a5e5123faca6863bf07d75a4e0" +checksum = "d4790277f605573dd24b6751701e0823582a63c7cafc095e427e6c66e45dd75e" dependencies = [ "bitflags 1.2.1", "errno", "io-lifetimes", "libc", "linux-raw-sys 0.3.7", - "windows-sys 0.48.0", + "windows-sys 0.45.0", ] [[package]] @@ -6887,7 +6887,7 @@ dependencies = [ "cfg-if 1.0.0", "fastrand", "redox_syscall 0.3.5", - "rustix 0.37.13", + "rustix 0.37.1", "windows-sys 0.45.0", ] diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 12927cca13..3fa2cdd8ed 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -16,7 +16,7 @@ pub mod wallet; use clap::{ArgGroup, ArgMatches, ColorChoice}; use color_eyre::eyre::Result; use utils::*; -pub use utils::{dispatch_prompt, safe_exit, Cmd, TESTIN}; +pub use utils::{safe_exit, Cmd}; pub use self::context::Context; use crate::cli::api::CliIo; diff --git a/apps/src/lib/cli/utils.rs b/apps/src/lib/cli/utils.rs index c35298976c..73d144596c 100644 --- a/apps/src/lib/cli/utils.rs +++ b/apps/src/lib/cli/utils.rs @@ -6,7 +6,6 @@ use std::str::FromStr; use clap::{ArgAction, ArgMatches}; use color_eyre::eyre::Result; -use lazy_static::lazy_static; use super::args; use super::context::{Context, FromContext}; @@ -342,53 +341,3 @@ pub fn safe_exit(_: i32) -> ! { panic!("Test failed because the client exited unexpectedly.") } - -lazy_static! { - /// A replacement for stdin in testing. - pub static ref TESTIN: std::sync::Arc>> = - std::sync::Arc::new(std::sync::Mutex::new(vec![])); -} - -/// A generic function for displaying a prompt to users and reading -/// in their response. -fn prompt_aux(mut reader: R, mut writer: W, question: &str) -> String -where - R: std::io::Read, - W: Write, -{ - write!(&mut writer, "{}", question).expect("Unable to write"); - writer.flush().unwrap(); - let mut s = String::new(); - reader.read_to_string(&mut s).expect("Unable to read"); - s -} - -/// A function that chooses how to dispatch prompts -/// to users. There is a hierarchy of feature flags -/// that determines this. If no flags are set, -/// the question is printed to stdout and response -/// read from stdin. -pub fn dispatch_prompt(question: impl AsRef) -> String { - if cfg!(feature = "testing") { - prompt_aux( - TESTIN.lock().unwrap().as_slice(), - std::io::stdout(), - question.as_ref(), - ) - } else { - prompt_aux( - std::io::stdin().lock(), - std::io::stdout(), - question.as_ref(), - ) - } -} - -#[macro_export] -/// A convenience macro for formatting the user prompt before -/// forwarding it to the `[dispatch_prompt]` method. -macro_rules! prompt { - ($($arg:tt)*) => {{ - $crate::cli::dispatch_prompt(format!("{}", format_args!($($arg)*))) - }} -} diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 56696fb022..00435eee31 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -48,13 +48,12 @@ use namada::types::masp::{BalanceOwner, ExtendedViewingKey, PaymentAddress}; use namada::types::storage::{BlockHeight, BlockResults, Epoch, Key, KeySeg}; use namada::types::token::{Change, MaspDenom}; use namada::types::{storage, token}; -use namada::{display, display_line, edisplay_line}; +use namada::{display, display_line, edisplay_lin, prompt}; use tokio::time::Instant; use crate::cli::{self, args}; use crate::facade::tendermint::merkle::proof::Proof; use crate::facade::tendermint_rpc::error::Error as TError; -use crate::prompt; use crate::wallet::CliWalletUtils; /// Query the status of a given transaction. @@ -456,7 +455,7 @@ pub async fn query_pinned_balance< } // If a suitable viewing key was not found, then demand it from the user if balance == pinned_error { - let vk_str = prompt!("Enter the viewing key for {}: ", owner); + let vk_str = prompt!(IO, "Enter the viewing key for {}: ", owner); let fvk = match ExtendedViewingKey::from_str(vk_str.trim()) { Ok(fvk) => fvk, _ => { diff --git a/apps/src/lib/node/ledger/shell/testing/client.rs b/apps/src/lib/node/ledger/shell/testing/client.rs index 0f27a2f6f1..1aa35ca321 100644 --- a/apps/src/lib/node/ledger/shell/testing/client.rs +++ b/apps/src/lib/node/ledger/shell/testing/client.rs @@ -3,14 +3,14 @@ use std::ops::ControlFlow; use clap::Command as App; use eyre::Report; use namada::types::control_flow::Halt; -use namada::types::io::{DefaultIo, Io}; +use namada::types::io::Io; use tendermint_config::net::Address as TendermintAddress; use super::node::MockNode; use crate::cli::api::{CliApi, CliClient}; use crate::cli::args::Global; use crate::cli::{args, cmds, Cmd, Context, NamadaClient}; -use crate::node::ledger::shell::testing::utils::Bin; +use crate::node::ledger::shell::testing::utils::{Bin, TestingIo}; pub fn run( node: &MockNode, @@ -25,7 +25,7 @@ pub fn run( wasm_dir: Some(locked.wasm_dir.clone()), } }; - let ctx = Context::new::(global.clone())?; + let ctx = Context::new::(global.clone())?; let rt = tokio::runtime::Runtime::new().unwrap(); match who { @@ -47,7 +47,7 @@ pub fn run( NamadaClient::WithoutContext(sub_cmd, global) } }; - rt.block_on(CliApi::::handle_client_command( + rt.block_on(CliApi::::handle_client_command( Some(node), cmd, )) @@ -60,7 +60,7 @@ pub fn run( let cmd = cmds::NamadaWallet::parse(&matches) .expect("Could not parse wallet command"); - CliApi::::handle_wallet_command(cmd, ctx) + CliApi::::handle_wallet_command(cmd, ctx) } Bin::Relayer => { args.insert(0, "relayer"); @@ -69,7 +69,7 @@ pub fn run( let matches = app.get_matches_from(args.clone()); let cmd = cmds::NamadaRelayer::parse(&matches) .expect("Could not parse wallet command"); - rt.block_on(CliApi::::handle_relayer_command( + rt.block_on(CliApi::::handle_relayer_command( Some(node), cmd, )) diff --git a/apps/src/lib/node/ledger/shell/testing/utils.rs b/apps/src/lib/node/ledger/shell/testing/utils.rs index e66ead21e7..95da7efb85 100644 --- a/apps/src/lib/node/ledger/shell/testing/utils.rs +++ b/apps/src/lib/node/ledger/shell/testing/utils.rs @@ -1,5 +1,7 @@ use std::path::{Path, PathBuf}; +use lazy_static::lazy_static; +use namada::types::io::{prompt_aux, read_aux, Io}; use tempfile::tempdir; /// Namada binaries @@ -46,3 +48,198 @@ impl Default for TestDir { Self::new() } } + +/// The max number of bytes that the is currently remembered from stdout while +/// testing. +const TESTOUT_BUF_SIZE: usize = 100_000; + +lazy_static! { + /// A replacement for stdout in testing. The maximum number of bytes + /// it holds is limited to prevent memory issues. + pub static ref TESTOUT: std::sync::Arc>> = + std::sync::Arc::new(std::sync::Mutex::new(FixedBuffer::new(TESTOUT_BUF_SIZE))); +} + +lazy_static! { + /// A replacement for stdin in testing. + pub static ref TESTIN: std::sync::Arc>> = + std::sync::Arc::new(std::sync::Mutex::new(vec![])); +} + +pub struct TestingIo; + +impl Io for TestingIo { + fn print(output: impl AsRef) { + let mut testout = TESTOUT.lock().unwrap(); + testout.append(output.as_ref().as_bytes().to_vec()); + print!("{}", output.as_ref()); + } + + fn println(output: impl AsRef) { + let mut testout = TESTOUT.lock().unwrap(); + let mut bytes = output.as_ref().as_bytes().to_vec(); + bytes.extend_from_slice("\n".as_bytes()); + testout.append(bytes); + println!("{}", output.as_ref()); + } + + fn write( + _: W, + output: impl AsRef, + ) -> std::io::Result<()> { + Self::print(output); + Ok(()) + } + + fn writeln( + _: W, + output: impl AsRef, + ) -> std::io::Result<()> { + Self::println(output); + Ok(()) + } + + fn eprintln(output: impl AsRef) { + let mut testout = TESTOUT.lock().unwrap(); + let mut bytes = output.as_ref().as_bytes().to_vec(); + bytes.extend_from_slice("\n".as_bytes()); + testout.append(bytes); + eprintln!("{}", output.as_ref()); + } + + fn read() -> std::io::Result { + read_aux(TESTIN.lock().unwrap().as_slice()) + } + + fn prompt(question: impl AsRef) -> String { + prompt_aux( + TESTIN.lock().unwrap().as_slice(), + std::io::stdout(), + question.as_ref(), + ) + } +} + +/// Test helper that captures stdout of +/// a process. +pub struct CapturedOutput { + pub output: String, + pub result: T, + input: String, +} + +impl CapturedOutput { + pub fn with_input(input: String) -> Self { + Self { + output: "".to_string(), + result: (), + input, + } + } +} + +impl CapturedOutput { + /// Run a client command and capture + /// the output to the mocked stdout. + pub fn of(func: F) -> Self + where + F: FnOnce() -> T, + { + let mut capture = Self { + output: Default::default(), + result: func(), + input: Default::default(), + }; + capture.output = TESTOUT.lock().unwrap().read_string(); + capture + } + + /// Run a client command with input to the mocked stdin and capture + /// the output to the mocked stdout + pub fn run(&self, func: F) -> CapturedOutput + where + F: FnOnce() -> U, + { + { + // write the input to the mocked stdin + let mut buf = TESTIN.lock().unwrap(); + buf.clear(); + buf.extend_from_slice(self.input.as_bytes()); + } + CapturedOutput::of(func) + } + + /// Check if the captured output contains the regex. + pub fn matches(&self, needle: regex::Regex) -> bool { + needle.captures(&self.output).is_some() + } + + /// Check if the captured output contains the string. + pub fn contains(&self, needle: &str) -> bool { + let needle = regex::Regex::new(needle).unwrap(); + self.matches(needle) + } +} + +/// A buffer with a max size. Drops elements from the front on +/// size overflow. +pub struct FixedBuffer { + inner: Vec, + max_size: usize, +} + +impl FixedBuffer { + fn new(max_size: usize) -> Self { + Self { + inner: vec![], + max_size, + } + } + + /// Remove the first `size` elements from the buffer. + fn roll(&mut self, size: usize) { + self.inner = self.inner[size..].to_vec(); + } + + /// Add data to the end of the buffer, deleting from the + /// front as necessary. + fn append(&mut self, mut other: Vec) { + // if new data exceeds max size, take the tail. + if other.len() > self.max_size { + self.inner = other[(other.len() - self.max_size)..].to_vec(); + return; + } + // check if appending the data overflows buffer + let free_space = self.max_size - self.inner.len(); + if other.len() > free_space { + // delete the minimum amount of data from the front of the buffer + // to fit new data. + self.roll(other.len() - free_space); + } + self.inner.append(&mut other); + } +} + +impl FixedBuffer { + /// Read the inner buffer out to string + pub fn read_string(&mut self) -> String { + let mut fresh = vec![]; + std::mem::swap(&mut fresh, &mut self.inner); + String::from_utf8(fresh).unwrap() + } +} + +#[cfg(test)] +mod testing { + use super::*; + + #[test] + fn test_buffer() { + let mut buffer = FixedBuffer::::new(10); + buffer.inner = (1u64..=9_u64).collect(); + buffer.append(vec![10, 11, 12, 13, 14, 15]); + assert_eq!(buffer.inner, (6u64..=15_u64).collect::>()); + buffer.append((20u64..=40_u64).collect()); + assert_eq!(buffer.inner, (31u64..=40_u64).collect::>()); + } +} diff --git a/shared/src/types/io.rs b/shared/src/types/io.rs index 80eeb2eb69..7e1bf40d38 100644 --- a/shared/src/types/io.rs +++ b/shared/src/types/io.rs @@ -117,3 +117,12 @@ macro_rules! edisplay_line { <$io>::eprintln(format_args!($($args)*).to_string()) }; } + +#[macro_export] +/// A convenience macro for formatting the user prompt before +/// forwarding it to the [`Io::prompt`] method. +macro_rules! prompt { + ($io:ty,$($arg:tt)*) => {{ + <$io>::prompt(format!("{}", format_args!($($arg)*))) + }} +} diff --git a/tests/src/integration.rs b/tests/src/integration.rs index 8642e0e03c..1a7c84dbfb 100644 --- a/tests/src/integration.rs +++ b/tests/src/integration.rs @@ -1,3 +1,2 @@ mod masp; mod setup; -mod utils; diff --git a/tests/src/integration/masp.rs b/tests/src/integration/masp.rs index b8f9c47ed5..841bdb01c8 100644 --- a/tests/src/integration/masp.rs +++ b/tests/src/integration/masp.rs @@ -5,7 +5,7 @@ use color_eyre::owo_colors::OwoColorize; use namada::types::io::DefaultIo; use namada_apps::client::tx::CLIShieldedUtils; use namada_apps::node::ledger::shell::testing::client::run; -use namada_apps::node::ledger::shell::testing::utils::Bin; +use namada_apps::node::ledger::shell::testing::utils::{Bin, CapturedOutput}; use namada_core::types::address::{btc, eth, masp_rewards}; use namada_core::types::token; use namada_core::types::token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; @@ -17,7 +17,6 @@ use crate::e2e::setup::constants::{ AC_PAYMENT_ADDRESS, AC_VIEWING_KEY, ALBERT, A_SPENDING_KEY, BB_PAYMENT_ADDRESS, BERTHA, BTC, B_SPENDING_KEY, CHRISTEL, ETH, MASP, NAM, }; -use crate::integration::utils::CapturedOutput; /// In this test we verify that users of the MASP receive the correct rewards /// for leaving their assets in the pool for varying periods of time. diff --git a/tests/src/integration/utils.rs b/tests/src/integration/utils.rs deleted file mode 100644 index f626a001ee..0000000000 --- a/tests/src/integration/utils.rs +++ /dev/null @@ -1,83 +0,0 @@ -use std::fs::File; -use std::path::PathBuf; -use std::sync::Arc; - -struct TempFile(PathBuf); -impl TempFile { - fn new(path: PathBuf) -> (Self, File) { - let f = File::create(&path).unwrap(); - (Self(path), f) - } -} - -impl Drop for TempFile { - fn drop(&mut self) { - _ = std::fs::remove_file(&self.0); - } -} - -/// Test helper that captures stdout of -/// a process. -pub struct CapturedOutput { - pub output: String, - pub result: T, - input: String, -} - -impl CapturedOutput { - pub fn with_input(input: String) -> Self { - Self { - output: "".to_string(), - result: (), - input, - } - } -} - -impl CapturedOutput { - /// Run a client command and capture - /// the output to the mocked stdout. - pub(crate) fn of(func: F) -> Self - where - F: FnOnce() -> T, - { - std::io::set_output_capture(Some(Default::default())); - let mut capture = Self { - output: Default::default(), - result: func(), - input: Default::default(), - }; - let captured = std::io::set_output_capture(None); - let captured = captured.unwrap(); - let captured = Arc::try_unwrap(captured).unwrap(); - let captured = captured.into_inner().unwrap(); - capture.output = String::from_utf8(captured).unwrap(); - capture - } - - /// Run a client command with input to the mocked stdin and capture - /// the output to the mocked stdout - pub fn run(&self, func: F) -> CapturedOutput - where - F: FnOnce() -> U, - { - { - // write the input to the mocked stdin - let mut buf = namada_apps::cli::TESTIN.lock().unwrap(); - buf.clear(); - buf.extend_from_slice(self.input.as_bytes()); - } - CapturedOutput::of(func) - } - - /// Check if the captured output contains the regex. - pub fn matches(&self, needle: regex::Regex) -> bool { - needle.captures(&self.output).is_some() - } - - /// Check if the captured output contains the string. - pub fn contains(&self, needle: &str) -> bool { - let needle = regex::Regex::new(needle).unwrap(); - self.matches(needle) - } -} diff --git a/tests/src/lib.rs b/tests/src/lib.rs index 7e35947477..1d9958a30a 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -1,4 +1,3 @@ -#![cfg_attr(test, feature(internal_output_capture))] //! Namada integrations and WASM tests and testing helpers. #![doc(html_favicon_url = "https://dev.namada.net/master/favicon.png")] @@ -13,7 +12,6 @@ pub use vm_host_env::{ibc, tx, vp}; #[cfg(test)] mod e2e; #[cfg(test)] -#[allow(dead_code)] mod integration; pub mod native_vp; pub mod storage; diff --git a/wasm/checksums.json b/wasm/checksums.json index 71bcf7c9ef..adf06b5afe 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,21 +1,21 @@ { - "tx_bond.wasm": "tx_bond.e9201ea2342459a86343e5e6db1400b23d0843fe4407f6f759c65dc63395b5cd.wasm", - "tx_bridge_pool.wasm": "tx_bridge_pool.c1b7457cc85bfb4ca141a3f1157700be2d0f672aec21a2a7883a6264e8775cc1.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.c91a746933ecb67daf41d77d6cb7810277dfb661a72e6f4726cee7300f750cde.wasm", - "tx_ibc.wasm": "tx_ibc.917a7c8ad4138679eb467e8a18b0234d6c3ba56802a728186d7fcd047e0c0c4b.wasm", - "tx_init_account.wasm": "tx_init_account.6fc0c6ebbf933befd27f8515f0d439814ad8ea2732ec62271fe5ec4da177340e.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.0dd098e0962ba8b3fac93eefae0524959125ce334edf05e05edbdf4d38ec9069.wasm", - "tx_init_validator.wasm": "tx_init_validator.88f4d2b5cc358e8e9a53512314450161b8ab123a54ccbbbc44c6272fc3496ee2.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.4d4243a995611cb5c53bb7ad9657dd6112f8f3e309b1497c4af2763dcff5f0e7.wasm", - "tx_transfer.wasm": "tx_transfer.c6d4ac6e8311a75f199a0ca009f2b80c1ef107eeadfe7bbab94ff6453f075954.wasm", - "tx_unbond.wasm": "tx_unbond.00df2be4c4eaa94bec27f4bb89ccb96f7725cfdc4af2c965473ec1e9679f0989.wasm", - "tx_unjail_validator.wasm": "tx_unjail_validator.96081388ae0cb289df7fb48b37a5f640c867205f19c9c44a2af4f2f9598c4a27.wasm", - "tx_update_vp.wasm": "tx_update_vp.a1b0a203c05769a90c63711ef3ff922c5ba4b82f74511ade2a446ead47a2913b.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.9b4103f524a21e45d39361afb349e60a5010d94db5bf5ed5a8f678ec9c0df1b8.wasm", - "tx_withdraw.wasm": "tx_withdraw.37ffd46d73cc8cb67c14df42389b18fff03cd0c77861e2d9fc64ac486a13f65c.wasm", - "vp_implicit.wasm": "vp_implicit.e16621fd592ef6ad906b820762959dec1224e5a2e6917e8d8f0258601ed8dadd.wasm", - "vp_masp.wasm": "vp_masp.6aee2c99eba0b680198c985aabb1c6be877538ae338e36e9164c60685eec9334.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.1a78846b5aca8ce6a34c258dec573fec7d247ae07c09dd7b92196b2bd0a8d1dd.wasm", - "vp_user.wasm": "vp_user.e0200e6274839583464001363bfce2137c8a0a9191b242a9c34c32e31b525a04.wasm", - "vp_validator.wasm": "vp_validator.3a23a0a5629de85f2029543d3fc3d9d3fd58473280859b4984a9a25723c1ca88.wasm" + "tx_bond.wasm": "tx_bond.3d9341191f3059b478435c52ec0db4b19fe9d2b7a60ac7371287fb15552dbe3b.wasm", + "tx_bridge_pool.wasm": "tx_bridge_pool.0d8d4de6222aeb5b0cb80fa39b1ce1ee9e2f50115ffdaa2a1e79eabdddeb6bfe.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.349170accac147e8b4b77eb10ded0421936176b09d8913246235db0c5a4fac83.wasm", + "tx_ibc.wasm": "tx_ibc.5ddb7f3032d8b59446486a7fb20bac8c43c5a002f6e9b3309ff2e01048b98c7f.wasm", + "tx_init_account.wasm": "tx_init_account.f254fa819c85694d01f440132690ac54b3a8911a5eca9ccb49bc6171cf219017.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.1cf4ac19672bc368ff1e8ea0fd31507ac4a9a33c1b21ff80d85de8b36c6bce66.wasm", + "tx_init_validator.wasm": "tx_init_validator.bd5aa6513bf890e4591413fe6a4345dc28a0767ebc2fad2279e851fb1373091a.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.8f252e6fd0ab74be7f7afb4dbe4ba2b3d441011fd57ce5ccd06fe24109728019.wasm", + "tx_transfer.wasm": "tx_transfer.cdb2edbaa152608e2ae6d96a586955b911b74cc097228fee2bb5768f5f6db0ea.wasm", + "tx_unbond.wasm": "tx_unbond.0e6319063d74311a42a405435c15aa867894fc0a7ae209c08fc5fa9eee4013c5.wasm", + "tx_unjail_validator.wasm": "tx_unjail_validator.24e345f69f62fc8b75c475eee66e3252dc06a5468411f6770096b1b8ced8f022.wasm", + "tx_update_vp.wasm": "tx_update_vp.86123368b829488aface2804afc397a8c26873e9f319e3183fb211b292239e1d.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.80586902bae368088edfdef22a6fcc25aee8779620fd48eabbe1f3f11754d701.wasm", + "tx_withdraw.wasm": "tx_withdraw.5648dac90b0f25c94d60fb39dfed6120f6eed8794e4c3dbf554af36adf7bafc3.wasm", + "vp_implicit.wasm": "vp_implicit.18f2b20fa776493a0182b05e95b3eb0676be702dc0a4d39a09cb9ab51d116146.wasm", + "vp_masp.wasm": "vp_masp.b07f344c2d760ee69dfcef1ae4e703f63c7c6594a377a48550ef576f9fcaca96.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.9e3c9d133e52d4b762b9fc7d3bcc1e7d0ad2bc4d1754d390ed31555471f184d5.wasm", + "vp_user.wasm": "vp_user.df279909cb2de9d5125bbfe8b34b14ff3b924ed7db61dcc93cbb06f2f3f29509.wasm", + "vp_validator.wasm": "vp_validator.e3ed90196c676b73bcfa2f4142e2831b306eef56bde73243b2d50ddc5ab8e2b2.wasm" } \ No newline at end of file From 6aa9ee50d0577494fecc90451ce98b567b65d918 Mon Sep 17 00:00:00 2001 From: satan Date: Mon, 24 Jul 2023 23:50:54 +0200 Subject: [PATCH 05/28] [chore]: rebasing on bat/generic-io --- apps/src/lib/client/rpc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 00435eee31..6862ee2a39 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -48,7 +48,7 @@ use namada::types::masp::{BalanceOwner, ExtendedViewingKey, PaymentAddress}; use namada::types::storage::{BlockHeight, BlockResults, Epoch, Key, KeySeg}; use namada::types::token::{Change, MaspDenom}; use namada::types::{storage, token}; -use namada::{display, display_line, edisplay_lin, prompt}; +use namada::{display, display_line, edisplay_line, prompt}; use tokio::time::Instant; use crate::cli::{self, args}; From c833aa3bf18719ac8f04d1c70fd8bbd5989308b7 Mon Sep 17 00:00:00 2001 From: satan Date: Wed, 26 Jul 2023 13:41:01 +0200 Subject: [PATCH 06/28] [fix]: Fixing downstream async io changes from bat/generic-io --- apps/src/lib/client/rpc.rs | 3 ++- apps/src/lib/node/ledger/shell/testing/utils.rs | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 6862ee2a39..1b332964f3 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -455,7 +455,8 @@ pub async fn query_pinned_balance< } // If a suitable viewing key was not found, then demand it from the user if balance == pinned_error { - let vk_str = prompt!(IO, "Enter the viewing key for {}: ", owner); + let vk_str = + prompt!(IO, "Enter the viewing key for {}: ", owner).await; let fvk = match ExtendedViewingKey::from_str(vk_str.trim()) { Ok(fvk) => fvk, _ => { diff --git a/apps/src/lib/node/ledger/shell/testing/utils.rs b/apps/src/lib/node/ledger/shell/testing/utils.rs index 95da7efb85..3ff3bdb7d3 100644 --- a/apps/src/lib/node/ledger/shell/testing/utils.rs +++ b/apps/src/lib/node/ledger/shell/testing/utils.rs @@ -68,6 +68,7 @@ lazy_static! { pub struct TestingIo; +#[async_trait::async_trait(?Send)] impl Io for TestingIo { fn print(output: impl AsRef) { let mut testout = TESTOUT.lock().unwrap(); @@ -107,11 +108,11 @@ impl Io for TestingIo { eprintln!("{}", output.as_ref()); } - fn read() -> std::io::Result { + async fn read() -> std::io::Result { read_aux(TESTIN.lock().unwrap().as_slice()) } - fn prompt(question: impl AsRef) -> String { + async fn prompt(question: impl AsRef) -> String { prompt_aux( TESTIN.lock().unwrap().as_slice(), std::io::stdout(), From 12f219f14f9e45b3cff6f43b2589bd9a33ad2895 Mon Sep 17 00:00:00 2001 From: satan Date: Mon, 24 Jul 2023 17:00:05 +0200 Subject: [PATCH 07/28] [feat]: Refactored the masp integration tests to no longer need the nightly feature --- Cargo.lock | 10 +- apps/src/lib/cli.rs | 2 +- apps/src/lib/cli/utils.rs | 51 ----- apps/src/lib/client/rpc.rs | 5 +- .../lib/node/ledger/shell/testing/client.rs | 12 +- .../lib/node/ledger/shell/testing/utils.rs | 197 ++++++++++++++++++ shared/src/types/io.rs | 9 + tests/src/integration.rs | 1 - tests/src/integration/masp.rs | 3 +- tests/src/integration/utils.rs | 83 -------- tests/src/lib.rs | 2 - wasm/checksums.json | 38 ++-- 12 files changed, 240 insertions(+), 173 deletions(-) delete mode 100644 tests/src/integration/utils.rs diff --git a/Cargo.lock b/Cargo.lock index decc428928..5821c9162e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3608,7 +3608,7 @@ checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", - "rustix 0.37.13", + "rustix 0.37.1", "windows-sys 0.48.0", ] @@ -6022,16 +6022,16 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.13" +version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79bef90eb6d984c72722595b5b1348ab39275a5e5123faca6863bf07d75a4e0" +checksum = "d4790277f605573dd24b6751701e0823582a63c7cafc095e427e6c66e45dd75e" dependencies = [ "bitflags 1.2.1", "errno", "io-lifetimes", "libc", "linux-raw-sys 0.3.7", - "windows-sys 0.48.0", + "windows-sys 0.45.0", ] [[package]] @@ -6887,7 +6887,7 @@ dependencies = [ "cfg-if 1.0.0", "fastrand", "redox_syscall 0.3.5", - "rustix 0.37.13", + "rustix 0.37.1", "windows-sys 0.45.0", ] diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 12927cca13..3fa2cdd8ed 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -16,7 +16,7 @@ pub mod wallet; use clap::{ArgGroup, ArgMatches, ColorChoice}; use color_eyre::eyre::Result; use utils::*; -pub use utils::{dispatch_prompt, safe_exit, Cmd, TESTIN}; +pub use utils::{safe_exit, Cmd}; pub use self::context::Context; use crate::cli::api::CliIo; diff --git a/apps/src/lib/cli/utils.rs b/apps/src/lib/cli/utils.rs index c35298976c..73d144596c 100644 --- a/apps/src/lib/cli/utils.rs +++ b/apps/src/lib/cli/utils.rs @@ -6,7 +6,6 @@ use std::str::FromStr; use clap::{ArgAction, ArgMatches}; use color_eyre::eyre::Result; -use lazy_static::lazy_static; use super::args; use super::context::{Context, FromContext}; @@ -342,53 +341,3 @@ pub fn safe_exit(_: i32) -> ! { panic!("Test failed because the client exited unexpectedly.") } - -lazy_static! { - /// A replacement for stdin in testing. - pub static ref TESTIN: std::sync::Arc>> = - std::sync::Arc::new(std::sync::Mutex::new(vec![])); -} - -/// A generic function for displaying a prompt to users and reading -/// in their response. -fn prompt_aux(mut reader: R, mut writer: W, question: &str) -> String -where - R: std::io::Read, - W: Write, -{ - write!(&mut writer, "{}", question).expect("Unable to write"); - writer.flush().unwrap(); - let mut s = String::new(); - reader.read_to_string(&mut s).expect("Unable to read"); - s -} - -/// A function that chooses how to dispatch prompts -/// to users. There is a hierarchy of feature flags -/// that determines this. If no flags are set, -/// the question is printed to stdout and response -/// read from stdin. -pub fn dispatch_prompt(question: impl AsRef) -> String { - if cfg!(feature = "testing") { - prompt_aux( - TESTIN.lock().unwrap().as_slice(), - std::io::stdout(), - question.as_ref(), - ) - } else { - prompt_aux( - std::io::stdin().lock(), - std::io::stdout(), - question.as_ref(), - ) - } -} - -#[macro_export] -/// A convenience macro for formatting the user prompt before -/// forwarding it to the `[dispatch_prompt]` method. -macro_rules! prompt { - ($($arg:tt)*) => {{ - $crate::cli::dispatch_prompt(format!("{}", format_args!($($arg)*))) - }} -} diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index efdc8ee8c3..ccac9cc398 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -48,13 +48,12 @@ use namada::types::masp::{BalanceOwner, ExtendedViewingKey, PaymentAddress}; use namada::types::storage::{BlockHeight, BlockResults, Epoch, Key, KeySeg}; use namada::types::token::{Change, MaspDenom}; use namada::types::{storage, token}; -use namada::{display, display_line, edisplay_line}; +use namada::{display, display_line, edisplay_lin, prompt}; use tokio::time::Instant; use crate::cli::{self, args}; use crate::facade::tendermint::merkle::proof::Proof; use crate::facade::tendermint_rpc::error::Error as TError; -use crate::prompt; use crate::wallet::CliWalletUtils; /// Query the status of a given transaction. @@ -455,7 +454,7 @@ pub async fn query_pinned_balance< } // If a suitable viewing key was not found, then demand it from the user if balance == pinned_error { - let vk_str = prompt!("Enter the viewing key for {}: ", owner); + let vk_str = prompt!(IO, "Enter the viewing key for {}: ", owner); let fvk = match ExtendedViewingKey::from_str(vk_str.trim()) { Ok(fvk) => fvk, _ => { diff --git a/apps/src/lib/node/ledger/shell/testing/client.rs b/apps/src/lib/node/ledger/shell/testing/client.rs index 0f27a2f6f1..1aa35ca321 100644 --- a/apps/src/lib/node/ledger/shell/testing/client.rs +++ b/apps/src/lib/node/ledger/shell/testing/client.rs @@ -3,14 +3,14 @@ use std::ops::ControlFlow; use clap::Command as App; use eyre::Report; use namada::types::control_flow::Halt; -use namada::types::io::{DefaultIo, Io}; +use namada::types::io::Io; use tendermint_config::net::Address as TendermintAddress; use super::node::MockNode; use crate::cli::api::{CliApi, CliClient}; use crate::cli::args::Global; use crate::cli::{args, cmds, Cmd, Context, NamadaClient}; -use crate::node::ledger::shell::testing::utils::Bin; +use crate::node::ledger::shell::testing::utils::{Bin, TestingIo}; pub fn run( node: &MockNode, @@ -25,7 +25,7 @@ pub fn run( wasm_dir: Some(locked.wasm_dir.clone()), } }; - let ctx = Context::new::(global.clone())?; + let ctx = Context::new::(global.clone())?; let rt = tokio::runtime::Runtime::new().unwrap(); match who { @@ -47,7 +47,7 @@ pub fn run( NamadaClient::WithoutContext(sub_cmd, global) } }; - rt.block_on(CliApi::::handle_client_command( + rt.block_on(CliApi::::handle_client_command( Some(node), cmd, )) @@ -60,7 +60,7 @@ pub fn run( let cmd = cmds::NamadaWallet::parse(&matches) .expect("Could not parse wallet command"); - CliApi::::handle_wallet_command(cmd, ctx) + CliApi::::handle_wallet_command(cmd, ctx) } Bin::Relayer => { args.insert(0, "relayer"); @@ -69,7 +69,7 @@ pub fn run( let matches = app.get_matches_from(args.clone()); let cmd = cmds::NamadaRelayer::parse(&matches) .expect("Could not parse wallet command"); - rt.block_on(CliApi::::handle_relayer_command( + rt.block_on(CliApi::::handle_relayer_command( Some(node), cmd, )) diff --git a/apps/src/lib/node/ledger/shell/testing/utils.rs b/apps/src/lib/node/ledger/shell/testing/utils.rs index e66ead21e7..95da7efb85 100644 --- a/apps/src/lib/node/ledger/shell/testing/utils.rs +++ b/apps/src/lib/node/ledger/shell/testing/utils.rs @@ -1,5 +1,7 @@ use std::path::{Path, PathBuf}; +use lazy_static::lazy_static; +use namada::types::io::{prompt_aux, read_aux, Io}; use tempfile::tempdir; /// Namada binaries @@ -46,3 +48,198 @@ impl Default for TestDir { Self::new() } } + +/// The max number of bytes that the is currently remembered from stdout while +/// testing. +const TESTOUT_BUF_SIZE: usize = 100_000; + +lazy_static! { + /// A replacement for stdout in testing. The maximum number of bytes + /// it holds is limited to prevent memory issues. + pub static ref TESTOUT: std::sync::Arc>> = + std::sync::Arc::new(std::sync::Mutex::new(FixedBuffer::new(TESTOUT_BUF_SIZE))); +} + +lazy_static! { + /// A replacement for stdin in testing. + pub static ref TESTIN: std::sync::Arc>> = + std::sync::Arc::new(std::sync::Mutex::new(vec![])); +} + +pub struct TestingIo; + +impl Io for TestingIo { + fn print(output: impl AsRef) { + let mut testout = TESTOUT.lock().unwrap(); + testout.append(output.as_ref().as_bytes().to_vec()); + print!("{}", output.as_ref()); + } + + fn println(output: impl AsRef) { + let mut testout = TESTOUT.lock().unwrap(); + let mut bytes = output.as_ref().as_bytes().to_vec(); + bytes.extend_from_slice("\n".as_bytes()); + testout.append(bytes); + println!("{}", output.as_ref()); + } + + fn write( + _: W, + output: impl AsRef, + ) -> std::io::Result<()> { + Self::print(output); + Ok(()) + } + + fn writeln( + _: W, + output: impl AsRef, + ) -> std::io::Result<()> { + Self::println(output); + Ok(()) + } + + fn eprintln(output: impl AsRef) { + let mut testout = TESTOUT.lock().unwrap(); + let mut bytes = output.as_ref().as_bytes().to_vec(); + bytes.extend_from_slice("\n".as_bytes()); + testout.append(bytes); + eprintln!("{}", output.as_ref()); + } + + fn read() -> std::io::Result { + read_aux(TESTIN.lock().unwrap().as_slice()) + } + + fn prompt(question: impl AsRef) -> String { + prompt_aux( + TESTIN.lock().unwrap().as_slice(), + std::io::stdout(), + question.as_ref(), + ) + } +} + +/// Test helper that captures stdout of +/// a process. +pub struct CapturedOutput { + pub output: String, + pub result: T, + input: String, +} + +impl CapturedOutput { + pub fn with_input(input: String) -> Self { + Self { + output: "".to_string(), + result: (), + input, + } + } +} + +impl CapturedOutput { + /// Run a client command and capture + /// the output to the mocked stdout. + pub fn of(func: F) -> Self + where + F: FnOnce() -> T, + { + let mut capture = Self { + output: Default::default(), + result: func(), + input: Default::default(), + }; + capture.output = TESTOUT.lock().unwrap().read_string(); + capture + } + + /// Run a client command with input to the mocked stdin and capture + /// the output to the mocked stdout + pub fn run(&self, func: F) -> CapturedOutput + where + F: FnOnce() -> U, + { + { + // write the input to the mocked stdin + let mut buf = TESTIN.lock().unwrap(); + buf.clear(); + buf.extend_from_slice(self.input.as_bytes()); + } + CapturedOutput::of(func) + } + + /// Check if the captured output contains the regex. + pub fn matches(&self, needle: regex::Regex) -> bool { + needle.captures(&self.output).is_some() + } + + /// Check if the captured output contains the string. + pub fn contains(&self, needle: &str) -> bool { + let needle = regex::Regex::new(needle).unwrap(); + self.matches(needle) + } +} + +/// A buffer with a max size. Drops elements from the front on +/// size overflow. +pub struct FixedBuffer { + inner: Vec, + max_size: usize, +} + +impl FixedBuffer { + fn new(max_size: usize) -> Self { + Self { + inner: vec![], + max_size, + } + } + + /// Remove the first `size` elements from the buffer. + fn roll(&mut self, size: usize) { + self.inner = self.inner[size..].to_vec(); + } + + /// Add data to the end of the buffer, deleting from the + /// front as necessary. + fn append(&mut self, mut other: Vec) { + // if new data exceeds max size, take the tail. + if other.len() > self.max_size { + self.inner = other[(other.len() - self.max_size)..].to_vec(); + return; + } + // check if appending the data overflows buffer + let free_space = self.max_size - self.inner.len(); + if other.len() > free_space { + // delete the minimum amount of data from the front of the buffer + // to fit new data. + self.roll(other.len() - free_space); + } + self.inner.append(&mut other); + } +} + +impl FixedBuffer { + /// Read the inner buffer out to string + pub fn read_string(&mut self) -> String { + let mut fresh = vec![]; + std::mem::swap(&mut fresh, &mut self.inner); + String::from_utf8(fresh).unwrap() + } +} + +#[cfg(test)] +mod testing { + use super::*; + + #[test] + fn test_buffer() { + let mut buffer = FixedBuffer::::new(10); + buffer.inner = (1u64..=9_u64).collect(); + buffer.append(vec![10, 11, 12, 13, 14, 15]); + assert_eq!(buffer.inner, (6u64..=15_u64).collect::>()); + buffer.append((20u64..=40_u64).collect()); + assert_eq!(buffer.inner, (31u64..=40_u64).collect::>()); + } +} diff --git a/shared/src/types/io.rs b/shared/src/types/io.rs index 80eeb2eb69..7e1bf40d38 100644 --- a/shared/src/types/io.rs +++ b/shared/src/types/io.rs @@ -117,3 +117,12 @@ macro_rules! edisplay_line { <$io>::eprintln(format_args!($($args)*).to_string()) }; } + +#[macro_export] +/// A convenience macro for formatting the user prompt before +/// forwarding it to the [`Io::prompt`] method. +macro_rules! prompt { + ($io:ty,$($arg:tt)*) => {{ + <$io>::prompt(format!("{}", format_args!($($arg)*))) + }} +} diff --git a/tests/src/integration.rs b/tests/src/integration.rs index 8642e0e03c..1a7c84dbfb 100644 --- a/tests/src/integration.rs +++ b/tests/src/integration.rs @@ -1,3 +1,2 @@ mod masp; mod setup; -mod utils; diff --git a/tests/src/integration/masp.rs b/tests/src/integration/masp.rs index b8f9c47ed5..841bdb01c8 100644 --- a/tests/src/integration/masp.rs +++ b/tests/src/integration/masp.rs @@ -5,7 +5,7 @@ use color_eyre::owo_colors::OwoColorize; use namada::types::io::DefaultIo; use namada_apps::client::tx::CLIShieldedUtils; use namada_apps::node::ledger::shell::testing::client::run; -use namada_apps::node::ledger::shell::testing::utils::Bin; +use namada_apps::node::ledger::shell::testing::utils::{Bin, CapturedOutput}; use namada_core::types::address::{btc, eth, masp_rewards}; use namada_core::types::token; use namada_core::types::token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; @@ -17,7 +17,6 @@ use crate::e2e::setup::constants::{ AC_PAYMENT_ADDRESS, AC_VIEWING_KEY, ALBERT, A_SPENDING_KEY, BB_PAYMENT_ADDRESS, BERTHA, BTC, B_SPENDING_KEY, CHRISTEL, ETH, MASP, NAM, }; -use crate::integration::utils::CapturedOutput; /// In this test we verify that users of the MASP receive the correct rewards /// for leaving their assets in the pool for varying periods of time. diff --git a/tests/src/integration/utils.rs b/tests/src/integration/utils.rs deleted file mode 100644 index f626a001ee..0000000000 --- a/tests/src/integration/utils.rs +++ /dev/null @@ -1,83 +0,0 @@ -use std::fs::File; -use std::path::PathBuf; -use std::sync::Arc; - -struct TempFile(PathBuf); -impl TempFile { - fn new(path: PathBuf) -> (Self, File) { - let f = File::create(&path).unwrap(); - (Self(path), f) - } -} - -impl Drop for TempFile { - fn drop(&mut self) { - _ = std::fs::remove_file(&self.0); - } -} - -/// Test helper that captures stdout of -/// a process. -pub struct CapturedOutput { - pub output: String, - pub result: T, - input: String, -} - -impl CapturedOutput { - pub fn with_input(input: String) -> Self { - Self { - output: "".to_string(), - result: (), - input, - } - } -} - -impl CapturedOutput { - /// Run a client command and capture - /// the output to the mocked stdout. - pub(crate) fn of(func: F) -> Self - where - F: FnOnce() -> T, - { - std::io::set_output_capture(Some(Default::default())); - let mut capture = Self { - output: Default::default(), - result: func(), - input: Default::default(), - }; - let captured = std::io::set_output_capture(None); - let captured = captured.unwrap(); - let captured = Arc::try_unwrap(captured).unwrap(); - let captured = captured.into_inner().unwrap(); - capture.output = String::from_utf8(captured).unwrap(); - capture - } - - /// Run a client command with input to the mocked stdin and capture - /// the output to the mocked stdout - pub fn run(&self, func: F) -> CapturedOutput - where - F: FnOnce() -> U, - { - { - // write the input to the mocked stdin - let mut buf = namada_apps::cli::TESTIN.lock().unwrap(); - buf.clear(); - buf.extend_from_slice(self.input.as_bytes()); - } - CapturedOutput::of(func) - } - - /// Check if the captured output contains the regex. - pub fn matches(&self, needle: regex::Regex) -> bool { - needle.captures(&self.output).is_some() - } - - /// Check if the captured output contains the string. - pub fn contains(&self, needle: &str) -> bool { - let needle = regex::Regex::new(needle).unwrap(); - self.matches(needle) - } -} diff --git a/tests/src/lib.rs b/tests/src/lib.rs index 7e35947477..1d9958a30a 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -1,4 +1,3 @@ -#![cfg_attr(test, feature(internal_output_capture))] //! Namada integrations and WASM tests and testing helpers. #![doc(html_favicon_url = "https://dev.namada.net/master/favicon.png")] @@ -13,7 +12,6 @@ pub use vm_host_env::{ibc, tx, vp}; #[cfg(test)] mod e2e; #[cfg(test)] -#[allow(dead_code)] mod integration; pub mod native_vp; pub mod storage; diff --git a/wasm/checksums.json b/wasm/checksums.json index cb9ca8ed52..adf06b5afe 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,21 +1,21 @@ { - "tx_bond.wasm": "tx_bond.08cedac1d825cd8ee9b04ef059bc68fee04f82fec57e0f6bc38123de8fb35b85.wasm", - "tx_bridge_pool.wasm": "tx_bridge_pool.c9ef2bdb19910e12eadabc7f0552b45c937bcf1f47ca09759e52112b68d94114.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.2be60082c6b891cb437b4392e1b2570f5d92fed35dd624cd079ea48c4bc7902c.wasm", - "tx_ibc.wasm": "tx_ibc.f8056f465d17043d2864309d1c50a897a028b6ebf9702e5c403e1bf8c670f6e6.wasm", - "tx_init_account.wasm": "tx_init_account.e91ab25cebd15af9a958eca4a34ba287804b1ff6ba4c82fc941f7f50816e9c4d.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.1683eb9a2b2616af21f4f55457f7e06e0ae0591f460ede412a548ea9b1ab694c.wasm", - "tx_init_validator.wasm": "tx_init_validator.67574f70c1f76c2c68498d17323793366c13d119480b6787c8c5ede312e51da0.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.e6375abba3f750700c06fbdeb2d5edec22b6a382116a67a7f9d7f7ba49f134e1.wasm", - "tx_transfer.wasm": "tx_transfer.e65b47bc94c5a09e3edc68298ad0e5e35041b91bc4d679bf5b7f433dffdce58e.wasm", - "tx_unbond.wasm": "tx_unbond.4fd70d297ccedb369bf88d8a8459a47ea733d329410387a6f80a0e026c6e480d.wasm", - "tx_unjail_validator.wasm": "tx_unjail_validator.b8e7204b14e15a3c395432f35788eca45ef4332916d96dfba87627a5717916de.wasm", - "tx_update_vp.wasm": "tx_update_vp.65c5ca3e48fdef70e696460eca7540cf6196511d76fb2465133b145409329b3e.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.e0a003d922230d32b741b57ca18913cbc4d5d2290f02cb37dfdaa7f27cebb486.wasm", - "tx_withdraw.wasm": "tx_withdraw.40499cb0e268d3cc3d9bca5dacca05d8df35f5b90adf4087a5171505472d921a.wasm", - "vp_implicit.wasm": "vp_implicit.57af3b7d2ee9e2c9d7ef1e33a85646abeea7ea02dad19b7e0b20b289de5a7ae9.wasm", - "vp_masp.wasm": "vp_masp.4d656f775b7462095e7d9ff533e346829fad02568b1cf92fa2f99cc0677720ce.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.b0c855f0356a468db1d26baebc988508d4824836b86adb94ac432368feb03ac4.wasm", - "vp_user.wasm": "vp_user.31967a7411caf61729e46840eec90d2db386cf4745f49d59086a43cc3c3e2d39.wasm", - "vp_validator.wasm": "vp_validator.0bca8e34f5d51c74b2861d588e3dd6a8e61de7c63d356af63e2182e030ac42ee.wasm" + "tx_bond.wasm": "tx_bond.3d9341191f3059b478435c52ec0db4b19fe9d2b7a60ac7371287fb15552dbe3b.wasm", + "tx_bridge_pool.wasm": "tx_bridge_pool.0d8d4de6222aeb5b0cb80fa39b1ce1ee9e2f50115ffdaa2a1e79eabdddeb6bfe.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.349170accac147e8b4b77eb10ded0421936176b09d8913246235db0c5a4fac83.wasm", + "tx_ibc.wasm": "tx_ibc.5ddb7f3032d8b59446486a7fb20bac8c43c5a002f6e9b3309ff2e01048b98c7f.wasm", + "tx_init_account.wasm": "tx_init_account.f254fa819c85694d01f440132690ac54b3a8911a5eca9ccb49bc6171cf219017.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.1cf4ac19672bc368ff1e8ea0fd31507ac4a9a33c1b21ff80d85de8b36c6bce66.wasm", + "tx_init_validator.wasm": "tx_init_validator.bd5aa6513bf890e4591413fe6a4345dc28a0767ebc2fad2279e851fb1373091a.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.8f252e6fd0ab74be7f7afb4dbe4ba2b3d441011fd57ce5ccd06fe24109728019.wasm", + "tx_transfer.wasm": "tx_transfer.cdb2edbaa152608e2ae6d96a586955b911b74cc097228fee2bb5768f5f6db0ea.wasm", + "tx_unbond.wasm": "tx_unbond.0e6319063d74311a42a405435c15aa867894fc0a7ae209c08fc5fa9eee4013c5.wasm", + "tx_unjail_validator.wasm": "tx_unjail_validator.24e345f69f62fc8b75c475eee66e3252dc06a5468411f6770096b1b8ced8f022.wasm", + "tx_update_vp.wasm": "tx_update_vp.86123368b829488aface2804afc397a8c26873e9f319e3183fb211b292239e1d.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.80586902bae368088edfdef22a6fcc25aee8779620fd48eabbe1f3f11754d701.wasm", + "tx_withdraw.wasm": "tx_withdraw.5648dac90b0f25c94d60fb39dfed6120f6eed8794e4c3dbf554af36adf7bafc3.wasm", + "vp_implicit.wasm": "vp_implicit.18f2b20fa776493a0182b05e95b3eb0676be702dc0a4d39a09cb9ab51d116146.wasm", + "vp_masp.wasm": "vp_masp.b07f344c2d760ee69dfcef1ae4e703f63c7c6594a377a48550ef576f9fcaca96.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.9e3c9d133e52d4b762b9fc7d3bcc1e7d0ad2bc4d1754d390ed31555471f184d5.wasm", + "vp_user.wasm": "vp_user.df279909cb2de9d5125bbfe8b34b14ff3b924ed7db61dcc93cbb06f2f3f29509.wasm", + "vp_validator.wasm": "vp_validator.e3ed90196c676b73bcfa2f4142e2831b306eef56bde73243b2d50ddc5ab8e2b2.wasm" } \ No newline at end of file From 014e4f76b788bc8cfd8298d8ba4c98cd4144fdbc Mon Sep 17 00:00:00 2001 From: satan Date: Mon, 24 Jul 2023 23:50:54 +0200 Subject: [PATCH 08/28] [chore]: rebasing on bat/generic-io --- apps/src/lib/client/rpc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index ccac9cc398..26b1956a11 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -48,7 +48,7 @@ use namada::types::masp::{BalanceOwner, ExtendedViewingKey, PaymentAddress}; use namada::types::storage::{BlockHeight, BlockResults, Epoch, Key, KeySeg}; use namada::types::token::{Change, MaspDenom}; use namada::types::{storage, token}; -use namada::{display, display_line, edisplay_lin, prompt}; +use namada::{display, display_line, edisplay_line, prompt}; use tokio::time::Instant; use crate::cli::{self, args}; From c64fdee5197a0001d2db6c63b02731d386a5b5cf Mon Sep 17 00:00:00 2001 From: satan Date: Wed, 26 Jul 2023 13:41:01 +0200 Subject: [PATCH 09/28] [fix]: Fixing downstream async io changes from bat/generic-io --- apps/src/lib/client/rpc.rs | 3 ++- apps/src/lib/node/ledger/shell/testing/utils.rs | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 26b1956a11..20ba1509bc 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -454,7 +454,8 @@ pub async fn query_pinned_balance< } // If a suitable viewing key was not found, then demand it from the user if balance == pinned_error { - let vk_str = prompt!(IO, "Enter the viewing key for {}: ", owner); + let vk_str = + prompt!(IO, "Enter the viewing key for {}: ", owner).await; let fvk = match ExtendedViewingKey::from_str(vk_str.trim()) { Ok(fvk) => fvk, _ => { diff --git a/apps/src/lib/node/ledger/shell/testing/utils.rs b/apps/src/lib/node/ledger/shell/testing/utils.rs index 95da7efb85..3ff3bdb7d3 100644 --- a/apps/src/lib/node/ledger/shell/testing/utils.rs +++ b/apps/src/lib/node/ledger/shell/testing/utils.rs @@ -68,6 +68,7 @@ lazy_static! { pub struct TestingIo; +#[async_trait::async_trait(?Send)] impl Io for TestingIo { fn print(output: impl AsRef) { let mut testout = TESTOUT.lock().unwrap(); @@ -107,11 +108,11 @@ impl Io for TestingIo { eprintln!("{}", output.as_ref()); } - fn read() -> std::io::Result { + async fn read() -> std::io::Result { read_aux(TESTIN.lock().unwrap().as_slice()) } - fn prompt(question: impl AsRef) -> String { + async fn prompt(question: impl AsRef) -> String { prompt_aux( TESTIN.lock().unwrap().as_slice(), std::io::stdout(), From c249df8d53bce10db1250d2b2b243b2cc532e4e4 Mon Sep 17 00:00:00 2001 From: satan Date: Mon, 24 Jul 2023 17:00:05 +0200 Subject: [PATCH 10/28] [feat]: Refactored the masp integration tests to no longer need the nightly feature --- Cargo.lock | 10 +- apps/src/lib/cli.rs | 2 +- apps/src/lib/cli/utils.rs | 51 ----- apps/src/lib/client/rpc.rs | 5 +- .../lib/node/ledger/shell/testing/client.rs | 12 +- .../lib/node/ledger/shell/testing/utils.rs | 197 ++++++++++++++++++ shared/src/types/io.rs | 9 + tests/src/integration.rs | 1 - tests/src/integration/masp.rs | 3 +- tests/src/integration/utils.rs | 83 -------- tests/src/lib.rs | 2 - wasm/checksums.json | 38 ++-- 12 files changed, 240 insertions(+), 173 deletions(-) delete mode 100644 tests/src/integration/utils.rs diff --git a/Cargo.lock b/Cargo.lock index decc428928..5821c9162e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3608,7 +3608,7 @@ checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", - "rustix 0.37.13", + "rustix 0.37.1", "windows-sys 0.48.0", ] @@ -6022,16 +6022,16 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.13" +version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79bef90eb6d984c72722595b5b1348ab39275a5e5123faca6863bf07d75a4e0" +checksum = "d4790277f605573dd24b6751701e0823582a63c7cafc095e427e6c66e45dd75e" dependencies = [ "bitflags 1.2.1", "errno", "io-lifetimes", "libc", "linux-raw-sys 0.3.7", - "windows-sys 0.48.0", + "windows-sys 0.45.0", ] [[package]] @@ -6887,7 +6887,7 @@ dependencies = [ "cfg-if 1.0.0", "fastrand", "redox_syscall 0.3.5", - "rustix 0.37.13", + "rustix 0.37.1", "windows-sys 0.45.0", ] diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 12927cca13..3fa2cdd8ed 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -16,7 +16,7 @@ pub mod wallet; use clap::{ArgGroup, ArgMatches, ColorChoice}; use color_eyre::eyre::Result; use utils::*; -pub use utils::{dispatch_prompt, safe_exit, Cmd, TESTIN}; +pub use utils::{safe_exit, Cmd}; pub use self::context::Context; use crate::cli::api::CliIo; diff --git a/apps/src/lib/cli/utils.rs b/apps/src/lib/cli/utils.rs index c35298976c..73d144596c 100644 --- a/apps/src/lib/cli/utils.rs +++ b/apps/src/lib/cli/utils.rs @@ -6,7 +6,6 @@ use std::str::FromStr; use clap::{ArgAction, ArgMatches}; use color_eyre::eyre::Result; -use lazy_static::lazy_static; use super::args; use super::context::{Context, FromContext}; @@ -342,53 +341,3 @@ pub fn safe_exit(_: i32) -> ! { panic!("Test failed because the client exited unexpectedly.") } - -lazy_static! { - /// A replacement for stdin in testing. - pub static ref TESTIN: std::sync::Arc>> = - std::sync::Arc::new(std::sync::Mutex::new(vec![])); -} - -/// A generic function for displaying a prompt to users and reading -/// in their response. -fn prompt_aux(mut reader: R, mut writer: W, question: &str) -> String -where - R: std::io::Read, - W: Write, -{ - write!(&mut writer, "{}", question).expect("Unable to write"); - writer.flush().unwrap(); - let mut s = String::new(); - reader.read_to_string(&mut s).expect("Unable to read"); - s -} - -/// A function that chooses how to dispatch prompts -/// to users. There is a hierarchy of feature flags -/// that determines this. If no flags are set, -/// the question is printed to stdout and response -/// read from stdin. -pub fn dispatch_prompt(question: impl AsRef) -> String { - if cfg!(feature = "testing") { - prompt_aux( - TESTIN.lock().unwrap().as_slice(), - std::io::stdout(), - question.as_ref(), - ) - } else { - prompt_aux( - std::io::stdin().lock(), - std::io::stdout(), - question.as_ref(), - ) - } -} - -#[macro_export] -/// A convenience macro for formatting the user prompt before -/// forwarding it to the `[dispatch_prompt]` method. -macro_rules! prompt { - ($($arg:tt)*) => {{ - $crate::cli::dispatch_prompt(format!("{}", format_args!($($arg)*))) - }} -} diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index efdc8ee8c3..ccac9cc398 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -48,13 +48,12 @@ use namada::types::masp::{BalanceOwner, ExtendedViewingKey, PaymentAddress}; use namada::types::storage::{BlockHeight, BlockResults, Epoch, Key, KeySeg}; use namada::types::token::{Change, MaspDenom}; use namada::types::{storage, token}; -use namada::{display, display_line, edisplay_line}; +use namada::{display, display_line, edisplay_lin, prompt}; use tokio::time::Instant; use crate::cli::{self, args}; use crate::facade::tendermint::merkle::proof::Proof; use crate::facade::tendermint_rpc::error::Error as TError; -use crate::prompt; use crate::wallet::CliWalletUtils; /// Query the status of a given transaction. @@ -455,7 +454,7 @@ pub async fn query_pinned_balance< } // If a suitable viewing key was not found, then demand it from the user if balance == pinned_error { - let vk_str = prompt!("Enter the viewing key for {}: ", owner); + let vk_str = prompt!(IO, "Enter the viewing key for {}: ", owner); let fvk = match ExtendedViewingKey::from_str(vk_str.trim()) { Ok(fvk) => fvk, _ => { diff --git a/apps/src/lib/node/ledger/shell/testing/client.rs b/apps/src/lib/node/ledger/shell/testing/client.rs index 0f27a2f6f1..1aa35ca321 100644 --- a/apps/src/lib/node/ledger/shell/testing/client.rs +++ b/apps/src/lib/node/ledger/shell/testing/client.rs @@ -3,14 +3,14 @@ use std::ops::ControlFlow; use clap::Command as App; use eyre::Report; use namada::types::control_flow::Halt; -use namada::types::io::{DefaultIo, Io}; +use namada::types::io::Io; use tendermint_config::net::Address as TendermintAddress; use super::node::MockNode; use crate::cli::api::{CliApi, CliClient}; use crate::cli::args::Global; use crate::cli::{args, cmds, Cmd, Context, NamadaClient}; -use crate::node::ledger::shell::testing::utils::Bin; +use crate::node::ledger::shell::testing::utils::{Bin, TestingIo}; pub fn run( node: &MockNode, @@ -25,7 +25,7 @@ pub fn run( wasm_dir: Some(locked.wasm_dir.clone()), } }; - let ctx = Context::new::(global.clone())?; + let ctx = Context::new::(global.clone())?; let rt = tokio::runtime::Runtime::new().unwrap(); match who { @@ -47,7 +47,7 @@ pub fn run( NamadaClient::WithoutContext(sub_cmd, global) } }; - rt.block_on(CliApi::::handle_client_command( + rt.block_on(CliApi::::handle_client_command( Some(node), cmd, )) @@ -60,7 +60,7 @@ pub fn run( let cmd = cmds::NamadaWallet::parse(&matches) .expect("Could not parse wallet command"); - CliApi::::handle_wallet_command(cmd, ctx) + CliApi::::handle_wallet_command(cmd, ctx) } Bin::Relayer => { args.insert(0, "relayer"); @@ -69,7 +69,7 @@ pub fn run( let matches = app.get_matches_from(args.clone()); let cmd = cmds::NamadaRelayer::parse(&matches) .expect("Could not parse wallet command"); - rt.block_on(CliApi::::handle_relayer_command( + rt.block_on(CliApi::::handle_relayer_command( Some(node), cmd, )) diff --git a/apps/src/lib/node/ledger/shell/testing/utils.rs b/apps/src/lib/node/ledger/shell/testing/utils.rs index e66ead21e7..95da7efb85 100644 --- a/apps/src/lib/node/ledger/shell/testing/utils.rs +++ b/apps/src/lib/node/ledger/shell/testing/utils.rs @@ -1,5 +1,7 @@ use std::path::{Path, PathBuf}; +use lazy_static::lazy_static; +use namada::types::io::{prompt_aux, read_aux, Io}; use tempfile::tempdir; /// Namada binaries @@ -46,3 +48,198 @@ impl Default for TestDir { Self::new() } } + +/// The max number of bytes that the is currently remembered from stdout while +/// testing. +const TESTOUT_BUF_SIZE: usize = 100_000; + +lazy_static! { + /// A replacement for stdout in testing. The maximum number of bytes + /// it holds is limited to prevent memory issues. + pub static ref TESTOUT: std::sync::Arc>> = + std::sync::Arc::new(std::sync::Mutex::new(FixedBuffer::new(TESTOUT_BUF_SIZE))); +} + +lazy_static! { + /// A replacement for stdin in testing. + pub static ref TESTIN: std::sync::Arc>> = + std::sync::Arc::new(std::sync::Mutex::new(vec![])); +} + +pub struct TestingIo; + +impl Io for TestingIo { + fn print(output: impl AsRef) { + let mut testout = TESTOUT.lock().unwrap(); + testout.append(output.as_ref().as_bytes().to_vec()); + print!("{}", output.as_ref()); + } + + fn println(output: impl AsRef) { + let mut testout = TESTOUT.lock().unwrap(); + let mut bytes = output.as_ref().as_bytes().to_vec(); + bytes.extend_from_slice("\n".as_bytes()); + testout.append(bytes); + println!("{}", output.as_ref()); + } + + fn write( + _: W, + output: impl AsRef, + ) -> std::io::Result<()> { + Self::print(output); + Ok(()) + } + + fn writeln( + _: W, + output: impl AsRef, + ) -> std::io::Result<()> { + Self::println(output); + Ok(()) + } + + fn eprintln(output: impl AsRef) { + let mut testout = TESTOUT.lock().unwrap(); + let mut bytes = output.as_ref().as_bytes().to_vec(); + bytes.extend_from_slice("\n".as_bytes()); + testout.append(bytes); + eprintln!("{}", output.as_ref()); + } + + fn read() -> std::io::Result { + read_aux(TESTIN.lock().unwrap().as_slice()) + } + + fn prompt(question: impl AsRef) -> String { + prompt_aux( + TESTIN.lock().unwrap().as_slice(), + std::io::stdout(), + question.as_ref(), + ) + } +} + +/// Test helper that captures stdout of +/// a process. +pub struct CapturedOutput { + pub output: String, + pub result: T, + input: String, +} + +impl CapturedOutput { + pub fn with_input(input: String) -> Self { + Self { + output: "".to_string(), + result: (), + input, + } + } +} + +impl CapturedOutput { + /// Run a client command and capture + /// the output to the mocked stdout. + pub fn of(func: F) -> Self + where + F: FnOnce() -> T, + { + let mut capture = Self { + output: Default::default(), + result: func(), + input: Default::default(), + }; + capture.output = TESTOUT.lock().unwrap().read_string(); + capture + } + + /// Run a client command with input to the mocked stdin and capture + /// the output to the mocked stdout + pub fn run(&self, func: F) -> CapturedOutput + where + F: FnOnce() -> U, + { + { + // write the input to the mocked stdin + let mut buf = TESTIN.lock().unwrap(); + buf.clear(); + buf.extend_from_slice(self.input.as_bytes()); + } + CapturedOutput::of(func) + } + + /// Check if the captured output contains the regex. + pub fn matches(&self, needle: regex::Regex) -> bool { + needle.captures(&self.output).is_some() + } + + /// Check if the captured output contains the string. + pub fn contains(&self, needle: &str) -> bool { + let needle = regex::Regex::new(needle).unwrap(); + self.matches(needle) + } +} + +/// A buffer with a max size. Drops elements from the front on +/// size overflow. +pub struct FixedBuffer { + inner: Vec, + max_size: usize, +} + +impl FixedBuffer { + fn new(max_size: usize) -> Self { + Self { + inner: vec![], + max_size, + } + } + + /// Remove the first `size` elements from the buffer. + fn roll(&mut self, size: usize) { + self.inner = self.inner[size..].to_vec(); + } + + /// Add data to the end of the buffer, deleting from the + /// front as necessary. + fn append(&mut self, mut other: Vec) { + // if new data exceeds max size, take the tail. + if other.len() > self.max_size { + self.inner = other[(other.len() - self.max_size)..].to_vec(); + return; + } + // check if appending the data overflows buffer + let free_space = self.max_size - self.inner.len(); + if other.len() > free_space { + // delete the minimum amount of data from the front of the buffer + // to fit new data. + self.roll(other.len() - free_space); + } + self.inner.append(&mut other); + } +} + +impl FixedBuffer { + /// Read the inner buffer out to string + pub fn read_string(&mut self) -> String { + let mut fresh = vec![]; + std::mem::swap(&mut fresh, &mut self.inner); + String::from_utf8(fresh).unwrap() + } +} + +#[cfg(test)] +mod testing { + use super::*; + + #[test] + fn test_buffer() { + let mut buffer = FixedBuffer::::new(10); + buffer.inner = (1u64..=9_u64).collect(); + buffer.append(vec![10, 11, 12, 13, 14, 15]); + assert_eq!(buffer.inner, (6u64..=15_u64).collect::>()); + buffer.append((20u64..=40_u64).collect()); + assert_eq!(buffer.inner, (31u64..=40_u64).collect::>()); + } +} diff --git a/shared/src/types/io.rs b/shared/src/types/io.rs index 93d45a75ff..13798cc5b2 100644 --- a/shared/src/types/io.rs +++ b/shared/src/types/io.rs @@ -121,3 +121,12 @@ macro_rules! edisplay_line { <$io>::eprintln(format_args!($($args)*).to_string()) }; } + +#[macro_export] +/// A convenience macro for formatting the user prompt before +/// forwarding it to the [`Io::prompt`] method. +macro_rules! prompt { + ($io:ty,$($arg:tt)*) => {{ + <$io>::prompt(format!("{}", format_args!($($arg)*))) + }} +} diff --git a/tests/src/integration.rs b/tests/src/integration.rs index 8642e0e03c..1a7c84dbfb 100644 --- a/tests/src/integration.rs +++ b/tests/src/integration.rs @@ -1,3 +1,2 @@ mod masp; mod setup; -mod utils; diff --git a/tests/src/integration/masp.rs b/tests/src/integration/masp.rs index b8f9c47ed5..841bdb01c8 100644 --- a/tests/src/integration/masp.rs +++ b/tests/src/integration/masp.rs @@ -5,7 +5,7 @@ use color_eyre::owo_colors::OwoColorize; use namada::types::io::DefaultIo; use namada_apps::client::tx::CLIShieldedUtils; use namada_apps::node::ledger::shell::testing::client::run; -use namada_apps::node::ledger::shell::testing::utils::Bin; +use namada_apps::node::ledger::shell::testing::utils::{Bin, CapturedOutput}; use namada_core::types::address::{btc, eth, masp_rewards}; use namada_core::types::token; use namada_core::types::token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; @@ -17,7 +17,6 @@ use crate::e2e::setup::constants::{ AC_PAYMENT_ADDRESS, AC_VIEWING_KEY, ALBERT, A_SPENDING_KEY, BB_PAYMENT_ADDRESS, BERTHA, BTC, B_SPENDING_KEY, CHRISTEL, ETH, MASP, NAM, }; -use crate::integration::utils::CapturedOutput; /// In this test we verify that users of the MASP receive the correct rewards /// for leaving their assets in the pool for varying periods of time. diff --git a/tests/src/integration/utils.rs b/tests/src/integration/utils.rs deleted file mode 100644 index f626a001ee..0000000000 --- a/tests/src/integration/utils.rs +++ /dev/null @@ -1,83 +0,0 @@ -use std::fs::File; -use std::path::PathBuf; -use std::sync::Arc; - -struct TempFile(PathBuf); -impl TempFile { - fn new(path: PathBuf) -> (Self, File) { - let f = File::create(&path).unwrap(); - (Self(path), f) - } -} - -impl Drop for TempFile { - fn drop(&mut self) { - _ = std::fs::remove_file(&self.0); - } -} - -/// Test helper that captures stdout of -/// a process. -pub struct CapturedOutput { - pub output: String, - pub result: T, - input: String, -} - -impl CapturedOutput { - pub fn with_input(input: String) -> Self { - Self { - output: "".to_string(), - result: (), - input, - } - } -} - -impl CapturedOutput { - /// Run a client command and capture - /// the output to the mocked stdout. - pub(crate) fn of(func: F) -> Self - where - F: FnOnce() -> T, - { - std::io::set_output_capture(Some(Default::default())); - let mut capture = Self { - output: Default::default(), - result: func(), - input: Default::default(), - }; - let captured = std::io::set_output_capture(None); - let captured = captured.unwrap(); - let captured = Arc::try_unwrap(captured).unwrap(); - let captured = captured.into_inner().unwrap(); - capture.output = String::from_utf8(captured).unwrap(); - capture - } - - /// Run a client command with input to the mocked stdin and capture - /// the output to the mocked stdout - pub fn run(&self, func: F) -> CapturedOutput - where - F: FnOnce() -> U, - { - { - // write the input to the mocked stdin - let mut buf = namada_apps::cli::TESTIN.lock().unwrap(); - buf.clear(); - buf.extend_from_slice(self.input.as_bytes()); - } - CapturedOutput::of(func) - } - - /// Check if the captured output contains the regex. - pub fn matches(&self, needle: regex::Regex) -> bool { - needle.captures(&self.output).is_some() - } - - /// Check if the captured output contains the string. - pub fn contains(&self, needle: &str) -> bool { - let needle = regex::Regex::new(needle).unwrap(); - self.matches(needle) - } -} diff --git a/tests/src/lib.rs b/tests/src/lib.rs index 7e35947477..1d9958a30a 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -1,4 +1,3 @@ -#![cfg_attr(test, feature(internal_output_capture))] //! Namada integrations and WASM tests and testing helpers. #![doc(html_favicon_url = "https://dev.namada.net/master/favicon.png")] @@ -13,7 +12,6 @@ pub use vm_host_env::{ibc, tx, vp}; #[cfg(test)] mod e2e; #[cfg(test)] -#[allow(dead_code)] mod integration; pub mod native_vp; pub mod storage; diff --git a/wasm/checksums.json b/wasm/checksums.json index cb9ca8ed52..adf06b5afe 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,21 +1,21 @@ { - "tx_bond.wasm": "tx_bond.08cedac1d825cd8ee9b04ef059bc68fee04f82fec57e0f6bc38123de8fb35b85.wasm", - "tx_bridge_pool.wasm": "tx_bridge_pool.c9ef2bdb19910e12eadabc7f0552b45c937bcf1f47ca09759e52112b68d94114.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.2be60082c6b891cb437b4392e1b2570f5d92fed35dd624cd079ea48c4bc7902c.wasm", - "tx_ibc.wasm": "tx_ibc.f8056f465d17043d2864309d1c50a897a028b6ebf9702e5c403e1bf8c670f6e6.wasm", - "tx_init_account.wasm": "tx_init_account.e91ab25cebd15af9a958eca4a34ba287804b1ff6ba4c82fc941f7f50816e9c4d.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.1683eb9a2b2616af21f4f55457f7e06e0ae0591f460ede412a548ea9b1ab694c.wasm", - "tx_init_validator.wasm": "tx_init_validator.67574f70c1f76c2c68498d17323793366c13d119480b6787c8c5ede312e51da0.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.e6375abba3f750700c06fbdeb2d5edec22b6a382116a67a7f9d7f7ba49f134e1.wasm", - "tx_transfer.wasm": "tx_transfer.e65b47bc94c5a09e3edc68298ad0e5e35041b91bc4d679bf5b7f433dffdce58e.wasm", - "tx_unbond.wasm": "tx_unbond.4fd70d297ccedb369bf88d8a8459a47ea733d329410387a6f80a0e026c6e480d.wasm", - "tx_unjail_validator.wasm": "tx_unjail_validator.b8e7204b14e15a3c395432f35788eca45ef4332916d96dfba87627a5717916de.wasm", - "tx_update_vp.wasm": "tx_update_vp.65c5ca3e48fdef70e696460eca7540cf6196511d76fb2465133b145409329b3e.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.e0a003d922230d32b741b57ca18913cbc4d5d2290f02cb37dfdaa7f27cebb486.wasm", - "tx_withdraw.wasm": "tx_withdraw.40499cb0e268d3cc3d9bca5dacca05d8df35f5b90adf4087a5171505472d921a.wasm", - "vp_implicit.wasm": "vp_implicit.57af3b7d2ee9e2c9d7ef1e33a85646abeea7ea02dad19b7e0b20b289de5a7ae9.wasm", - "vp_masp.wasm": "vp_masp.4d656f775b7462095e7d9ff533e346829fad02568b1cf92fa2f99cc0677720ce.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.b0c855f0356a468db1d26baebc988508d4824836b86adb94ac432368feb03ac4.wasm", - "vp_user.wasm": "vp_user.31967a7411caf61729e46840eec90d2db386cf4745f49d59086a43cc3c3e2d39.wasm", - "vp_validator.wasm": "vp_validator.0bca8e34f5d51c74b2861d588e3dd6a8e61de7c63d356af63e2182e030ac42ee.wasm" + "tx_bond.wasm": "tx_bond.3d9341191f3059b478435c52ec0db4b19fe9d2b7a60ac7371287fb15552dbe3b.wasm", + "tx_bridge_pool.wasm": "tx_bridge_pool.0d8d4de6222aeb5b0cb80fa39b1ce1ee9e2f50115ffdaa2a1e79eabdddeb6bfe.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.349170accac147e8b4b77eb10ded0421936176b09d8913246235db0c5a4fac83.wasm", + "tx_ibc.wasm": "tx_ibc.5ddb7f3032d8b59446486a7fb20bac8c43c5a002f6e9b3309ff2e01048b98c7f.wasm", + "tx_init_account.wasm": "tx_init_account.f254fa819c85694d01f440132690ac54b3a8911a5eca9ccb49bc6171cf219017.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.1cf4ac19672bc368ff1e8ea0fd31507ac4a9a33c1b21ff80d85de8b36c6bce66.wasm", + "tx_init_validator.wasm": "tx_init_validator.bd5aa6513bf890e4591413fe6a4345dc28a0767ebc2fad2279e851fb1373091a.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.8f252e6fd0ab74be7f7afb4dbe4ba2b3d441011fd57ce5ccd06fe24109728019.wasm", + "tx_transfer.wasm": "tx_transfer.cdb2edbaa152608e2ae6d96a586955b911b74cc097228fee2bb5768f5f6db0ea.wasm", + "tx_unbond.wasm": "tx_unbond.0e6319063d74311a42a405435c15aa867894fc0a7ae209c08fc5fa9eee4013c5.wasm", + "tx_unjail_validator.wasm": "tx_unjail_validator.24e345f69f62fc8b75c475eee66e3252dc06a5468411f6770096b1b8ced8f022.wasm", + "tx_update_vp.wasm": "tx_update_vp.86123368b829488aface2804afc397a8c26873e9f319e3183fb211b292239e1d.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.80586902bae368088edfdef22a6fcc25aee8779620fd48eabbe1f3f11754d701.wasm", + "tx_withdraw.wasm": "tx_withdraw.5648dac90b0f25c94d60fb39dfed6120f6eed8794e4c3dbf554af36adf7bafc3.wasm", + "vp_implicit.wasm": "vp_implicit.18f2b20fa776493a0182b05e95b3eb0676be702dc0a4d39a09cb9ab51d116146.wasm", + "vp_masp.wasm": "vp_masp.b07f344c2d760ee69dfcef1ae4e703f63c7c6594a377a48550ef576f9fcaca96.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.9e3c9d133e52d4b762b9fc7d3bcc1e7d0ad2bc4d1754d390ed31555471f184d5.wasm", + "vp_user.wasm": "vp_user.df279909cb2de9d5125bbfe8b34b14ff3b924ed7db61dcc93cbb06f2f3f29509.wasm", + "vp_validator.wasm": "vp_validator.e3ed90196c676b73bcfa2f4142e2831b306eef56bde73243b2d50ddc5ab8e2b2.wasm" } \ No newline at end of file From a2c5cedab902b8855e837f19f094ab3cb0a31182 Mon Sep 17 00:00:00 2001 From: satan Date: Mon, 24 Jul 2023 23:50:54 +0200 Subject: [PATCH 11/28] [chore]: rebasing on bat/generic-io --- apps/src/lib/client/rpc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index ccac9cc398..26b1956a11 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -48,7 +48,7 @@ use namada::types::masp::{BalanceOwner, ExtendedViewingKey, PaymentAddress}; use namada::types::storage::{BlockHeight, BlockResults, Epoch, Key, KeySeg}; use namada::types::token::{Change, MaspDenom}; use namada::types::{storage, token}; -use namada::{display, display_line, edisplay_lin, prompt}; +use namada::{display, display_line, edisplay_line, prompt}; use tokio::time::Instant; use crate::cli::{self, args}; From 88820fd20c0a4c3dd3a52c8ee794e1e2253bc9b5 Mon Sep 17 00:00:00 2001 From: satan Date: Wed, 26 Jul 2023 13:41:01 +0200 Subject: [PATCH 12/28] [fix]: Fixing downstream async io changes from bat/generic-io --- apps/src/lib/client/rpc.rs | 3 ++- apps/src/lib/node/ledger/shell/testing/utils.rs | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 26b1956a11..20ba1509bc 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -454,7 +454,8 @@ pub async fn query_pinned_balance< } // If a suitable viewing key was not found, then demand it from the user if balance == pinned_error { - let vk_str = prompt!(IO, "Enter the viewing key for {}: ", owner); + let vk_str = + prompt!(IO, "Enter the viewing key for {}: ", owner).await; let fvk = match ExtendedViewingKey::from_str(vk_str.trim()) { Ok(fvk) => fvk, _ => { diff --git a/apps/src/lib/node/ledger/shell/testing/utils.rs b/apps/src/lib/node/ledger/shell/testing/utils.rs index 95da7efb85..3ff3bdb7d3 100644 --- a/apps/src/lib/node/ledger/shell/testing/utils.rs +++ b/apps/src/lib/node/ledger/shell/testing/utils.rs @@ -68,6 +68,7 @@ lazy_static! { pub struct TestingIo; +#[async_trait::async_trait(?Send)] impl Io for TestingIo { fn print(output: impl AsRef) { let mut testout = TESTOUT.lock().unwrap(); @@ -107,11 +108,11 @@ impl Io for TestingIo { eprintln!("{}", output.as_ref()); } - fn read() -> std::io::Result { + async fn read() -> std::io::Result { read_aux(TESTIN.lock().unwrap().as_slice()) } - fn prompt(question: impl AsRef) -> String { + async fn prompt(question: impl AsRef) -> String { prompt_aux( TESTIN.lock().unwrap().as_slice(), std::io::stdout(), From 844e4a2e21002fca0f3d00af5d1089d89715aaad Mon Sep 17 00:00:00 2001 From: satan Date: Fri, 28 Jul 2023 13:16:44 +0200 Subject: [PATCH 13/28] [feat]: After rebasing on bat/generic-io, need to make io read methods async for integration testing --- .../lib/node/ledger/shell/testing/utils.rs | 43 +++++++++++++++---- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/testing/utils.rs b/apps/src/lib/node/ledger/shell/testing/utils.rs index 3ff3bdb7d3..0161f86416 100644 --- a/apps/src/lib/node/ledger/shell/testing/utils.rs +++ b/apps/src/lib/node/ledger/shell/testing/utils.rs @@ -1,8 +1,12 @@ +use std::ops::{Deref, DerefMut}; use std::path::{Path, PathBuf}; +use std::pin::Pin; +use std::task::{Context, Poll}; use lazy_static::lazy_static; use namada::types::io::{prompt_aux, read_aux, Io}; use tempfile::tempdir; +use tokio::io::{AsyncRead, ReadBuf}; /// Namada binaries #[derive(Debug)] @@ -62,8 +66,8 @@ lazy_static! { lazy_static! { /// A replacement for stdin in testing. - pub static ref TESTIN: std::sync::Arc>> = - std::sync::Arc::new(std::sync::Mutex::new(vec![])); + pub static ref TESTIN: AtomicBuffer = + AtomicBuffer(std::sync::Arc::new(std::sync::Mutex::new(vec![]))); } pub struct TestingIo; @@ -108,16 +112,12 @@ impl Io for TestingIo { eprintln!("{}", output.as_ref()); } - async fn read() -> std::io::Result { - read_aux(TESTIN.lock().unwrap().as_slice()) + async fn read() -> tokio::io::Result { + read_aux(&*TESTIN).await } async fn prompt(question: impl AsRef) -> String { - prompt_aux( - TESTIN.lock().unwrap().as_slice(), - std::io::stdout(), - question.as_ref(), - ) + prompt_aux(&*TESTIN, tokio::io::stdout(), question.as_ref()).await } } @@ -230,6 +230,31 @@ impl FixedBuffer { } } +pub struct AtomicBuffer(std::sync::Arc>>); + +impl Deref for AtomicBuffer { + type Target = std::sync::Arc>>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'a> AsyncRead for &'a AtomicBuffer { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut ReadBuf<'_>, + ) -> Poll> { + let mut inner = self.lock().unwrap(); + let buf_before = buf.filled().len(); + let res = AsyncRead::poll_read(Pin::new(&mut inner.as_slice()), cx, buf); + let amount_read = buf.filled().len() - buf_before; + *inner.deref_mut() = inner[amount_read..].to_vec(); + res + } +} + #[cfg(test)] mod testing { use super::*; From b734c6535cf00c5602a522df09c34d18f8e36511 Mon Sep 17 00:00:00 2001 From: satan Date: Fri, 28 Jul 2023 13:18:13 +0200 Subject: [PATCH 14/28] [chore]: Formatting --- apps/src/lib/node/ledger/shell/testing/utils.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/src/lib/node/ledger/shell/testing/utils.rs b/apps/src/lib/node/ledger/shell/testing/utils.rs index 0161f86416..bfcb7f50ab 100644 --- a/apps/src/lib/node/ledger/shell/testing/utils.rs +++ b/apps/src/lib/node/ledger/shell/testing/utils.rs @@ -248,7 +248,8 @@ impl<'a> AsyncRead for &'a AtomicBuffer { ) -> Poll> { let mut inner = self.lock().unwrap(); let buf_before = buf.filled().len(); - let res = AsyncRead::poll_read(Pin::new(&mut inner.as_slice()), cx, buf); + let res = + AsyncRead::poll_read(Pin::new(&mut inner.as_slice()), cx, buf); let amount_read = buf.filled().len() - buf_before; *inner.deref_mut() = inner[amount_read..].to_vec(); res From bf599b444b812575808add2a3634df0af93204fe Mon Sep 17 00:00:00 2001 From: satan Date: Mon, 24 Jul 2023 17:00:05 +0200 Subject: [PATCH 15/28] [feat]: Refactored the masp integration tests to no longer need the nightly feature --- Cargo.lock | 10 +- apps/src/lib/cli.rs | 2 +- apps/src/lib/cli/utils.rs | 51 ----- apps/src/lib/client/rpc.rs | 5 +- .../lib/node/ledger/shell/testing/client.rs | 12 +- .../lib/node/ledger/shell/testing/utils.rs | 197 ++++++++++++++++++ shared/src/types/io.rs | 9 + tests/src/integration.rs | 1 - tests/src/integration/masp.rs | 3 +- tests/src/integration/utils.rs | 83 -------- tests/src/lib.rs | 2 - wasm/checksums.json | 38 ++-- 12 files changed, 240 insertions(+), 173 deletions(-) delete mode 100644 tests/src/integration/utils.rs diff --git a/Cargo.lock b/Cargo.lock index 3a069b0956..339380655a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3370,7 +3370,7 @@ checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", - "rustix 0.37.13", + "rustix 0.37.1", "windows-sys 0.48.0", ] @@ -5674,16 +5674,16 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.13" +version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79bef90eb6d984c72722595b5b1348ab39275a5e5123faca6863bf07d75a4e0" +checksum = "d4790277f605573dd24b6751701e0823582a63c7cafc095e427e6c66e45dd75e" dependencies = [ "bitflags 1.2.1", "errno", "io-lifetimes", "libc", "linux-raw-sys 0.3.7", - "windows-sys 0.48.0", + "windows-sys 0.45.0", ] [[package]] @@ -6446,7 +6446,7 @@ dependencies = [ "cfg-if 1.0.0", "fastrand", "redox_syscall 0.3.5", - "rustix 0.37.13", + "rustix 0.37.1", "windows-sys 0.45.0", ] diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 31b28977f3..db6ef2679d 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -16,7 +16,7 @@ pub mod wallet; use clap::{ArgGroup, ArgMatches, ColorChoice}; use color_eyre::eyre::Result; use utils::*; -pub use utils::{dispatch_prompt, safe_exit, Cmd, TESTIN}; +pub use utils::{safe_exit, Cmd}; pub use self::context::Context; use crate::cli::api::CliIo; diff --git a/apps/src/lib/cli/utils.rs b/apps/src/lib/cli/utils.rs index 4ae8e4adbc..26cc38ff7f 100644 --- a/apps/src/lib/cli/utils.rs +++ b/apps/src/lib/cli/utils.rs @@ -6,7 +6,6 @@ use std::str::FromStr; use clap::{ArgAction, ArgMatches}; use color_eyre::eyre::Result; -use lazy_static::lazy_static; use super::args; use super::context::{Context, FromContext}; @@ -363,53 +362,3 @@ pub fn safe_exit(_: i32) -> ! { panic!("Test failed because the client exited unexpectedly.") } - -lazy_static! { - /// A replacement for stdin in testing. - pub static ref TESTIN: std::sync::Arc>> = - std::sync::Arc::new(std::sync::Mutex::new(vec![])); -} - -/// A generic function for displaying a prompt to users and reading -/// in their response. -fn prompt_aux(mut reader: R, mut writer: W, question: &str) -> String -where - R: std::io::Read, - W: Write, -{ - write!(&mut writer, "{}", question).expect("Unable to write"); - writer.flush().unwrap(); - let mut s = String::new(); - reader.read_to_string(&mut s).expect("Unable to read"); - s -} - -/// A function that chooses how to dispatch prompts -/// to users. There is a hierarchy of feature flags -/// that determines this. If no flags are set, -/// the question is printed to stdout and response -/// read from stdin. -pub fn dispatch_prompt(question: impl AsRef) -> String { - if cfg!(feature = "testing") { - prompt_aux( - TESTIN.lock().unwrap().as_slice(), - std::io::stdout(), - question.as_ref(), - ) - } else { - prompt_aux( - std::io::stdin().lock(), - std::io::stdout(), - question.as_ref(), - ) - } -} - -#[macro_export] -/// A convenience macro for formatting the user prompt before -/// forwarding it to the `[dispatch_prompt]` method. -macro_rules! prompt { - ($($arg:tt)*) => {{ - $crate::cli::dispatch_prompt(format!("{}", format_args!($($arg)*))) - }} -} diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index a49be3f89e..2f01716ec3 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -50,13 +50,12 @@ use namada::types::masp::{BalanceOwner, ExtendedViewingKey, PaymentAddress}; use namada::types::storage::{BlockHeight, BlockResults, Epoch, Key, KeySeg}; use namada::types::token::{Change, MaspDenom}; use namada::types::{storage, token}; -use namada::{display, display_line, edisplay_line}; +use namada::{display, display_line, edisplay_lin, prompt}; use tokio::time::Instant; use crate::cli::{self, args}; use crate::facade::tendermint::merkle::proof::Proof; use crate::facade::tendermint_rpc::error::Error as TError; -use crate::prompt; use crate::wallet::CliWalletUtils; /// Query the status of a given transaction. @@ -457,7 +456,7 @@ pub async fn query_pinned_balance< } // If a suitable viewing key was not found, then demand it from the user if balance == pinned_error { - let vk_str = prompt!("Enter the viewing key for {}: ", owner); + let vk_str = prompt!(IO, "Enter the viewing key for {}: ", owner); let fvk = match ExtendedViewingKey::from_str(vk_str.trim()) { Ok(fvk) => fvk, _ => { diff --git a/apps/src/lib/node/ledger/shell/testing/client.rs b/apps/src/lib/node/ledger/shell/testing/client.rs index 0f27a2f6f1..1aa35ca321 100644 --- a/apps/src/lib/node/ledger/shell/testing/client.rs +++ b/apps/src/lib/node/ledger/shell/testing/client.rs @@ -3,14 +3,14 @@ use std::ops::ControlFlow; use clap::Command as App; use eyre::Report; use namada::types::control_flow::Halt; -use namada::types::io::{DefaultIo, Io}; +use namada::types::io::Io; use tendermint_config::net::Address as TendermintAddress; use super::node::MockNode; use crate::cli::api::{CliApi, CliClient}; use crate::cli::args::Global; use crate::cli::{args, cmds, Cmd, Context, NamadaClient}; -use crate::node::ledger::shell::testing::utils::Bin; +use crate::node::ledger::shell::testing::utils::{Bin, TestingIo}; pub fn run( node: &MockNode, @@ -25,7 +25,7 @@ pub fn run( wasm_dir: Some(locked.wasm_dir.clone()), } }; - let ctx = Context::new::(global.clone())?; + let ctx = Context::new::(global.clone())?; let rt = tokio::runtime::Runtime::new().unwrap(); match who { @@ -47,7 +47,7 @@ pub fn run( NamadaClient::WithoutContext(sub_cmd, global) } }; - rt.block_on(CliApi::::handle_client_command( + rt.block_on(CliApi::::handle_client_command( Some(node), cmd, )) @@ -60,7 +60,7 @@ pub fn run( let cmd = cmds::NamadaWallet::parse(&matches) .expect("Could not parse wallet command"); - CliApi::::handle_wallet_command(cmd, ctx) + CliApi::::handle_wallet_command(cmd, ctx) } Bin::Relayer => { args.insert(0, "relayer"); @@ -69,7 +69,7 @@ pub fn run( let matches = app.get_matches_from(args.clone()); let cmd = cmds::NamadaRelayer::parse(&matches) .expect("Could not parse wallet command"); - rt.block_on(CliApi::::handle_relayer_command( + rt.block_on(CliApi::::handle_relayer_command( Some(node), cmd, )) diff --git a/apps/src/lib/node/ledger/shell/testing/utils.rs b/apps/src/lib/node/ledger/shell/testing/utils.rs index e66ead21e7..95da7efb85 100644 --- a/apps/src/lib/node/ledger/shell/testing/utils.rs +++ b/apps/src/lib/node/ledger/shell/testing/utils.rs @@ -1,5 +1,7 @@ use std::path::{Path, PathBuf}; +use lazy_static::lazy_static; +use namada::types::io::{prompt_aux, read_aux, Io}; use tempfile::tempdir; /// Namada binaries @@ -46,3 +48,198 @@ impl Default for TestDir { Self::new() } } + +/// The max number of bytes that the is currently remembered from stdout while +/// testing. +const TESTOUT_BUF_SIZE: usize = 100_000; + +lazy_static! { + /// A replacement for stdout in testing. The maximum number of bytes + /// it holds is limited to prevent memory issues. + pub static ref TESTOUT: std::sync::Arc>> = + std::sync::Arc::new(std::sync::Mutex::new(FixedBuffer::new(TESTOUT_BUF_SIZE))); +} + +lazy_static! { + /// A replacement for stdin in testing. + pub static ref TESTIN: std::sync::Arc>> = + std::sync::Arc::new(std::sync::Mutex::new(vec![])); +} + +pub struct TestingIo; + +impl Io for TestingIo { + fn print(output: impl AsRef) { + let mut testout = TESTOUT.lock().unwrap(); + testout.append(output.as_ref().as_bytes().to_vec()); + print!("{}", output.as_ref()); + } + + fn println(output: impl AsRef) { + let mut testout = TESTOUT.lock().unwrap(); + let mut bytes = output.as_ref().as_bytes().to_vec(); + bytes.extend_from_slice("\n".as_bytes()); + testout.append(bytes); + println!("{}", output.as_ref()); + } + + fn write( + _: W, + output: impl AsRef, + ) -> std::io::Result<()> { + Self::print(output); + Ok(()) + } + + fn writeln( + _: W, + output: impl AsRef, + ) -> std::io::Result<()> { + Self::println(output); + Ok(()) + } + + fn eprintln(output: impl AsRef) { + let mut testout = TESTOUT.lock().unwrap(); + let mut bytes = output.as_ref().as_bytes().to_vec(); + bytes.extend_from_slice("\n".as_bytes()); + testout.append(bytes); + eprintln!("{}", output.as_ref()); + } + + fn read() -> std::io::Result { + read_aux(TESTIN.lock().unwrap().as_slice()) + } + + fn prompt(question: impl AsRef) -> String { + prompt_aux( + TESTIN.lock().unwrap().as_slice(), + std::io::stdout(), + question.as_ref(), + ) + } +} + +/// Test helper that captures stdout of +/// a process. +pub struct CapturedOutput { + pub output: String, + pub result: T, + input: String, +} + +impl CapturedOutput { + pub fn with_input(input: String) -> Self { + Self { + output: "".to_string(), + result: (), + input, + } + } +} + +impl CapturedOutput { + /// Run a client command and capture + /// the output to the mocked stdout. + pub fn of(func: F) -> Self + where + F: FnOnce() -> T, + { + let mut capture = Self { + output: Default::default(), + result: func(), + input: Default::default(), + }; + capture.output = TESTOUT.lock().unwrap().read_string(); + capture + } + + /// Run a client command with input to the mocked stdin and capture + /// the output to the mocked stdout + pub fn run(&self, func: F) -> CapturedOutput + where + F: FnOnce() -> U, + { + { + // write the input to the mocked stdin + let mut buf = TESTIN.lock().unwrap(); + buf.clear(); + buf.extend_from_slice(self.input.as_bytes()); + } + CapturedOutput::of(func) + } + + /// Check if the captured output contains the regex. + pub fn matches(&self, needle: regex::Regex) -> bool { + needle.captures(&self.output).is_some() + } + + /// Check if the captured output contains the string. + pub fn contains(&self, needle: &str) -> bool { + let needle = regex::Regex::new(needle).unwrap(); + self.matches(needle) + } +} + +/// A buffer with a max size. Drops elements from the front on +/// size overflow. +pub struct FixedBuffer { + inner: Vec, + max_size: usize, +} + +impl FixedBuffer { + fn new(max_size: usize) -> Self { + Self { + inner: vec![], + max_size, + } + } + + /// Remove the first `size` elements from the buffer. + fn roll(&mut self, size: usize) { + self.inner = self.inner[size..].to_vec(); + } + + /// Add data to the end of the buffer, deleting from the + /// front as necessary. + fn append(&mut self, mut other: Vec) { + // if new data exceeds max size, take the tail. + if other.len() > self.max_size { + self.inner = other[(other.len() - self.max_size)..].to_vec(); + return; + } + // check if appending the data overflows buffer + let free_space = self.max_size - self.inner.len(); + if other.len() > free_space { + // delete the minimum amount of data from the front of the buffer + // to fit new data. + self.roll(other.len() - free_space); + } + self.inner.append(&mut other); + } +} + +impl FixedBuffer { + /// Read the inner buffer out to string + pub fn read_string(&mut self) -> String { + let mut fresh = vec![]; + std::mem::swap(&mut fresh, &mut self.inner); + String::from_utf8(fresh).unwrap() + } +} + +#[cfg(test)] +mod testing { + use super::*; + + #[test] + fn test_buffer() { + let mut buffer = FixedBuffer::::new(10); + buffer.inner = (1u64..=9_u64).collect(); + buffer.append(vec![10, 11, 12, 13, 14, 15]); + assert_eq!(buffer.inner, (6u64..=15_u64).collect::>()); + buffer.append((20u64..=40_u64).collect()); + assert_eq!(buffer.inner, (31u64..=40_u64).collect::>()); + } +} diff --git a/shared/src/types/io.rs b/shared/src/types/io.rs index 93d45a75ff..13798cc5b2 100644 --- a/shared/src/types/io.rs +++ b/shared/src/types/io.rs @@ -121,3 +121,12 @@ macro_rules! edisplay_line { <$io>::eprintln(format_args!($($args)*).to_string()) }; } + +#[macro_export] +/// A convenience macro for formatting the user prompt before +/// forwarding it to the [`Io::prompt`] method. +macro_rules! prompt { + ($io:ty,$($arg:tt)*) => {{ + <$io>::prompt(format!("{}", format_args!($($arg)*))) + }} +} diff --git a/tests/src/integration.rs b/tests/src/integration.rs index 8642e0e03c..1a7c84dbfb 100644 --- a/tests/src/integration.rs +++ b/tests/src/integration.rs @@ -1,3 +1,2 @@ mod masp; mod setup; -mod utils; diff --git a/tests/src/integration/masp.rs b/tests/src/integration/masp.rs index d024f84132..659c7bbaa7 100644 --- a/tests/src/integration/masp.rs +++ b/tests/src/integration/masp.rs @@ -5,7 +5,7 @@ use color_eyre::owo_colors::OwoColorize; use namada::types::io::DefaultIo; use namada_apps::client::tx::CLIShieldedUtils; use namada_apps::node::ledger::shell::testing::client::run; -use namada_apps::node::ledger::shell::testing::utils::Bin; +use namada_apps::node::ledger::shell::testing::utils::{Bin, CapturedOutput}; use namada_core::types::address::{btc, eth, masp_rewards}; use namada_core::types::token; use namada_core::types::token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; @@ -17,7 +17,6 @@ use crate::e2e::setup::constants::{ AC_PAYMENT_ADDRESS, AC_VIEWING_KEY, ALBERT, A_SPENDING_KEY, BB_PAYMENT_ADDRESS, BERTHA, BTC, B_SPENDING_KEY, CHRISTEL, ETH, MASP, NAM, }; -use crate::integration::utils::CapturedOutput; /// In this test we verify that users of the MASP receive the correct rewards /// for leaving their assets in the pool for varying periods of time. diff --git a/tests/src/integration/utils.rs b/tests/src/integration/utils.rs deleted file mode 100644 index f626a001ee..0000000000 --- a/tests/src/integration/utils.rs +++ /dev/null @@ -1,83 +0,0 @@ -use std::fs::File; -use std::path::PathBuf; -use std::sync::Arc; - -struct TempFile(PathBuf); -impl TempFile { - fn new(path: PathBuf) -> (Self, File) { - let f = File::create(&path).unwrap(); - (Self(path), f) - } -} - -impl Drop for TempFile { - fn drop(&mut self) { - _ = std::fs::remove_file(&self.0); - } -} - -/// Test helper that captures stdout of -/// a process. -pub struct CapturedOutput { - pub output: String, - pub result: T, - input: String, -} - -impl CapturedOutput { - pub fn with_input(input: String) -> Self { - Self { - output: "".to_string(), - result: (), - input, - } - } -} - -impl CapturedOutput { - /// Run a client command and capture - /// the output to the mocked stdout. - pub(crate) fn of(func: F) -> Self - where - F: FnOnce() -> T, - { - std::io::set_output_capture(Some(Default::default())); - let mut capture = Self { - output: Default::default(), - result: func(), - input: Default::default(), - }; - let captured = std::io::set_output_capture(None); - let captured = captured.unwrap(); - let captured = Arc::try_unwrap(captured).unwrap(); - let captured = captured.into_inner().unwrap(); - capture.output = String::from_utf8(captured).unwrap(); - capture - } - - /// Run a client command with input to the mocked stdin and capture - /// the output to the mocked stdout - pub fn run(&self, func: F) -> CapturedOutput - where - F: FnOnce() -> U, - { - { - // write the input to the mocked stdin - let mut buf = namada_apps::cli::TESTIN.lock().unwrap(); - buf.clear(); - buf.extend_from_slice(self.input.as_bytes()); - } - CapturedOutput::of(func) - } - - /// Check if the captured output contains the regex. - pub fn matches(&self, needle: regex::Regex) -> bool { - needle.captures(&self.output).is_some() - } - - /// Check if the captured output contains the string. - pub fn contains(&self, needle: &str) -> bool { - let needle = regex::Regex::new(needle).unwrap(); - self.matches(needle) - } -} diff --git a/tests/src/lib.rs b/tests/src/lib.rs index 7e35947477..1d9958a30a 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -1,4 +1,3 @@ -#![cfg_attr(test, feature(internal_output_capture))] //! Namada integrations and WASM tests and testing helpers. #![doc(html_favicon_url = "https://dev.namada.net/master/favicon.png")] @@ -13,7 +12,6 @@ pub use vm_host_env::{ibc, tx, vp}; #[cfg(test)] mod e2e; #[cfg(test)] -#[allow(dead_code)] mod integration; pub mod native_vp; pub mod storage; diff --git a/wasm/checksums.json b/wasm/checksums.json index 71b5580e4a..adf06b5afe 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,21 +1,21 @@ { - "tx_bond.wasm": "tx_bond.0967203d10b78f7f28c3e1d674c0ff3ec25ec00e9b3a4010648c5cc381ad52ec.wasm", - "tx_bridge_pool.wasm": "tx_bridge_pool.c6dd30e79edff950016f495cee946bc6c0d6847f89322fbf733c89bb768c1fd4.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.1e89cf7f091a324872582c1092f2bda662be21040ac796d8aca08d08e9332a3e.wasm", - "tx_ibc.wasm": "tx_ibc.2b35a3a35049fe267121906176bafc0ea2b221a185fe31652216f93da66209e7.wasm", - "tx_init_account.wasm": "tx_init_account.6e77218b385a386f49c65c13b6112c75dcb06c50cf7bf7a5a479e7e8b7b458fa.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.d0c969c6b7a3d262d82586c630f29a00e587653fa9df1cf17021914764d4d3ab.wasm", - "tx_init_validator.wasm": "tx_init_validator.df780e2cc9ab4a5726931b662fd75cad907e495745aec1c466877878cee1ae3a.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.062b11c679a35e50f7fcfca95e3ada01bc89098591ae92526cfafaab5adf954e.wasm", - "tx_transfer.wasm": "tx_transfer.5f9e5a2f85fa5c1ec2bbb0aa9bebaba6bddc7e777ca956f5d980ad1cc9af3e7e.wasm", - "tx_unbond.wasm": "tx_unbond.dc169426d36f2bcfd82d976e77e2f3beb85ac98e75652ddf8367fa081fb17b3d.wasm", - "tx_unjail_validator.wasm": "tx_unjail_validator.23d1b075f28d5fa3ee413ee4db31792a2714d9b21929cbf1ba59832c3adec886.wasm", - "tx_update_account.wasm": "tx_update_account.88646c9fb5d3327701d69b12ce49ae35431ebe2ea3691900874cad66791f1ca9.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.c9abcd6f37d59daf9a39e30dcebd9e0789d5bcd679f5931ed7899073d0a5d36f.wasm", - "tx_withdraw.wasm": "tx_withdraw.02ac0399bc5b745346641facd968607931bf9abf67d7ed336b6b0540360f2d43.wasm", - "vp_implicit.wasm": "vp_implicit.1d212280efc3fccc16de9497dae8cc1670f21f0e0ce4c3bd9fc58fc816037cdf.wasm", - "vp_masp.wasm": "vp_masp.1d264f4ba602df546011ae4040da00bd0b103c6bad62ee5de985940e8bf0c2e7.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.d279129573a4f0625f411c7b147ac94bc40e0db2ea04b777f29e57be33a53522.wasm", - "vp_user.wasm": "vp_user.788b8039744b73bd6fe2b67b273802bae4c26cd117092eb8437f455d5aa95dbd.wasm", - "vp_validator.wasm": "vp_validator.98457b70476c76a16b1cd28232bacf043438bfd5e054bc9f7651ef1747e48594.wasm" + "tx_bond.wasm": "tx_bond.3d9341191f3059b478435c52ec0db4b19fe9d2b7a60ac7371287fb15552dbe3b.wasm", + "tx_bridge_pool.wasm": "tx_bridge_pool.0d8d4de6222aeb5b0cb80fa39b1ce1ee9e2f50115ffdaa2a1e79eabdddeb6bfe.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.349170accac147e8b4b77eb10ded0421936176b09d8913246235db0c5a4fac83.wasm", + "tx_ibc.wasm": "tx_ibc.5ddb7f3032d8b59446486a7fb20bac8c43c5a002f6e9b3309ff2e01048b98c7f.wasm", + "tx_init_account.wasm": "tx_init_account.f254fa819c85694d01f440132690ac54b3a8911a5eca9ccb49bc6171cf219017.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.1cf4ac19672bc368ff1e8ea0fd31507ac4a9a33c1b21ff80d85de8b36c6bce66.wasm", + "tx_init_validator.wasm": "tx_init_validator.bd5aa6513bf890e4591413fe6a4345dc28a0767ebc2fad2279e851fb1373091a.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.8f252e6fd0ab74be7f7afb4dbe4ba2b3d441011fd57ce5ccd06fe24109728019.wasm", + "tx_transfer.wasm": "tx_transfer.cdb2edbaa152608e2ae6d96a586955b911b74cc097228fee2bb5768f5f6db0ea.wasm", + "tx_unbond.wasm": "tx_unbond.0e6319063d74311a42a405435c15aa867894fc0a7ae209c08fc5fa9eee4013c5.wasm", + "tx_unjail_validator.wasm": "tx_unjail_validator.24e345f69f62fc8b75c475eee66e3252dc06a5468411f6770096b1b8ced8f022.wasm", + "tx_update_vp.wasm": "tx_update_vp.86123368b829488aface2804afc397a8c26873e9f319e3183fb211b292239e1d.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.80586902bae368088edfdef22a6fcc25aee8779620fd48eabbe1f3f11754d701.wasm", + "tx_withdraw.wasm": "tx_withdraw.5648dac90b0f25c94d60fb39dfed6120f6eed8794e4c3dbf554af36adf7bafc3.wasm", + "vp_implicit.wasm": "vp_implicit.18f2b20fa776493a0182b05e95b3eb0676be702dc0a4d39a09cb9ab51d116146.wasm", + "vp_masp.wasm": "vp_masp.b07f344c2d760ee69dfcef1ae4e703f63c7c6594a377a48550ef576f9fcaca96.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.9e3c9d133e52d4b762b9fc7d3bcc1e7d0ad2bc4d1754d390ed31555471f184d5.wasm", + "vp_user.wasm": "vp_user.df279909cb2de9d5125bbfe8b34b14ff3b924ed7db61dcc93cbb06f2f3f29509.wasm", + "vp_validator.wasm": "vp_validator.e3ed90196c676b73bcfa2f4142e2831b306eef56bde73243b2d50ddc5ab8e2b2.wasm" } \ No newline at end of file From 7198560a672913c12d9a8f113e0ec034c3b06d1d Mon Sep 17 00:00:00 2001 From: satan Date: Mon, 24 Jul 2023 23:50:54 +0200 Subject: [PATCH 16/28] [chore]: rebasing on bat/generic-io --- apps/src/lib/client/rpc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index 2f01716ec3..c358582f6a 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -50,7 +50,7 @@ use namada::types::masp::{BalanceOwner, ExtendedViewingKey, PaymentAddress}; use namada::types::storage::{BlockHeight, BlockResults, Epoch, Key, KeySeg}; use namada::types::token::{Change, MaspDenom}; use namada::types::{storage, token}; -use namada::{display, display_line, edisplay_lin, prompt}; +use namada::{display, display_line, edisplay_line, prompt}; use tokio::time::Instant; use crate::cli::{self, args}; From 46fe66b4e049a19264687e87516eb2931057286c Mon Sep 17 00:00:00 2001 From: satan Date: Wed, 26 Jul 2023 13:41:01 +0200 Subject: [PATCH 17/28] [fix]: Fixing downstream async io changes from bat/generic-io --- apps/src/lib/client/rpc.rs | 3 ++- apps/src/lib/node/ledger/shell/testing/utils.rs | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index c358582f6a..cfd361a947 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -456,7 +456,8 @@ pub async fn query_pinned_balance< } // If a suitable viewing key was not found, then demand it from the user if balance == pinned_error { - let vk_str = prompt!(IO, "Enter the viewing key for {}: ", owner); + let vk_str = + prompt!(IO, "Enter the viewing key for {}: ", owner).await; let fvk = match ExtendedViewingKey::from_str(vk_str.trim()) { Ok(fvk) => fvk, _ => { diff --git a/apps/src/lib/node/ledger/shell/testing/utils.rs b/apps/src/lib/node/ledger/shell/testing/utils.rs index 95da7efb85..3ff3bdb7d3 100644 --- a/apps/src/lib/node/ledger/shell/testing/utils.rs +++ b/apps/src/lib/node/ledger/shell/testing/utils.rs @@ -68,6 +68,7 @@ lazy_static! { pub struct TestingIo; +#[async_trait::async_trait(?Send)] impl Io for TestingIo { fn print(output: impl AsRef) { let mut testout = TESTOUT.lock().unwrap(); @@ -107,11 +108,11 @@ impl Io for TestingIo { eprintln!("{}", output.as_ref()); } - fn read() -> std::io::Result { + async fn read() -> std::io::Result { read_aux(TESTIN.lock().unwrap().as_slice()) } - fn prompt(question: impl AsRef) -> String { + async fn prompt(question: impl AsRef) -> String { prompt_aux( TESTIN.lock().unwrap().as_slice(), std::io::stdout(), From 9208268bd4ce488274bc17c74a0d6f44e4785018 Mon Sep 17 00:00:00 2001 From: satan Date: Fri, 28 Jul 2023 13:16:44 +0200 Subject: [PATCH 18/28] [feat]: After rebasing on bat/generic-io, need to make io read methods async for integration testing --- .../lib/node/ledger/shell/testing/utils.rs | 43 +++++++++++++++---- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/testing/utils.rs b/apps/src/lib/node/ledger/shell/testing/utils.rs index 3ff3bdb7d3..0161f86416 100644 --- a/apps/src/lib/node/ledger/shell/testing/utils.rs +++ b/apps/src/lib/node/ledger/shell/testing/utils.rs @@ -1,8 +1,12 @@ +use std::ops::{Deref, DerefMut}; use std::path::{Path, PathBuf}; +use std::pin::Pin; +use std::task::{Context, Poll}; use lazy_static::lazy_static; use namada::types::io::{prompt_aux, read_aux, Io}; use tempfile::tempdir; +use tokio::io::{AsyncRead, ReadBuf}; /// Namada binaries #[derive(Debug)] @@ -62,8 +66,8 @@ lazy_static! { lazy_static! { /// A replacement for stdin in testing. - pub static ref TESTIN: std::sync::Arc>> = - std::sync::Arc::new(std::sync::Mutex::new(vec![])); + pub static ref TESTIN: AtomicBuffer = + AtomicBuffer(std::sync::Arc::new(std::sync::Mutex::new(vec![]))); } pub struct TestingIo; @@ -108,16 +112,12 @@ impl Io for TestingIo { eprintln!("{}", output.as_ref()); } - async fn read() -> std::io::Result { - read_aux(TESTIN.lock().unwrap().as_slice()) + async fn read() -> tokio::io::Result { + read_aux(&*TESTIN).await } async fn prompt(question: impl AsRef) -> String { - prompt_aux( - TESTIN.lock().unwrap().as_slice(), - std::io::stdout(), - question.as_ref(), - ) + prompt_aux(&*TESTIN, tokio::io::stdout(), question.as_ref()).await } } @@ -230,6 +230,31 @@ impl FixedBuffer { } } +pub struct AtomicBuffer(std::sync::Arc>>); + +impl Deref for AtomicBuffer { + type Target = std::sync::Arc>>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'a> AsyncRead for &'a AtomicBuffer { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut ReadBuf<'_>, + ) -> Poll> { + let mut inner = self.lock().unwrap(); + let buf_before = buf.filled().len(); + let res = AsyncRead::poll_read(Pin::new(&mut inner.as_slice()), cx, buf); + let amount_read = buf.filled().len() - buf_before; + *inner.deref_mut() = inner[amount_read..].to_vec(); + res + } +} + #[cfg(test)] mod testing { use super::*; From b6e925fe38770cab311f028dc4db0bfb71cb1986 Mon Sep 17 00:00:00 2001 From: satan Date: Fri, 28 Jul 2023 13:18:13 +0200 Subject: [PATCH 19/28] [chore]: Formatting --- apps/src/lib/node/ledger/shell/testing/utils.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/src/lib/node/ledger/shell/testing/utils.rs b/apps/src/lib/node/ledger/shell/testing/utils.rs index 0161f86416..bfcb7f50ab 100644 --- a/apps/src/lib/node/ledger/shell/testing/utils.rs +++ b/apps/src/lib/node/ledger/shell/testing/utils.rs @@ -248,7 +248,8 @@ impl<'a> AsyncRead for &'a AtomicBuffer { ) -> Poll> { let mut inner = self.lock().unwrap(); let buf_before = buf.filled().len(); - let res = AsyncRead::poll_read(Pin::new(&mut inner.as_slice()), cx, buf); + let res = + AsyncRead::poll_read(Pin::new(&mut inner.as_slice()), cx, buf); let amount_read = buf.filled().len() - buf_before; *inner.deref_mut() = inner[amount_read..].to_vec(); res From 9d06bbd6f43ef3c6b23f728e88b36f0bea5946af Mon Sep 17 00:00:00 2001 From: satan Date: Fri, 1 Sep 2023 10:21:34 +0200 Subject: [PATCH 20/28] [chore]: merge in v0.21.1 --- wasm/checksums.json | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/wasm/checksums.json b/wasm/checksums.json index adf06b5afe..71b5580e4a 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,21 +1,21 @@ { - "tx_bond.wasm": "tx_bond.3d9341191f3059b478435c52ec0db4b19fe9d2b7a60ac7371287fb15552dbe3b.wasm", - "tx_bridge_pool.wasm": "tx_bridge_pool.0d8d4de6222aeb5b0cb80fa39b1ce1ee9e2f50115ffdaa2a1e79eabdddeb6bfe.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.349170accac147e8b4b77eb10ded0421936176b09d8913246235db0c5a4fac83.wasm", - "tx_ibc.wasm": "tx_ibc.5ddb7f3032d8b59446486a7fb20bac8c43c5a002f6e9b3309ff2e01048b98c7f.wasm", - "tx_init_account.wasm": "tx_init_account.f254fa819c85694d01f440132690ac54b3a8911a5eca9ccb49bc6171cf219017.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.1cf4ac19672bc368ff1e8ea0fd31507ac4a9a33c1b21ff80d85de8b36c6bce66.wasm", - "tx_init_validator.wasm": "tx_init_validator.bd5aa6513bf890e4591413fe6a4345dc28a0767ebc2fad2279e851fb1373091a.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.8f252e6fd0ab74be7f7afb4dbe4ba2b3d441011fd57ce5ccd06fe24109728019.wasm", - "tx_transfer.wasm": "tx_transfer.cdb2edbaa152608e2ae6d96a586955b911b74cc097228fee2bb5768f5f6db0ea.wasm", - "tx_unbond.wasm": "tx_unbond.0e6319063d74311a42a405435c15aa867894fc0a7ae209c08fc5fa9eee4013c5.wasm", - "tx_unjail_validator.wasm": "tx_unjail_validator.24e345f69f62fc8b75c475eee66e3252dc06a5468411f6770096b1b8ced8f022.wasm", - "tx_update_vp.wasm": "tx_update_vp.86123368b829488aface2804afc397a8c26873e9f319e3183fb211b292239e1d.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.80586902bae368088edfdef22a6fcc25aee8779620fd48eabbe1f3f11754d701.wasm", - "tx_withdraw.wasm": "tx_withdraw.5648dac90b0f25c94d60fb39dfed6120f6eed8794e4c3dbf554af36adf7bafc3.wasm", - "vp_implicit.wasm": "vp_implicit.18f2b20fa776493a0182b05e95b3eb0676be702dc0a4d39a09cb9ab51d116146.wasm", - "vp_masp.wasm": "vp_masp.b07f344c2d760ee69dfcef1ae4e703f63c7c6594a377a48550ef576f9fcaca96.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.9e3c9d133e52d4b762b9fc7d3bcc1e7d0ad2bc4d1754d390ed31555471f184d5.wasm", - "vp_user.wasm": "vp_user.df279909cb2de9d5125bbfe8b34b14ff3b924ed7db61dcc93cbb06f2f3f29509.wasm", - "vp_validator.wasm": "vp_validator.e3ed90196c676b73bcfa2f4142e2831b306eef56bde73243b2d50ddc5ab8e2b2.wasm" + "tx_bond.wasm": "tx_bond.0967203d10b78f7f28c3e1d674c0ff3ec25ec00e9b3a4010648c5cc381ad52ec.wasm", + "tx_bridge_pool.wasm": "tx_bridge_pool.c6dd30e79edff950016f495cee946bc6c0d6847f89322fbf733c89bb768c1fd4.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.1e89cf7f091a324872582c1092f2bda662be21040ac796d8aca08d08e9332a3e.wasm", + "tx_ibc.wasm": "tx_ibc.2b35a3a35049fe267121906176bafc0ea2b221a185fe31652216f93da66209e7.wasm", + "tx_init_account.wasm": "tx_init_account.6e77218b385a386f49c65c13b6112c75dcb06c50cf7bf7a5a479e7e8b7b458fa.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.d0c969c6b7a3d262d82586c630f29a00e587653fa9df1cf17021914764d4d3ab.wasm", + "tx_init_validator.wasm": "tx_init_validator.df780e2cc9ab4a5726931b662fd75cad907e495745aec1c466877878cee1ae3a.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.062b11c679a35e50f7fcfca95e3ada01bc89098591ae92526cfafaab5adf954e.wasm", + "tx_transfer.wasm": "tx_transfer.5f9e5a2f85fa5c1ec2bbb0aa9bebaba6bddc7e777ca956f5d980ad1cc9af3e7e.wasm", + "tx_unbond.wasm": "tx_unbond.dc169426d36f2bcfd82d976e77e2f3beb85ac98e75652ddf8367fa081fb17b3d.wasm", + "tx_unjail_validator.wasm": "tx_unjail_validator.23d1b075f28d5fa3ee413ee4db31792a2714d9b21929cbf1ba59832c3adec886.wasm", + "tx_update_account.wasm": "tx_update_account.88646c9fb5d3327701d69b12ce49ae35431ebe2ea3691900874cad66791f1ca9.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.c9abcd6f37d59daf9a39e30dcebd9e0789d5bcd679f5931ed7899073d0a5d36f.wasm", + "tx_withdraw.wasm": "tx_withdraw.02ac0399bc5b745346641facd968607931bf9abf67d7ed336b6b0540360f2d43.wasm", + "vp_implicit.wasm": "vp_implicit.1d212280efc3fccc16de9497dae8cc1670f21f0e0ce4c3bd9fc58fc816037cdf.wasm", + "vp_masp.wasm": "vp_masp.1d264f4ba602df546011ae4040da00bd0b103c6bad62ee5de985940e8bf0c2e7.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.d279129573a4f0625f411c7b147ac94bc40e0db2ea04b777f29e57be33a53522.wasm", + "vp_user.wasm": "vp_user.788b8039744b73bd6fe2b67b273802bae4c26cd117092eb8437f455d5aa95dbd.wasm", + "vp_validator.wasm": "vp_validator.98457b70476c76a16b1cd28232bacf043438bfd5e054bc9f7651ef1747e48594.wasm" } \ No newline at end of file From bdfaff4a0b5e4154576c955f96bfb743d6f213cc Mon Sep 17 00:00:00 2001 From: satan Date: Mon, 24 Jul 2023 17:00:05 +0200 Subject: [PATCH 21/28] [feat]: Refactored the masp integration tests to no longer need the nightly feature --- Cargo.lock | 10 +- apps/src/lib/cli.rs | 2 +- apps/src/lib/cli/utils.rs | 51 ----- apps/src/lib/client/rpc.rs | 7 +- .../lib/node/ledger/shell/testing/client.rs | 12 +- .../lib/node/ledger/shell/testing/utils.rs | 197 ++++++++++++++++++ shared/src/types/io.rs | 9 + tests/src/integration.rs | 1 - tests/src/integration/masp.rs | 3 +- tests/src/integration/utils.rs | 83 -------- tests/src/lib.rs | 2 - wasm/checksums.json | 40 ++-- 12 files changed, 241 insertions(+), 176 deletions(-) delete mode 100644 tests/src/integration/utils.rs diff --git a/Cargo.lock b/Cargo.lock index 143043ddcb..e100cceb45 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3485,7 +3485,7 @@ checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" dependencies = [ "hermit-abi 0.3.1", "io-lifetimes", - "rustix 0.37.13", + "rustix 0.37.1", "windows-sys 0.48.0", ] @@ -5851,16 +5851,16 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.13" +version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79bef90eb6d984c72722595b5b1348ab39275a5e5123faca6863bf07d75a4e0" +checksum = "d4790277f605573dd24b6751701e0823582a63c7cafc095e427e6c66e45dd75e" dependencies = [ "bitflags 1.2.1", "errno", "io-lifetimes", "libc", "linux-raw-sys 0.3.7", - "windows-sys 0.48.0", + "windows-sys 0.45.0", ] [[package]] @@ -6623,7 +6623,7 @@ dependencies = [ "cfg-if 1.0.0", "fastrand", "redox_syscall 0.3.5", - "rustix 0.37.13", + "rustix 0.37.1", "windows-sys 0.45.0", ] diff --git a/apps/src/lib/cli.rs b/apps/src/lib/cli.rs index 1565e8ee21..677a048fe0 100644 --- a/apps/src/lib/cli.rs +++ b/apps/src/lib/cli.rs @@ -17,7 +17,7 @@ use clap::{ArgGroup, ArgMatches, ColorChoice}; use color_eyre::eyre::Result; use namada::types::io::DefaultIo; use utils::*; -pub use utils::{dispatch_prompt, safe_exit, Cmd, TESTIN}; +pub use utils::{safe_exit, Cmd}; pub use self::context::Context; use crate::cli::api::CliIo; diff --git a/apps/src/lib/cli/utils.rs b/apps/src/lib/cli/utils.rs index 4ae8e4adbc..26cc38ff7f 100644 --- a/apps/src/lib/cli/utils.rs +++ b/apps/src/lib/cli/utils.rs @@ -6,7 +6,6 @@ use std::str::FromStr; use clap::{ArgAction, ArgMatches}; use color_eyre::eyre::Result; -use lazy_static::lazy_static; use super::args; use super::context::{Context, FromContext}; @@ -363,53 +362,3 @@ pub fn safe_exit(_: i32) -> ! { panic!("Test failed because the client exited unexpectedly.") } - -lazy_static! { - /// A replacement for stdin in testing. - pub static ref TESTIN: std::sync::Arc>> = - std::sync::Arc::new(std::sync::Mutex::new(vec![])); -} - -/// A generic function for displaying a prompt to users and reading -/// in their response. -fn prompt_aux(mut reader: R, mut writer: W, question: &str) -> String -where - R: std::io::Read, - W: Write, -{ - write!(&mut writer, "{}", question).expect("Unable to write"); - writer.flush().unwrap(); - let mut s = String::new(); - reader.read_to_string(&mut s).expect("Unable to read"); - s -} - -/// A function that chooses how to dispatch prompts -/// to users. There is a hierarchy of feature flags -/// that determines this. If no flags are set, -/// the question is printed to stdout and response -/// read from stdin. -pub fn dispatch_prompt(question: impl AsRef) -> String { - if cfg!(feature = "testing") { - prompt_aux( - TESTIN.lock().unwrap().as_slice(), - std::io::stdout(), - question.as_ref(), - ) - } else { - prompt_aux( - std::io::stdin().lock(), - std::io::stdout(), - question.as_ref(), - ) - } -} - -#[macro_export] -/// A convenience macro for formatting the user prompt before -/// forwarding it to the `[dispatch_prompt]` method. -macro_rules! prompt { - ($($arg:tt)*) => {{ - $crate::cli::dispatch_prompt(format!("{}", format_args!($($arg)*))) - }} -} diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index a1e97cfb23..a2777cd82e 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -52,13 +52,12 @@ use namada::types::masp::{BalanceOwner, ExtendedViewingKey, PaymentAddress}; use namada::types::storage::{BlockHeight, BlockResults, Epoch, Key, KeySeg}; use namada::types::token::{Change, MaspDenom}; use namada::types::{error, storage, token}; -use namada::{display, display_line, edisplay_line}; +use namada::{display, display_line, edisplay_line, prompt}; use tokio::time::Instant; use crate::cli::{self, args}; use crate::facade::tendermint::merkle::proof::Proof; use crate::facade::tendermint_rpc::error::Error as TError; -use crate::prompt; use crate::wallet::CliWalletUtils; /// Query the status of a given transaction. @@ -461,8 +460,8 @@ pub async fn query_pinned_balance< } } // If a suitable viewing key was not found, then demand it from the user - if is_pinned_error(&balance) { - let vk_str = prompt!("Enter the viewing key for {}: ", owner); + if balance == pinned_error { + let vk_str = prompt!(IO, "Enter the viewing key for {}: ", owner); let fvk = match ExtendedViewingKey::from_str(vk_str.trim()) { Ok(fvk) => fvk, _ => { diff --git a/apps/src/lib/node/ledger/shell/testing/client.rs b/apps/src/lib/node/ledger/shell/testing/client.rs index 68a346a4ed..9ebc825f54 100644 --- a/apps/src/lib/node/ledger/shell/testing/client.rs +++ b/apps/src/lib/node/ledger/shell/testing/client.rs @@ -3,14 +3,14 @@ use std::ops::ControlFlow; use clap::Command as App; use eyre::Report; use namada::types::control_flow::Halt; -use namada::types::io::{DefaultIo, Io}; +use namada::types::io::Io; use tendermint_config::net::Address as TendermintAddress; use super::node::MockNode; use crate::cli::api::{CliApi, CliClient}; use crate::cli::args::Global; use crate::cli::{args, cmds, Cmd, Context, NamadaClient, NamadaRelayer}; -use crate::node::ledger::shell::testing::utils::Bin; +use crate::node::ledger::shell::testing::utils::{Bin, TestingIo}; pub fn run( node: &MockNode, @@ -25,7 +25,7 @@ pub fn run( wasm_dir: Some(locked.wasm_dir.clone()), } }; - let ctx = Context::new::(global.clone())?; + let ctx = Context::new::(global.clone())?; let rt = tokio::runtime::Runtime::new().unwrap(); match who { @@ -47,7 +47,7 @@ pub fn run( NamadaClient::WithoutContext(sub_cmd, global) } }; - rt.block_on(CliApi::::handle_client_command( + rt.block_on(CliApi::::handle_client_command( Some(node), cmd, )) @@ -60,7 +60,7 @@ pub fn run( let cmd = cmds::NamadaWallet::parse(&matches) .expect("Could not parse wallet command"); - CliApi::::handle_wallet_command(cmd, ctx) + CliApi::::handle_wallet_command(cmd, ctx) } Bin::Relayer => { args.insert(0, "relayer"); @@ -82,7 +82,7 @@ pub fn run( NamadaRelayer::ValidatorSet(sub_cmd) } }; - rt.block_on(CliApi::::handle_relayer_command( + rt.block_on(CliApi::::handle_relayer_command( Some(node), cmd, )) diff --git a/apps/src/lib/node/ledger/shell/testing/utils.rs b/apps/src/lib/node/ledger/shell/testing/utils.rs index e66ead21e7..95da7efb85 100644 --- a/apps/src/lib/node/ledger/shell/testing/utils.rs +++ b/apps/src/lib/node/ledger/shell/testing/utils.rs @@ -1,5 +1,7 @@ use std::path::{Path, PathBuf}; +use lazy_static::lazy_static; +use namada::types::io::{prompt_aux, read_aux, Io}; use tempfile::tempdir; /// Namada binaries @@ -46,3 +48,198 @@ impl Default for TestDir { Self::new() } } + +/// The max number of bytes that the is currently remembered from stdout while +/// testing. +const TESTOUT_BUF_SIZE: usize = 100_000; + +lazy_static! { + /// A replacement for stdout in testing. The maximum number of bytes + /// it holds is limited to prevent memory issues. + pub static ref TESTOUT: std::sync::Arc>> = + std::sync::Arc::new(std::sync::Mutex::new(FixedBuffer::new(TESTOUT_BUF_SIZE))); +} + +lazy_static! { + /// A replacement for stdin in testing. + pub static ref TESTIN: std::sync::Arc>> = + std::sync::Arc::new(std::sync::Mutex::new(vec![])); +} + +pub struct TestingIo; + +impl Io for TestingIo { + fn print(output: impl AsRef) { + let mut testout = TESTOUT.lock().unwrap(); + testout.append(output.as_ref().as_bytes().to_vec()); + print!("{}", output.as_ref()); + } + + fn println(output: impl AsRef) { + let mut testout = TESTOUT.lock().unwrap(); + let mut bytes = output.as_ref().as_bytes().to_vec(); + bytes.extend_from_slice("\n".as_bytes()); + testout.append(bytes); + println!("{}", output.as_ref()); + } + + fn write( + _: W, + output: impl AsRef, + ) -> std::io::Result<()> { + Self::print(output); + Ok(()) + } + + fn writeln( + _: W, + output: impl AsRef, + ) -> std::io::Result<()> { + Self::println(output); + Ok(()) + } + + fn eprintln(output: impl AsRef) { + let mut testout = TESTOUT.lock().unwrap(); + let mut bytes = output.as_ref().as_bytes().to_vec(); + bytes.extend_from_slice("\n".as_bytes()); + testout.append(bytes); + eprintln!("{}", output.as_ref()); + } + + fn read() -> std::io::Result { + read_aux(TESTIN.lock().unwrap().as_slice()) + } + + fn prompt(question: impl AsRef) -> String { + prompt_aux( + TESTIN.lock().unwrap().as_slice(), + std::io::stdout(), + question.as_ref(), + ) + } +} + +/// Test helper that captures stdout of +/// a process. +pub struct CapturedOutput { + pub output: String, + pub result: T, + input: String, +} + +impl CapturedOutput { + pub fn with_input(input: String) -> Self { + Self { + output: "".to_string(), + result: (), + input, + } + } +} + +impl CapturedOutput { + /// Run a client command and capture + /// the output to the mocked stdout. + pub fn of(func: F) -> Self + where + F: FnOnce() -> T, + { + let mut capture = Self { + output: Default::default(), + result: func(), + input: Default::default(), + }; + capture.output = TESTOUT.lock().unwrap().read_string(); + capture + } + + /// Run a client command with input to the mocked stdin and capture + /// the output to the mocked stdout + pub fn run(&self, func: F) -> CapturedOutput + where + F: FnOnce() -> U, + { + { + // write the input to the mocked stdin + let mut buf = TESTIN.lock().unwrap(); + buf.clear(); + buf.extend_from_slice(self.input.as_bytes()); + } + CapturedOutput::of(func) + } + + /// Check if the captured output contains the regex. + pub fn matches(&self, needle: regex::Regex) -> bool { + needle.captures(&self.output).is_some() + } + + /// Check if the captured output contains the string. + pub fn contains(&self, needle: &str) -> bool { + let needle = regex::Regex::new(needle).unwrap(); + self.matches(needle) + } +} + +/// A buffer with a max size. Drops elements from the front on +/// size overflow. +pub struct FixedBuffer { + inner: Vec, + max_size: usize, +} + +impl FixedBuffer { + fn new(max_size: usize) -> Self { + Self { + inner: vec![], + max_size, + } + } + + /// Remove the first `size` elements from the buffer. + fn roll(&mut self, size: usize) { + self.inner = self.inner[size..].to_vec(); + } + + /// Add data to the end of the buffer, deleting from the + /// front as necessary. + fn append(&mut self, mut other: Vec) { + // if new data exceeds max size, take the tail. + if other.len() > self.max_size { + self.inner = other[(other.len() - self.max_size)..].to_vec(); + return; + } + // check if appending the data overflows buffer + let free_space = self.max_size - self.inner.len(); + if other.len() > free_space { + // delete the minimum amount of data from the front of the buffer + // to fit new data. + self.roll(other.len() - free_space); + } + self.inner.append(&mut other); + } +} + +impl FixedBuffer { + /// Read the inner buffer out to string + pub fn read_string(&mut self) -> String { + let mut fresh = vec![]; + std::mem::swap(&mut fresh, &mut self.inner); + String::from_utf8(fresh).unwrap() + } +} + +#[cfg(test)] +mod testing { + use super::*; + + #[test] + fn test_buffer() { + let mut buffer = FixedBuffer::::new(10); + buffer.inner = (1u64..=9_u64).collect(); + buffer.append(vec![10, 11, 12, 13, 14, 15]); + assert_eq!(buffer.inner, (6u64..=15_u64).collect::>()); + buffer.append((20u64..=40_u64).collect()); + assert_eq!(buffer.inner, (31u64..=40_u64).collect::>()); + } +} diff --git a/shared/src/types/io.rs b/shared/src/types/io.rs index 93d45a75ff..13798cc5b2 100644 --- a/shared/src/types/io.rs +++ b/shared/src/types/io.rs @@ -121,3 +121,12 @@ macro_rules! edisplay_line { <$io>::eprintln(format_args!($($args)*).to_string()) }; } + +#[macro_export] +/// A convenience macro for formatting the user prompt before +/// forwarding it to the [`Io::prompt`] method. +macro_rules! prompt { + ($io:ty,$($arg:tt)*) => {{ + <$io>::prompt(format!("{}", format_args!($($arg)*))) + }} +} diff --git a/tests/src/integration.rs b/tests/src/integration.rs index 8642e0e03c..1a7c84dbfb 100644 --- a/tests/src/integration.rs +++ b/tests/src/integration.rs @@ -1,3 +1,2 @@ mod masp; mod setup; -mod utils; diff --git a/tests/src/integration/masp.rs b/tests/src/integration/masp.rs index 02d6dabc8f..8c55910b0a 100644 --- a/tests/src/integration/masp.rs +++ b/tests/src/integration/masp.rs @@ -5,7 +5,7 @@ use color_eyre::owo_colors::OwoColorize; use namada::types::io::DefaultIo; use namada_apps::client::tx::CLIShieldedUtils; use namada_apps::node::ledger::shell::testing::client::run; -use namada_apps::node::ledger::shell::testing::utils::Bin; +use namada_apps::node::ledger::shell::testing::utils::{Bin, CapturedOutput}; use namada_core::types::address::{btc, eth, masp_rewards}; use namada_core::types::token; use namada_core::types::token::{DenominatedAmount, NATIVE_MAX_DECIMAL_PLACES}; @@ -17,7 +17,6 @@ use crate::e2e::setup::constants::{ AC_PAYMENT_ADDRESS, AC_VIEWING_KEY, ALBERT, A_SPENDING_KEY, BB_PAYMENT_ADDRESS, BERTHA, BTC, B_SPENDING_KEY, CHRISTEL, ETH, MASP, NAM, }; -use crate::integration::utils::CapturedOutput; /// In this test we verify that users of the MASP receive the correct rewards /// for leaving their assets in the pool for varying periods of time. diff --git a/tests/src/integration/utils.rs b/tests/src/integration/utils.rs deleted file mode 100644 index f626a001ee..0000000000 --- a/tests/src/integration/utils.rs +++ /dev/null @@ -1,83 +0,0 @@ -use std::fs::File; -use std::path::PathBuf; -use std::sync::Arc; - -struct TempFile(PathBuf); -impl TempFile { - fn new(path: PathBuf) -> (Self, File) { - let f = File::create(&path).unwrap(); - (Self(path), f) - } -} - -impl Drop for TempFile { - fn drop(&mut self) { - _ = std::fs::remove_file(&self.0); - } -} - -/// Test helper that captures stdout of -/// a process. -pub struct CapturedOutput { - pub output: String, - pub result: T, - input: String, -} - -impl CapturedOutput { - pub fn with_input(input: String) -> Self { - Self { - output: "".to_string(), - result: (), - input, - } - } -} - -impl CapturedOutput { - /// Run a client command and capture - /// the output to the mocked stdout. - pub(crate) fn of(func: F) -> Self - where - F: FnOnce() -> T, - { - std::io::set_output_capture(Some(Default::default())); - let mut capture = Self { - output: Default::default(), - result: func(), - input: Default::default(), - }; - let captured = std::io::set_output_capture(None); - let captured = captured.unwrap(); - let captured = Arc::try_unwrap(captured).unwrap(); - let captured = captured.into_inner().unwrap(); - capture.output = String::from_utf8(captured).unwrap(); - capture - } - - /// Run a client command with input to the mocked stdin and capture - /// the output to the mocked stdout - pub fn run(&self, func: F) -> CapturedOutput - where - F: FnOnce() -> U, - { - { - // write the input to the mocked stdin - let mut buf = namada_apps::cli::TESTIN.lock().unwrap(); - buf.clear(); - buf.extend_from_slice(self.input.as_bytes()); - } - CapturedOutput::of(func) - } - - /// Check if the captured output contains the regex. - pub fn matches(&self, needle: regex::Regex) -> bool { - needle.captures(&self.output).is_some() - } - - /// Check if the captured output contains the string. - pub fn contains(&self, needle: &str) -> bool { - let needle = regex::Regex::new(needle).unwrap(); - self.matches(needle) - } -} diff --git a/tests/src/lib.rs b/tests/src/lib.rs index 7e35947477..1d9958a30a 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -1,4 +1,3 @@ -#![cfg_attr(test, feature(internal_output_capture))] //! Namada integrations and WASM tests and testing helpers. #![doc(html_favicon_url = "https://dev.namada.net/master/favicon.png")] @@ -13,7 +12,6 @@ pub use vm_host_env::{ibc, tx, vp}; #[cfg(test)] mod e2e; #[cfg(test)] -#[allow(dead_code)] mod integration; pub mod native_vp; pub mod storage; diff --git a/wasm/checksums.json b/wasm/checksums.json index 7ddad4f479..adf06b5afe 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,23 +1,21 @@ { - "tx_bond.wasm": "tx_bond.65775f3faf6f0ff1bbfa8bd7f171dd1d591d83536a4eac3016c2a460471b7fa7.wasm", - "tx_bridge_pool.wasm": "tx_bridge_pool.2e664c7d28bf61b04a826a43532c35c3982e4f5eb9fbf5159888b4e6a1f540a6.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.c3f55f00b0a58b9f78703f8b96893839aa771610b670aeeef4e9db55a00ec1bc.wasm", - "tx_ibc.wasm": "tx_ibc.27f6f51add11129c6845000e76ad5fcef3151f3c3339464ee1bb367ea2654981.wasm", - "tx_init_account.wasm": "tx_init_account.6bd6d908fb6e88ea91777d59c30e4ed0de0cd682101098966703bed7d9fbf58b.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.d7b84245eab1529041367d7109594ca4496b6d7425cf4c9f4bfb69951cf2f5f3.wasm", - "tx_init_validator.wasm": "tx_init_validator.f8f5c1512b4f117ac4bd7f1860799265e1b671739ea3e712e85ed4c8063594d1.wasm", - "tx_resign_steward.wasm": "tx_resign_steward.3460f267f3bad1de285d2cd53f1eecda29b44eefb2f7dee0df9d059f778670c0.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.214dbad89fecd6897d41db962688fd5320ed50a805025eef857d47761fb30088.wasm", - "tx_transfer.wasm": "tx_transfer.e452dbee5ffadc6f4df60a7adbf638592b94da8fdbcf5767f09170b71dee68f0.wasm", - "tx_unbond.wasm": "tx_unbond.4082bf7da70105c1c248ee403762d2cacb1ce15bf1e7da7cd82e800fae7b05fb.wasm", - "tx_unjail_validator.wasm": "tx_unjail_validator.1dfe798b2bffcfcafd0439cfea7b40ad7a3380dc02d424ecf07de35d6219f55e.wasm", - "tx_update_account.wasm": "tx_update_account.22ad3f7a3afafe8cb3aedd6d4fc210b93208221795fdd76626e803b972935176.wasm", - "tx_update_steward_commission.wasm": "tx_update_steward_commission.eb59360509115e7ec537ba49f7e4438a4522b1929c78d7b644af1025479594a3.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.ca1171723084845931b994535631b05a843b5db0d54b52a484653a932de704db.wasm", - "tx_withdraw.wasm": "tx_withdraw.2505b1c659e910b078f8c618c10659645e96c9ae0aac55c3adbdb05a7e7622d1.wasm", - "vp_implicit.wasm": "vp_implicit.d46e9d52909cd365203bedd007effd12fe17e50a5f25151d827e4cc05cee0cca.wasm", - "vp_masp.wasm": "vp_masp.0dda4af5fb350cb2d4c795c51abe150f0df772a4d335540846cc5dffd8880928.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.54a30792965ebdb8476f0cdcae4e374350653baa69dd1c0e2e1ffde4e496169c.wasm", - "vp_user.wasm": "vp_user.c5c935be6353c1ea08c012af3850dd7819ee58f81b2c8ffaf7826020b1fb41d6.wasm", - "vp_validator.wasm": "vp_validator.f91045a01992002574796e598c611ac71ad57bb17061467b049596cb0c756ecc.wasm" + "tx_bond.wasm": "tx_bond.3d9341191f3059b478435c52ec0db4b19fe9d2b7a60ac7371287fb15552dbe3b.wasm", + "tx_bridge_pool.wasm": "tx_bridge_pool.0d8d4de6222aeb5b0cb80fa39b1ce1ee9e2f50115ffdaa2a1e79eabdddeb6bfe.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.349170accac147e8b4b77eb10ded0421936176b09d8913246235db0c5a4fac83.wasm", + "tx_ibc.wasm": "tx_ibc.5ddb7f3032d8b59446486a7fb20bac8c43c5a002f6e9b3309ff2e01048b98c7f.wasm", + "tx_init_account.wasm": "tx_init_account.f254fa819c85694d01f440132690ac54b3a8911a5eca9ccb49bc6171cf219017.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.1cf4ac19672bc368ff1e8ea0fd31507ac4a9a33c1b21ff80d85de8b36c6bce66.wasm", + "tx_init_validator.wasm": "tx_init_validator.bd5aa6513bf890e4591413fe6a4345dc28a0767ebc2fad2279e851fb1373091a.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.8f252e6fd0ab74be7f7afb4dbe4ba2b3d441011fd57ce5ccd06fe24109728019.wasm", + "tx_transfer.wasm": "tx_transfer.cdb2edbaa152608e2ae6d96a586955b911b74cc097228fee2bb5768f5f6db0ea.wasm", + "tx_unbond.wasm": "tx_unbond.0e6319063d74311a42a405435c15aa867894fc0a7ae209c08fc5fa9eee4013c5.wasm", + "tx_unjail_validator.wasm": "tx_unjail_validator.24e345f69f62fc8b75c475eee66e3252dc06a5468411f6770096b1b8ced8f022.wasm", + "tx_update_vp.wasm": "tx_update_vp.86123368b829488aface2804afc397a8c26873e9f319e3183fb211b292239e1d.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.80586902bae368088edfdef22a6fcc25aee8779620fd48eabbe1f3f11754d701.wasm", + "tx_withdraw.wasm": "tx_withdraw.5648dac90b0f25c94d60fb39dfed6120f6eed8794e4c3dbf554af36adf7bafc3.wasm", + "vp_implicit.wasm": "vp_implicit.18f2b20fa776493a0182b05e95b3eb0676be702dc0a4d39a09cb9ab51d116146.wasm", + "vp_masp.wasm": "vp_masp.b07f344c2d760ee69dfcef1ae4e703f63c7c6594a377a48550ef576f9fcaca96.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.9e3c9d133e52d4b762b9fc7d3bcc1e7d0ad2bc4d1754d390ed31555471f184d5.wasm", + "vp_user.wasm": "vp_user.df279909cb2de9d5125bbfe8b34b14ff3b924ed7db61dcc93cbb06f2f3f29509.wasm", + "vp_validator.wasm": "vp_validator.e3ed90196c676b73bcfa2f4142e2831b306eef56bde73243b2d50ddc5ab8e2b2.wasm" } \ No newline at end of file From bbfb810009ac75a35009966a9ab4849680a6b05e Mon Sep 17 00:00:00 2001 From: satan Date: Wed, 26 Jul 2023 13:41:01 +0200 Subject: [PATCH 22/28] [fix]: Fixing downstream async io changes from bat/generic-io --- apps/src/lib/client/rpc.rs | 3 ++- apps/src/lib/node/ledger/shell/testing/utils.rs | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index a2777cd82e..cd7aeb2b56 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -461,7 +461,8 @@ pub async fn query_pinned_balance< } // If a suitable viewing key was not found, then demand it from the user if balance == pinned_error { - let vk_str = prompt!(IO, "Enter the viewing key for {}: ", owner); + let vk_str = + prompt!(IO, "Enter the viewing key for {}: ", owner).await; let fvk = match ExtendedViewingKey::from_str(vk_str.trim()) { Ok(fvk) => fvk, _ => { diff --git a/apps/src/lib/node/ledger/shell/testing/utils.rs b/apps/src/lib/node/ledger/shell/testing/utils.rs index 95da7efb85..3ff3bdb7d3 100644 --- a/apps/src/lib/node/ledger/shell/testing/utils.rs +++ b/apps/src/lib/node/ledger/shell/testing/utils.rs @@ -68,6 +68,7 @@ lazy_static! { pub struct TestingIo; +#[async_trait::async_trait(?Send)] impl Io for TestingIo { fn print(output: impl AsRef) { let mut testout = TESTOUT.lock().unwrap(); @@ -107,11 +108,11 @@ impl Io for TestingIo { eprintln!("{}", output.as_ref()); } - fn read() -> std::io::Result { + async fn read() -> std::io::Result { read_aux(TESTIN.lock().unwrap().as_slice()) } - fn prompt(question: impl AsRef) -> String { + async fn prompt(question: impl AsRef) -> String { prompt_aux( TESTIN.lock().unwrap().as_slice(), std::io::stdout(), From 4cdcd6c93367bb2f0521eafd6d6a1da1d2f873b2 Mon Sep 17 00:00:00 2001 From: satan Date: Fri, 28 Jul 2023 13:16:44 +0200 Subject: [PATCH 23/28] [feat]: After rebasing on bat/generic-io, need to make io read methods async for integration testing --- .../lib/node/ledger/shell/testing/utils.rs | 43 +++++++++++++++---- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/apps/src/lib/node/ledger/shell/testing/utils.rs b/apps/src/lib/node/ledger/shell/testing/utils.rs index 3ff3bdb7d3..0161f86416 100644 --- a/apps/src/lib/node/ledger/shell/testing/utils.rs +++ b/apps/src/lib/node/ledger/shell/testing/utils.rs @@ -1,8 +1,12 @@ +use std::ops::{Deref, DerefMut}; use std::path::{Path, PathBuf}; +use std::pin::Pin; +use std::task::{Context, Poll}; use lazy_static::lazy_static; use namada::types::io::{prompt_aux, read_aux, Io}; use tempfile::tempdir; +use tokio::io::{AsyncRead, ReadBuf}; /// Namada binaries #[derive(Debug)] @@ -62,8 +66,8 @@ lazy_static! { lazy_static! { /// A replacement for stdin in testing. - pub static ref TESTIN: std::sync::Arc>> = - std::sync::Arc::new(std::sync::Mutex::new(vec![])); + pub static ref TESTIN: AtomicBuffer = + AtomicBuffer(std::sync::Arc::new(std::sync::Mutex::new(vec![]))); } pub struct TestingIo; @@ -108,16 +112,12 @@ impl Io for TestingIo { eprintln!("{}", output.as_ref()); } - async fn read() -> std::io::Result { - read_aux(TESTIN.lock().unwrap().as_slice()) + async fn read() -> tokio::io::Result { + read_aux(&*TESTIN).await } async fn prompt(question: impl AsRef) -> String { - prompt_aux( - TESTIN.lock().unwrap().as_slice(), - std::io::stdout(), - question.as_ref(), - ) + prompt_aux(&*TESTIN, tokio::io::stdout(), question.as_ref()).await } } @@ -230,6 +230,31 @@ impl FixedBuffer { } } +pub struct AtomicBuffer(std::sync::Arc>>); + +impl Deref for AtomicBuffer { + type Target = std::sync::Arc>>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'a> AsyncRead for &'a AtomicBuffer { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut ReadBuf<'_>, + ) -> Poll> { + let mut inner = self.lock().unwrap(); + let buf_before = buf.filled().len(); + let res = AsyncRead::poll_read(Pin::new(&mut inner.as_slice()), cx, buf); + let amount_read = buf.filled().len() - buf_before; + *inner.deref_mut() = inner[amount_read..].to_vec(); + res + } +} + #[cfg(test)] mod testing { use super::*; From 52446311d7e1dd71e117934b898869b5b1a2c94c Mon Sep 17 00:00:00 2001 From: satan Date: Fri, 28 Jul 2023 13:18:13 +0200 Subject: [PATCH 24/28] [chore]: Formatting --- apps/src/lib/node/ledger/shell/testing/utils.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/src/lib/node/ledger/shell/testing/utils.rs b/apps/src/lib/node/ledger/shell/testing/utils.rs index 0161f86416..bfcb7f50ab 100644 --- a/apps/src/lib/node/ledger/shell/testing/utils.rs +++ b/apps/src/lib/node/ledger/shell/testing/utils.rs @@ -248,7 +248,8 @@ impl<'a> AsyncRead for &'a AtomicBuffer { ) -> Poll> { let mut inner = self.lock().unwrap(); let buf_before = buf.filled().len(); - let res = AsyncRead::poll_read(Pin::new(&mut inner.as_slice()), cx, buf); + let res = + AsyncRead::poll_read(Pin::new(&mut inner.as_slice()), cx, buf); let amount_read = buf.filled().len() - buf_before; *inner.deref_mut() = inner[amount_read..].to_vec(); res From e9a613af2cafe2b9690595f33fc4fdeb9091a249 Mon Sep 17 00:00:00 2001 From: satan Date: Fri, 1 Sep 2023 10:21:34 +0200 Subject: [PATCH 25/28] [chore]: merge in v0.21.1 --- wasm/checksums.json | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/wasm/checksums.json b/wasm/checksums.json index adf06b5afe..71b5580e4a 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,21 +1,21 @@ { - "tx_bond.wasm": "tx_bond.3d9341191f3059b478435c52ec0db4b19fe9d2b7a60ac7371287fb15552dbe3b.wasm", - "tx_bridge_pool.wasm": "tx_bridge_pool.0d8d4de6222aeb5b0cb80fa39b1ce1ee9e2f50115ffdaa2a1e79eabdddeb6bfe.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.349170accac147e8b4b77eb10ded0421936176b09d8913246235db0c5a4fac83.wasm", - "tx_ibc.wasm": "tx_ibc.5ddb7f3032d8b59446486a7fb20bac8c43c5a002f6e9b3309ff2e01048b98c7f.wasm", - "tx_init_account.wasm": "tx_init_account.f254fa819c85694d01f440132690ac54b3a8911a5eca9ccb49bc6171cf219017.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.1cf4ac19672bc368ff1e8ea0fd31507ac4a9a33c1b21ff80d85de8b36c6bce66.wasm", - "tx_init_validator.wasm": "tx_init_validator.bd5aa6513bf890e4591413fe6a4345dc28a0767ebc2fad2279e851fb1373091a.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.8f252e6fd0ab74be7f7afb4dbe4ba2b3d441011fd57ce5ccd06fe24109728019.wasm", - "tx_transfer.wasm": "tx_transfer.cdb2edbaa152608e2ae6d96a586955b911b74cc097228fee2bb5768f5f6db0ea.wasm", - "tx_unbond.wasm": "tx_unbond.0e6319063d74311a42a405435c15aa867894fc0a7ae209c08fc5fa9eee4013c5.wasm", - "tx_unjail_validator.wasm": "tx_unjail_validator.24e345f69f62fc8b75c475eee66e3252dc06a5468411f6770096b1b8ced8f022.wasm", - "tx_update_vp.wasm": "tx_update_vp.86123368b829488aface2804afc397a8c26873e9f319e3183fb211b292239e1d.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.80586902bae368088edfdef22a6fcc25aee8779620fd48eabbe1f3f11754d701.wasm", - "tx_withdraw.wasm": "tx_withdraw.5648dac90b0f25c94d60fb39dfed6120f6eed8794e4c3dbf554af36adf7bafc3.wasm", - "vp_implicit.wasm": "vp_implicit.18f2b20fa776493a0182b05e95b3eb0676be702dc0a4d39a09cb9ab51d116146.wasm", - "vp_masp.wasm": "vp_masp.b07f344c2d760ee69dfcef1ae4e703f63c7c6594a377a48550ef576f9fcaca96.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.9e3c9d133e52d4b762b9fc7d3bcc1e7d0ad2bc4d1754d390ed31555471f184d5.wasm", - "vp_user.wasm": "vp_user.df279909cb2de9d5125bbfe8b34b14ff3b924ed7db61dcc93cbb06f2f3f29509.wasm", - "vp_validator.wasm": "vp_validator.e3ed90196c676b73bcfa2f4142e2831b306eef56bde73243b2d50ddc5ab8e2b2.wasm" + "tx_bond.wasm": "tx_bond.0967203d10b78f7f28c3e1d674c0ff3ec25ec00e9b3a4010648c5cc381ad52ec.wasm", + "tx_bridge_pool.wasm": "tx_bridge_pool.c6dd30e79edff950016f495cee946bc6c0d6847f89322fbf733c89bb768c1fd4.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.1e89cf7f091a324872582c1092f2bda662be21040ac796d8aca08d08e9332a3e.wasm", + "tx_ibc.wasm": "tx_ibc.2b35a3a35049fe267121906176bafc0ea2b221a185fe31652216f93da66209e7.wasm", + "tx_init_account.wasm": "tx_init_account.6e77218b385a386f49c65c13b6112c75dcb06c50cf7bf7a5a479e7e8b7b458fa.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.d0c969c6b7a3d262d82586c630f29a00e587653fa9df1cf17021914764d4d3ab.wasm", + "tx_init_validator.wasm": "tx_init_validator.df780e2cc9ab4a5726931b662fd75cad907e495745aec1c466877878cee1ae3a.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.062b11c679a35e50f7fcfca95e3ada01bc89098591ae92526cfafaab5adf954e.wasm", + "tx_transfer.wasm": "tx_transfer.5f9e5a2f85fa5c1ec2bbb0aa9bebaba6bddc7e777ca956f5d980ad1cc9af3e7e.wasm", + "tx_unbond.wasm": "tx_unbond.dc169426d36f2bcfd82d976e77e2f3beb85ac98e75652ddf8367fa081fb17b3d.wasm", + "tx_unjail_validator.wasm": "tx_unjail_validator.23d1b075f28d5fa3ee413ee4db31792a2714d9b21929cbf1ba59832c3adec886.wasm", + "tx_update_account.wasm": "tx_update_account.88646c9fb5d3327701d69b12ce49ae35431ebe2ea3691900874cad66791f1ca9.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.c9abcd6f37d59daf9a39e30dcebd9e0789d5bcd679f5931ed7899073d0a5d36f.wasm", + "tx_withdraw.wasm": "tx_withdraw.02ac0399bc5b745346641facd968607931bf9abf67d7ed336b6b0540360f2d43.wasm", + "vp_implicit.wasm": "vp_implicit.1d212280efc3fccc16de9497dae8cc1670f21f0e0ce4c3bd9fc58fc816037cdf.wasm", + "vp_masp.wasm": "vp_masp.1d264f4ba602df546011ae4040da00bd0b103c6bad62ee5de985940e8bf0c2e7.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.d279129573a4f0625f411c7b147ac94bc40e0db2ea04b777f29e57be33a53522.wasm", + "vp_user.wasm": "vp_user.788b8039744b73bd6fe2b67b273802bae4c26cd117092eb8437f455d5aa95dbd.wasm", + "vp_validator.wasm": "vp_validator.98457b70476c76a16b1cd28232bacf043438bfd5e054bc9f7651ef1747e48594.wasm" } \ No newline at end of file From 2e31a7d0da25a6b256ba1bba4ccc1a4044ab254b Mon Sep 17 00:00:00 2001 From: satan Date: Mon, 24 Jul 2023 17:00:05 +0200 Subject: [PATCH 26/28] [feat]: Refactored the masp integration tests to no longer need the nightly feature --- wasm/checksums.json | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/wasm/checksums.json b/wasm/checksums.json index 71b5580e4a..adf06b5afe 100644 --- a/wasm/checksums.json +++ b/wasm/checksums.json @@ -1,21 +1,21 @@ { - "tx_bond.wasm": "tx_bond.0967203d10b78f7f28c3e1d674c0ff3ec25ec00e9b3a4010648c5cc381ad52ec.wasm", - "tx_bridge_pool.wasm": "tx_bridge_pool.c6dd30e79edff950016f495cee946bc6c0d6847f89322fbf733c89bb768c1fd4.wasm", - "tx_change_validator_commission.wasm": "tx_change_validator_commission.1e89cf7f091a324872582c1092f2bda662be21040ac796d8aca08d08e9332a3e.wasm", - "tx_ibc.wasm": "tx_ibc.2b35a3a35049fe267121906176bafc0ea2b221a185fe31652216f93da66209e7.wasm", - "tx_init_account.wasm": "tx_init_account.6e77218b385a386f49c65c13b6112c75dcb06c50cf7bf7a5a479e7e8b7b458fa.wasm", - "tx_init_proposal.wasm": "tx_init_proposal.d0c969c6b7a3d262d82586c630f29a00e587653fa9df1cf17021914764d4d3ab.wasm", - "tx_init_validator.wasm": "tx_init_validator.df780e2cc9ab4a5726931b662fd75cad907e495745aec1c466877878cee1ae3a.wasm", - "tx_reveal_pk.wasm": "tx_reveal_pk.062b11c679a35e50f7fcfca95e3ada01bc89098591ae92526cfafaab5adf954e.wasm", - "tx_transfer.wasm": "tx_transfer.5f9e5a2f85fa5c1ec2bbb0aa9bebaba6bddc7e777ca956f5d980ad1cc9af3e7e.wasm", - "tx_unbond.wasm": "tx_unbond.dc169426d36f2bcfd82d976e77e2f3beb85ac98e75652ddf8367fa081fb17b3d.wasm", - "tx_unjail_validator.wasm": "tx_unjail_validator.23d1b075f28d5fa3ee413ee4db31792a2714d9b21929cbf1ba59832c3adec886.wasm", - "tx_update_account.wasm": "tx_update_account.88646c9fb5d3327701d69b12ce49ae35431ebe2ea3691900874cad66791f1ca9.wasm", - "tx_vote_proposal.wasm": "tx_vote_proposal.c9abcd6f37d59daf9a39e30dcebd9e0789d5bcd679f5931ed7899073d0a5d36f.wasm", - "tx_withdraw.wasm": "tx_withdraw.02ac0399bc5b745346641facd968607931bf9abf67d7ed336b6b0540360f2d43.wasm", - "vp_implicit.wasm": "vp_implicit.1d212280efc3fccc16de9497dae8cc1670f21f0e0ce4c3bd9fc58fc816037cdf.wasm", - "vp_masp.wasm": "vp_masp.1d264f4ba602df546011ae4040da00bd0b103c6bad62ee5de985940e8bf0c2e7.wasm", - "vp_testnet_faucet.wasm": "vp_testnet_faucet.d279129573a4f0625f411c7b147ac94bc40e0db2ea04b777f29e57be33a53522.wasm", - "vp_user.wasm": "vp_user.788b8039744b73bd6fe2b67b273802bae4c26cd117092eb8437f455d5aa95dbd.wasm", - "vp_validator.wasm": "vp_validator.98457b70476c76a16b1cd28232bacf043438bfd5e054bc9f7651ef1747e48594.wasm" + "tx_bond.wasm": "tx_bond.3d9341191f3059b478435c52ec0db4b19fe9d2b7a60ac7371287fb15552dbe3b.wasm", + "tx_bridge_pool.wasm": "tx_bridge_pool.0d8d4de6222aeb5b0cb80fa39b1ce1ee9e2f50115ffdaa2a1e79eabdddeb6bfe.wasm", + "tx_change_validator_commission.wasm": "tx_change_validator_commission.349170accac147e8b4b77eb10ded0421936176b09d8913246235db0c5a4fac83.wasm", + "tx_ibc.wasm": "tx_ibc.5ddb7f3032d8b59446486a7fb20bac8c43c5a002f6e9b3309ff2e01048b98c7f.wasm", + "tx_init_account.wasm": "tx_init_account.f254fa819c85694d01f440132690ac54b3a8911a5eca9ccb49bc6171cf219017.wasm", + "tx_init_proposal.wasm": "tx_init_proposal.1cf4ac19672bc368ff1e8ea0fd31507ac4a9a33c1b21ff80d85de8b36c6bce66.wasm", + "tx_init_validator.wasm": "tx_init_validator.bd5aa6513bf890e4591413fe6a4345dc28a0767ebc2fad2279e851fb1373091a.wasm", + "tx_reveal_pk.wasm": "tx_reveal_pk.8f252e6fd0ab74be7f7afb4dbe4ba2b3d441011fd57ce5ccd06fe24109728019.wasm", + "tx_transfer.wasm": "tx_transfer.cdb2edbaa152608e2ae6d96a586955b911b74cc097228fee2bb5768f5f6db0ea.wasm", + "tx_unbond.wasm": "tx_unbond.0e6319063d74311a42a405435c15aa867894fc0a7ae209c08fc5fa9eee4013c5.wasm", + "tx_unjail_validator.wasm": "tx_unjail_validator.24e345f69f62fc8b75c475eee66e3252dc06a5468411f6770096b1b8ced8f022.wasm", + "tx_update_vp.wasm": "tx_update_vp.86123368b829488aface2804afc397a8c26873e9f319e3183fb211b292239e1d.wasm", + "tx_vote_proposal.wasm": "tx_vote_proposal.80586902bae368088edfdef22a6fcc25aee8779620fd48eabbe1f3f11754d701.wasm", + "tx_withdraw.wasm": "tx_withdraw.5648dac90b0f25c94d60fb39dfed6120f6eed8794e4c3dbf554af36adf7bafc3.wasm", + "vp_implicit.wasm": "vp_implicit.18f2b20fa776493a0182b05e95b3eb0676be702dc0a4d39a09cb9ab51d116146.wasm", + "vp_masp.wasm": "vp_masp.b07f344c2d760ee69dfcef1ae4e703f63c7c6594a377a48550ef576f9fcaca96.wasm", + "vp_testnet_faucet.wasm": "vp_testnet_faucet.9e3c9d133e52d4b762b9fc7d3bcc1e7d0ad2bc4d1754d390ed31555471f184d5.wasm", + "vp_user.wasm": "vp_user.df279909cb2de9d5125bbfe8b34b14ff3b924ed7db61dcc93cbb06f2f3f29509.wasm", + "vp_validator.wasm": "vp_validator.e3ed90196c676b73bcfa2f4142e2831b306eef56bde73243b2d50ddc5ab8e2b2.wasm" } \ No newline at end of file From 17358c393475e91adda62d8bda5a263c3dd2d8a5 Mon Sep 17 00:00:00 2001 From: satan Date: Wed, 13 Sep 2023 14:10:58 +0200 Subject: [PATCH 27/28] [chore] Merging in v0.22 --- apps/src/lib/client/rpc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/src/lib/client/rpc.rs b/apps/src/lib/client/rpc.rs index cd7aeb2b56..c074f42315 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -460,7 +460,7 @@ pub async fn query_pinned_balance< } } // If a suitable viewing key was not found, then demand it from the user - if balance == pinned_error { + if is_pinned_error(&balance) { let vk_str = prompt!(IO, "Enter the viewing key for {}: ", owner).await; let fvk = match ExtendedViewingKey::from_str(vk_str.trim()) { From 40ab00aa0870bae7321c87b30cd5c18ccf846dbc Mon Sep 17 00:00:00 2001 From: satan Date: Tue, 19 Sep 2023 10:48:31 +0200 Subject: [PATCH 28/28] [fix]: Fixed removing tokio dep from wasm targeted builds --- Cargo.lock | 6 +++--- shared/src/types/io.rs | 30 +++++++++++++++++++++++++++--- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e100cceb45..f6099f4871 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4026,14 +4026,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] diff --git a/shared/src/types/io.rs b/shared/src/types/io.rs index 13798cc5b2..462dbef95f 100644 --- a/shared/src/types/io.rs +++ b/shared/src/types/io.rs @@ -42,18 +42,41 @@ pub trait Io { eprintln!("{}", output.as_ref()); } - async fn read() -> tokio::io::Result { - read_aux(tokio::io::stdin()).await + async fn read() -> std::io::Result { + #[cfg(not(target_family = "wasm"))] + { + read_aux(tokio::io::stdin()).await + } + #[cfg(target_family = "wasm")] + { + unreachable!("Wasm should not perform general IO") + } } async fn prompt(question: impl AsRef) -> String { - prompt_aux(tokio::io::stdin(), tokio::io::stdout(), question.as_ref()) + #[cfg(not(target_family = "wasm"))] + { + prompt_aux( + tokio::io::stdin(), + tokio::io::stdout(), + question.as_ref(), + ) .await + } + #[cfg(target_family = "wasm")] + { + unreachable!( + "Wasm should not perform general IO; received call for input \ + with question\n: {}", + question.as_ref() + ) + } } } /// A generic function for displaying a prompt to users and reading /// in their response. +#[cfg(not(target_family = "wasm"))] pub async fn prompt_aux( mut reader: R, mut writer: W, @@ -74,6 +97,7 @@ where } /// A generic function for reading input from users +#[cfg(not(target_family = "wasm"))] pub async fn read_aux(mut reader: R) -> tokio::io::Result where R: tokio::io::AsyncReadExt + Unpin,