diff --git a/Cargo.lock b/Cargo.lock index 143043ddcb..f6099f4871 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", ] @@ -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]] @@ -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 b2a52fc6e6..4d760d60c1 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 0ec3e597e6..d750ccc759 100644 --- a/apps/src/lib/client/rpc.rs +++ b/apps/src/lib/client/rpc.rs @@ -52,14 +52,13 @@ use namada::types::key::*; 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::types::{storage, token}; +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. @@ -463,7 +462,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); + 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/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..bfcb7f50ab 100644 --- a/apps/src/lib/node/ledger/shell/testing/utils.rs +++ b/apps/src/lib/node/ledger/shell/testing/utils.rs @@ -1,6 +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)] @@ -46,3 +52,221 @@ 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: AtomicBuffer = + AtomicBuffer(std::sync::Arc::new(std::sync::Mutex::new(vec![]))); +} + +pub struct TestingIo; + +#[async_trait::async_trait(?Send)] +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()); + } + + async fn read() -> tokio::io::Result { + read_aux(&*TESTIN).await + } + + async fn prompt(question: impl AsRef) -> String { + prompt_aux(&*TESTIN, tokio::io::stdout(), question.as_ref()).await + } +} + +/// 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() + } +} + +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::*; + + #[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/sdk/signing.rs b/shared/src/sdk/signing.rs index d4bc0b4dc6..8f8085b1e7 100644 --- a/shared/src/sdk/signing.rs +++ b/shared/src/sdk/signing.rs @@ -27,7 +27,6 @@ use crate::ibc::applications::transfer::msgs::transfer::MsgTransfer; use crate::ibc_proto::google::protobuf::Any; use crate::ledger::parameters::storage as parameter_storage; use crate::proto::{MaspBuilder, Section, Tx}; -use crate::sdk::{args, rpc}; use crate::sdk::error::{EncodingError, Error, TxError}; use crate::sdk::masp::{ make_asset_type, ShieldedContext, ShieldedTransfer, ShieldedUtils, @@ -43,6 +42,7 @@ use crate::sdk::tx::{ }; pub use crate::sdk::wallet::store::AddressVpType; use crate::sdk::wallet::{Wallet, WalletUtils}; +use crate::sdk::{args, rpc}; use crate::types::io::*; use crate::types::key::*; use crate::types::masp::{ExtendedViewingKey, PaymentAddress}; diff --git a/shared/src/sdk/tx.rs b/shared/src/sdk/tx.rs index 5d6d82bfda..6b7626bd0f 100644 --- a/shared/src/sdk/tx.rs +++ b/shared/src/sdk/tx.rs @@ -393,8 +393,7 @@ where ); let parsed = { - let wrapper_query = - rpc::TxEventQuery::Accepted(wrapper_hash.as_str()); + let wrapper_query = rpc::TxEventQuery::Accepted(wrapper_hash.as_str()); let event = rpc::query_tx_status::<_, IO>(client, wrapper_query, deadline) .await diff --git a/shared/src/types/io.rs b/shared/src/types/io.rs index 93d45a75ff..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, @@ -121,3 +145,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