use std::collections::BTreeMap;
use std::path::PathBuf;
use std::str::FromStr;
use color_eyre::eyre::Result;
use color_eyre::owo_colors::OwoColorize;
use itertools::Either;
use masp_primitives::convert::AllowedConversion;
use masp_primitives::merkle_tree::CommitmentTree;
use masp_primitives::sapling::Node;
use masp_primitives::transaction::components::I128Sum;
use namada_apps_lib::wallet::defaults::{
get_unencrypted_keypair, is_use_device,
};
use namada_core::address::Address;
use namada_core::dec::Dec;
use namada_core::masp::{MaspTxId, Precision, TokenMap, encode_asset_type};
use namada_node::shell::testing::client::run;
use namada_node::shell::testing::node::NodeResults;
use namada_node::shell::testing::utils::{Bin, CapturedOutput};
use namada_sdk::account::AccountPublicKeysMap;
use namada_sdk::masp::fs::FsShieldedUtils;
use namada_sdk::signing::SigningTxData;
use namada_sdk::state::{StorageRead, StorageWrite};
use namada_sdk::time::DateTimeUtc;
use namada_sdk::token::storage_key::{
masp_base_native_precision_key, masp_conversion_key,
masp_reward_precision_key, masp_scheduled_base_native_precision_key,
masp_scheduled_reward_precision_key, masp_token_map_key,
};
use namada_sdk::token::{self, Amount, DenominatedAmount, MaspEpoch};
use namada_sdk::tx::{Section, Tx};
use namada_sdk::{DEFAULT_GAS_LIMIT, tx};
use test_log::test;
use super::{helpers, setup};
use crate::e2e::setup::apply_use_device;
use crate::e2e::setup::constants::{
A_SPENDING_KEY, AA_PAYMENT_ADDRESS, AA_VIEWING_KEY, AB_PAYMENT_ADDRESS,
AB_VIEWING_KEY, AC_PAYMENT_ADDRESS, AC_VIEWING_KEY, ALBERT, ALBERT_KEY,
B_SPENDING_KEY, BB_PAYMENT_ADDRESS, BERTHA, BERTHA_KEY, BTC,
C_SPENDING_KEY, CHRISTEL, CHRISTEL_KEY, ETH, FRANK_KEY, MASP, NAM,
};
use crate::integration::helpers::make_temp_account;
use crate::strings::TX_APPLIED_SUCCESS;
#[test]
fn init_null_rewards() -> Result<()> {
const RPC: &str = "http://127.0.0.1:26567";
const TEST_TOKEN_ADDR: &str =
"tnam1q9382etwdaekg6tpwdkkzar0wd5ku6r0wvu5ukqd";
let test_token_addr: Address = TEST_TOKEN_ADDR.parse().unwrap();
let _ = FsShieldedUtils::new(PathBuf::new());
let (mut node, _services) = setup::setup()?;
token::write_denom(
&mut node.shell.lock().unwrap().state,
&test_token_addr,
0u8.into(),
)?;
let bertha_addr = helpers::find_address(&node, BERTHA)?;
token::credit_tokens(
&mut node.shell.lock().unwrap().state,
&test_token_addr,
&bertha_addr,
Amount::from_u64(1_000_000_000u64),
)?;
node.finalize_and_commit(None);
assert_eq!(
token::read_total_supply(
&node.shell.lock().unwrap().state,
&test_token_addr,
)?,
Amount::from_u64(1_000_000_000u64),
);
token::write_params(
&Some(token::ShieldedParams {
max_reward_rate: Dec::from_str("0").unwrap(),
kp_gain_nom: Dec::from_str("0").unwrap(),
kd_gain_nom: Dec::from_str("0").unwrap(),
locked_amount_target: 0,
}),
&mut node.shell.lock().unwrap().state,
&test_token_addr,
&0u8.into(),
)?;
let mut token_map =
token::read_token_map(&node.shell.lock().unwrap().state)?;
token_map.insert("TEST".to_owned(), test_token_addr.clone());
token::write_token_map(&mut node.shell.lock().unwrap().state, token_map)?;
node.finalize_and_commit(None);
node.next_masp_epoch();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
BERTHA,
"--target",
AA_PAYMENT_ADDRESS,
"--token",
TEST_TOKEN_ADDR,
"--amount",
"1000000",
"--node",
RPC,
]),
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
"--node",
RPC,
],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
TEST_TOKEN_ADDR,
"--node",
RPC,
],
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(&format!("{TEST_TOKEN_ADDR}: 1000000")));
for _ in 0..3 {
node.next_masp_epoch();
}
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
RPC,
],
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains("nam: 0"));
token::write_params(
&Some(token::ShieldedParams {
max_reward_rate: Dec::from_str("1.0").unwrap(),
kp_gain_nom: Dec::from_str("9999999999").unwrap(),
kd_gain_nom: Dec::from_str("9999999999").unwrap(),
locked_amount_target: 999999999u64,
}),
&mut node.shell.lock().unwrap().state,
&test_token_addr,
&0u8.into(),
)?;
node.finalize_and_commit(None);
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
RPC,
],
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains("nam: 0"));
node.next_masp_epoch();
const EXPECTED_REWARDS: u128 = 7;
const UNSHIELD_REWARDS_AMT: u128 = EXPECTED_REWARDS / 2;
const REMAINING_REWARDS_AMT: u128 = EXPECTED_REWARDS - UNSHIELD_REWARDS_AMT;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
RPC,
],
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(&format!("nam: {EXPECTED_REWARDS}")));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"unshield",
"--source",
A_SPENDING_KEY,
"--target",
BERTHA,
"--token",
NAM,
"--amount",
&UNSHIELD_REWARDS_AMT.to_string(),
"--signing-keys",
BERTHA_KEY,
"--node",
RPC,
]),
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
"--node",
RPC,
],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
RPC,
],
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(&format!("nam: {REMAINING_REWARDS_AMT}")));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"transfer",
"--source",
A_SPENDING_KEY,
"--target",
AB_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
&REMAINING_REWARDS_AMT.to_string(),
"--signing-keys",
BERTHA_KEY,
"--node",
RPC,
]),
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
"--node",
RPC,
],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
RPC,
],
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains("nam: 0"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"unshield",
"--source",
A_SPENDING_KEY,
"--target",
BERTHA,
"--token",
TEST_TOKEN_ADDR,
"--amount",
"500000",
"--signing-keys",
BERTHA_KEY,
"--node",
RPC,
]),
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
"--node",
RPC,
],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
TEST_TOKEN_ADDR,
"--node",
RPC,
],
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(&format!("{TEST_TOKEN_ADDR}: 500000")));
Ok(())
}
#[test]
fn values_spanning_multiple_masp_digits() -> Result<()> {
const RPC: &str = "http://127.0.0.1:26567";
const TEST_TOKEN_ADDR: &str =
"tnam1q9382etwdaekg6tpwdkkzar0wd5ku6r0wvu5ukqd";
let test_token_addr: Address = TEST_TOKEN_ADDR.parse().unwrap();
const TEST_TOKEN_INITIAL_SUPPLY: &str = "6427858447239330000000";
const HALF_TEST_TOKEN_INITIAL_SUPPLY: &str = "3213929223619665000000";
let test_token_initial_supply = {
let supply: DenominatedAmount =
TEST_TOKEN_INITIAL_SUPPLY.parse().unwrap();
assert_eq!(supply.denom(), 0u8.into());
supply.amount()
};
let _ = FsShieldedUtils::new(PathBuf::new());
let (mut node, _services) = setup::setup()?;
token::write_denom(
&mut node.shell.lock().unwrap().state,
&test_token_addr,
0u8.into(),
)?;
let bertha_addr = helpers::find_address(&node, BERTHA)?;
token::credit_tokens(
&mut node.shell.lock().unwrap().state,
&test_token_addr,
&bertha_addr,
test_token_initial_supply,
)?;
node.finalize_and_commit(None);
assert_eq!(
token::read_total_supply(
&node.shell.lock().unwrap().state,
&test_token_addr,
)?,
test_token_initial_supply,
);
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
BERTHA,
"--target",
AA_PAYMENT_ADDRESS,
"--token",
TEST_TOKEN_ADDR,
"--amount",
HALF_TEST_TOKEN_INITIAL_SUPPLY,
"--node",
RPC,
]),
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
"--node",
RPC,
],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
TEST_TOKEN_ADDR,
"--node",
RPC,
],
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(&format!(
"{TEST_TOKEN_ADDR}: {HALF_TEST_TOKEN_INITIAL_SUPPLY}"
)));
for _ in 0..3 {
node.next_masp_epoch();
}
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
RPC,
],
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains("nam: 0"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"unshield",
"--source",
A_SPENDING_KEY,
"--target",
BERTHA,
"--token",
TEST_TOKEN_ADDR,
"--amount",
HALF_TEST_TOKEN_INITIAL_SUPPLY,
"--signing-keys",
BERTHA_KEY,
"--node",
RPC,
]),
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
"--node",
RPC,
],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
TEST_TOKEN_ADDR,
"--node",
RPC,
],
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(&format!("{TEST_TOKEN_ADDR}: 0")));
token::write_params(
&Some(token::ShieldedParams {
max_reward_rate: Dec::from_str("999999999999999.0").unwrap(),
kp_gain_nom: Dec::from_str("99999999999999999999").unwrap(),
kd_gain_nom: Dec::from_str("99999999999999999999").unwrap(),
locked_amount_target: u64::MAX,
}),
&mut node.shell.lock().unwrap().state,
&test_token_addr,
&0u8.into(),
)?;
let mut token_map =
token::read_token_map(&node.shell.lock().unwrap().state)?;
token_map.insert("TEST".to_owned(), test_token_addr.clone());
token::write_token_map(&mut node.shell.lock().unwrap().state, token_map)?;
node.finalize_and_commit(None);
node.next_masp_epoch();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
BERTHA,
"--target",
AA_PAYMENT_ADDRESS,
"--token",
TEST_TOKEN_ADDR,
"--amount",
HALF_TEST_TOKEN_INITIAL_SUPPLY,
"--node",
RPC,
]),
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
"--node",
RPC,
],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
TEST_TOKEN_ADDR,
"--node",
RPC,
],
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(&format!(
"{TEST_TOKEN_ADDR}: {HALF_TEST_TOKEN_INITIAL_SUPPLY}"
)));
for _ in 0..3 {
node.next_masp_epoch();
}
const EXPECTED_REWARDS: u128 = 6427858447239330;
const UNSHIELD_REWARDS_AMT: u128 = EXPECTED_REWARDS / 2;
const REMAINING_REWARDS_AMT: u128 = EXPECTED_REWARDS - UNSHIELD_REWARDS_AMT;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
RPC,
],
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(&format!("nam: {EXPECTED_REWARDS}")));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"unshield",
"--source",
A_SPENDING_KEY,
"--target",
BERTHA,
"--token",
NAM,
"--amount",
&UNSHIELD_REWARDS_AMT.to_string(),
"--signing-keys",
BERTHA_KEY,
"--node",
RPC,
"--gas-limit",
"65000",
]),
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
"--node",
RPC,
],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
RPC,
],
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(&format!("nam: {REMAINING_REWARDS_AMT}")));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
BERTHA_KEY,
"--target",
AC_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"1",
"--gas-payer",
BERTHA_KEY,
"--node",
RPC,
]),
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
AC_VIEWING_KEY,
"--node",
RPC,
],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AC_VIEWING_KEY,
"--token",
NAM,
"--node",
RPC,
],
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains("nam: 1"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"unshield",
"--source",
A_SPENDING_KEY,
"--target",
BERTHA,
"--token",
NAM,
"--amount",
&REMAINING_REWARDS_AMT.to_string(),
"--node",
RPC,
"--gas-spending-key",
C_SPENDING_KEY,
"--gas-limit",
"65000",
]),
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
AC_VIEWING_KEY,
"--node",
RPC,
],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
RPC,
],
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains("nam: 0"));
Ok(())
}
#[test]
fn enable_rewards_after_shielding() -> Result<()> {
const RPC: &str = "http://127.0.0.1:26567";
const TEST_TOKEN_ADDR: &str =
"tnam1q9382etwdaekg6tpwdkkzar0wd5ku6r0wvu5ukqd";
let test_token_addr: Address = TEST_TOKEN_ADDR.parse().unwrap();
let _ = FsShieldedUtils::new(PathBuf::new());
let (mut node, _services) = setup::setup()?;
token::write_denom(
&mut node.shell.lock().unwrap().state,
&test_token_addr,
0u8.into(),
)?;
let bertha_addr = helpers::find_address(&node, BERTHA)?;
token::credit_tokens(
&mut node.shell.lock().unwrap().state,
&test_token_addr,
&bertha_addr,
Amount::from_u64(1_000_000_000u64),
)?;
node.finalize_and_commit(None);
assert_eq!(
token::read_total_supply(
&node.shell.lock().unwrap().state,
&test_token_addr,
)?,
Amount::from_u64(1_000_000_000u64),
);
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
BERTHA,
"--target",
AA_PAYMENT_ADDRESS,
"--token",
TEST_TOKEN_ADDR,
"--amount",
"1000000",
"--node",
RPC,
]),
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
"--node",
RPC,
],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
TEST_TOKEN_ADDR,
"--node",
RPC,
],
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(&format!("{TEST_TOKEN_ADDR}: 1000000")));
for _ in 0..3 {
node.next_masp_epoch();
}
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
TEST_TOKEN_ADDR,
"--node",
RPC,
],
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(&format!("{TEST_TOKEN_ADDR}: 1000000")));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
RPC,
],
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains("nam: 0"));
token::write_params(
&Some(token::ShieldedParams {
max_reward_rate: Dec::from_str("1.0").unwrap(),
kp_gain_nom: Dec::from_str("9999999999").unwrap(),
kd_gain_nom: Dec::from_str("9999999999").unwrap(),
locked_amount_target: 999999999u64,
}),
&mut node.shell.lock().unwrap().state,
&test_token_addr,
&0u8.into(),
)?;
let mut token_map =
token::read_token_map(&node.shell.lock().unwrap().state)?;
token_map.insert("TEST".to_owned(), test_token_addr.clone());
token::write_token_map(&mut node.shell.lock().unwrap().state, token_map)?;
node.finalize_and_commit(None);
for _ in 0..3 {
node.next_masp_epoch();
}
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
TEST_TOKEN_ADDR,
"--node",
RPC,
],
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(&format!("{TEST_TOKEN_ADDR}: 1000000")));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
RPC,
],
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains("nam: 0"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"unshield",
"--source",
A_SPENDING_KEY,
"--target",
BERTHA,
"--token",
TEST_TOKEN_ADDR,
"--amount",
"1000000",
"--signing-keys",
BERTHA_KEY,
"--node",
RPC,
]),
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
"--node",
RPC,
],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
TEST_TOKEN_ADDR,
"--node",
RPC,
],
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(&format!("{TEST_TOKEN_ADDR}: 0")));
node.next_masp_epoch();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
BERTHA,
"--target",
AA_PAYMENT_ADDRESS,
"--token",
TEST_TOKEN_ADDR,
"--amount",
"1000000",
"--node",
RPC,
]),
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
"--node",
RPC,
],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
TEST_TOKEN_ADDR,
"--node",
RPC,
],
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(&format!("{TEST_TOKEN_ADDR}: 1000000")));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
RPC,
],
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains("nam: 0"));
for _ in 0..3 {
node.next_masp_epoch();
}
const EXPECTED_REWARDS: u128 = 21;
const UNSHIELD_REWARDS_AMT: u128 = EXPECTED_REWARDS / 2;
const REMAINING_REWARDS_AMT: u128 = EXPECTED_REWARDS - UNSHIELD_REWARDS_AMT;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
RPC,
],
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(&format!("nam: {EXPECTED_REWARDS}")));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"unshield",
"--source",
A_SPENDING_KEY,
"--target",
BERTHA,
"--token",
NAM,
"--amount",
&UNSHIELD_REWARDS_AMT.to_string(),
"--signing-keys",
BERTHA_KEY,
"--node",
RPC,
]),
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
"--node",
RPC,
],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
RPC,
],
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(&format!("nam: {REMAINING_REWARDS_AMT}")));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"transfer",
"--source",
A_SPENDING_KEY,
"--target",
AB_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
&REMAINING_REWARDS_AMT.to_string(),
"--signing-keys",
BERTHA_KEY,
"--node",
RPC,
]),
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
"--node",
RPC,
],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
RPC,
],
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains("nam: 0"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"unshield",
"--source",
A_SPENDING_KEY,
"--target",
BERTHA,
"--token",
TEST_TOKEN_ADDR,
"--amount",
"500000",
"--signing-keys",
BERTHA_KEY,
"--node",
RPC,
]),
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
"--node",
RPC,
],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
TEST_TOKEN_ADDR,
"--node",
RPC,
],
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(&format!("{TEST_TOKEN_ADDR}: 500000")));
Ok(())
}
#[test]
fn auto_compounding() -> Result<()> {
let validator_one_rpc = "http://127.0.0.1:26567";
let _ = FsShieldedUtils::new(PathBuf::new());
let (mut node, _services) = setup::setup()?;
node.next_masp_epoch();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
ALBERT,
"--target",
AA_PAYMENT_ADDRESS,
"--token",
BTC,
"--amount",
"0.1",
"--signing-keys",
ALBERT_KEY,
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
ALBERT,
"--target",
AB_PAYMENT_ADDRESS,
"--token",
BTC,
"--amount",
"0.1",
"--signing-keys",
ALBERT_KEY,
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
AB_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)?;
let mut check_balance_and_reshield =
|bal_a, bal_b, est_a, est_b, total| -> Result<()> {
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 0.1"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AB_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 0.1"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(&format!("nam: {}", bal_a)));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AB_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(&format!("nam: {}", bal_b)));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"estimate-shielding-rewards",
"--key",
AA_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(&format!(
"Estimated native token rewards for the next MASP epoch: {}",
est_a
)));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"estimate-shielding-rewards",
"--key",
AB_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(&format!(
"Estimated native token rewards for the next MASP epoch: {}",
est_b
)));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
MASP,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(&format!("nam: {}", total)));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"unshield",
"--source",
B_SPENDING_KEY,
"--target",
ALBERT,
"--token",
NAM,
"--amount",
bal_b,
"--gas-limit",
"70000",
"--signing-keys",
ALBERT_KEY,
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
AB_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
ALBERT,
"--target",
AB_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
bal_b,
"--signing-keys",
ALBERT_KEY,
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
AB_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)?;
node.next_masp_epoch();
Ok(())
};
check_balance_and_reshield("0", "0", "0", "0", "0")?;
check_balance_and_reshield(
"0.0317", "0.0317", "0.0317", "0.0317", "0.0634",
)?;
check_balance_and_reshield(
"0.09534", "0.09533", "0.06491", "0.0649", "0.190688",
)?;
check_balance_and_reshield(
"0.191008", "0.190982", "0.09678", "0.096796", "0.382016",
)?;
check_balance_and_reshield(
"0.31854", "0.31851", "0.128528", "0.128524", "0.637092",
)?;
Ok(())
}
#[test]
fn base_precision_effective() -> Result<()> {
let validator_one_rpc = "http://127.0.0.1:26567";
let _ = FsShieldedUtils::new(PathBuf::new());
let (mut node, _services) = setup::setup()?;
const PRECISION: Precision = 1000000;
node.shell
.lock()
.unwrap()
.state
.write(&masp_base_native_precision_key(), PRECISION)
.expect("unable to write base precision");
const SCHEDULED_PRECISION: Precision = 10000;
node.shell
.lock()
.unwrap()
.state
.write(
&masp_scheduled_base_native_precision_key(&MaspEpoch::new(4)),
SCHEDULED_PRECISION,
)
.expect("unable to write scheduled base precision");
node.next_masp_epoch();
assert_eq!(
node.shell
.lock()
.unwrap()
.state
.read(&masp_base_native_precision_key())
.expect("unable to read base precision"),
Some(PRECISION),
);
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
ALBERT,
"--target",
AA_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"0.1",
"--signing-keys",
ALBERT_KEY,
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
AB_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0.1"));
node.next_masp_epoch();
assert_eq!(
node.shell
.lock()
.unwrap()
.state
.read(&masp_base_native_precision_key())
.expect("unable to read base precision"),
Some(PRECISION),
);
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0.1"));
node.next_masp_epoch();
assert_eq!(
node.shell
.lock()
.unwrap()
.state
.read(&masp_base_native_precision_key())
.expect("unable to read base precision"),
Some(PRECISION),
);
node.next_masp_epoch();
assert_eq!(
node.shell
.lock()
.unwrap()
.state
.read(&masp_base_native_precision_key())
.expect("unable to read base precision"),
Some(PRECISION),
);
node.next_masp_epoch();
assert_eq!(
node.shell
.lock()
.unwrap()
.state
.read(&masp_base_native_precision_key())
.expect("unable to read base precision"),
Some(SCHEDULED_PRECISION),
);
node.next_masp_epoch();
assert_eq!(
node.shell
.lock()
.unwrap()
.state
.read(&masp_base_native_precision_key())
.expect("unable to read base precision"),
Some(SCHEDULED_PRECISION),
);
Ok(())
}
#[test]
fn reset_conversions() -> Result<()> {
let validator_one_rpc = "http://127.0.0.1:26567";
let _ = FsShieldedUtils::new(PathBuf::new());
let (mut node, _services) = setup::setup()?;
node.next_masp_epoch();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
ALBERT,
"--target",
AA_PAYMENT_ADDRESS,
"--token",
BTC,
"--amount",
"1",
"--signing-keys",
ALBERT_KEY,
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
AB_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 1"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"estimate-shielding-rewards",
"--key",
AA_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(
captured.contains(
"Estimated native token rewards for the next MASP epoch: 0"
)
);
node.next_masp_epoch();
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 1"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0.063"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"estimate-shielding-rewards",
"--key",
AA_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(
"Estimated native token rewards for the next MASP epoch: 0.063"
));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
MASP,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0.063"));
node.next_masp_epoch();
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 1"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0.18887"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"estimate-shielding-rewards",
"--key",
AA_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(
captured.contains(
"Estimated native token rewards for the next MASP epoch: 0"
)
);
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
MASP,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0.18887"));
let btc_alias = BTC.to_lowercase();
let token_map_key = masp_token_map_key();
let tokens: TokenMap = node
.shell
.lock()
.unwrap()
.state
.read(&token_map_key)
.unwrap()
.unwrap_or_default();
let btc_addr = &tokens[&btc_alias];
let btc_denom =
token::read_denom(&node.shell.lock().unwrap().state, btc_addr)?
.expect("unable to read token denomination");
const PRECISION: i128 = 10000;
let mut asset_types = BTreeMap::new();
let mut precision_btcs = BTreeMap::new();
let mut reward_deltas = BTreeMap::new();
let mut asset_type = |epoch, digit| {
*asset_types.entry((epoch, digit)).or_insert_with(|| {
encode_asset_type(btc_addr.clone(), btc_denom, digit, Some(epoch))
.expect("unable to encode asset type")
})
};
let mut precision_btc = |epoch, digit| {
precision_btcs
.entry((epoch, digit))
.or_insert_with(|| {
AllowedConversion::from(I128Sum::from_pair(
asset_type(epoch, digit),
PRECISION,
))
})
.clone()
};
let mut reward_delta = |epoch, digit| {
reward_deltas
.entry((epoch, digit))
.or_insert_with(|| {
-precision_btc(epoch, digit)
+ precision_btc(epoch.next().unwrap(), digit)
})
.clone()
};
let current_masp_epoch = node.current_masp_epoch();
node.shell
.lock()
.unwrap()
.state
.write(
&masp_scheduled_reward_precision_key(¤t_masp_epoch, btc_addr),
Precision::try_from(PRECISION).unwrap(),
)
.expect("unable to write scheduled precision update");
for digit in token::MaspDigitPos::iter() {
let mut reward: AllowedConversion = I128Sum::zero().into();
for epoch in MaspEpoch::iter_bounds_inclusive(
MaspEpoch::zero(),
current_masp_epoch.prev().unwrap(),
)
.rev()
{
let asset_type = encode_asset_type(
btc_addr.clone(),
btc_denom,
digit,
Some(epoch),
)
.expect("unable to encode asset type");
reward += reward_delta(epoch, digit);
node.shell
.lock()
.unwrap()
.state
.write(
&masp_conversion_key(¤t_masp_epoch, &asset_type),
reward.clone(),
)
.expect("unable to write conversion update");
}
}
node.next_masp_epoch();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 1"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0.17272"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"estimate-shielding-rewards",
"--key",
AA_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(
"Estimated native token rewards for the next MASP epoch: 0.174772"
));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
MASP,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0.362712"));
Ok(())
}
#[test]
fn dynamic_precision() -> Result<()> {
let validator_one_rpc = "http://127.0.0.1:26567";
let _ = FsShieldedUtils::new(PathBuf::new());
let (mut node, _services) = setup::setup()?;
let btc = BTC.to_lowercase();
let token_map_key = masp_token_map_key();
let tokens: TokenMap = node
.shell
.lock()
.unwrap()
.state
.read(&token_map_key)
.unwrap()
.unwrap_or_default();
node.next_masp_epoch();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
ALBERT,
"--target",
AA_PAYMENT_ADDRESS,
"--token",
BTC,
"--amount",
"1",
"--signing-keys",
ALBERT_KEY,
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
AB_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 1"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"estimate-shielding-rewards",
"--key",
AA_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(
captured.contains(
"Estimated native token rewards for the next MASP epoch: 0"
)
);
node.next_masp_epoch();
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 1"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0.063"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"estimate-shielding-rewards",
"--key",
AA_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(
"Estimated native token rewards for the next MASP epoch: 0.063"
));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
MASP,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0.063"));
{
node.shell
.lock()
.unwrap()
.state
.write(&masp_reward_precision_key(&tokens[&btc]), 1000000u128)
.unwrap();
}
node.next_masp_epoch();
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 1"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"estimate-shielding-rewards",
"--key",
AA_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(
captured.contains(
"Estimated native token rewards for the next MASP epoch: 0"
)
);
node.next_masp_epoch();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"unshield",
"--source",
AA_VIEWING_KEY,
"--target",
ALBERT,
"--token",
BTC,
"--amount",
"1",
"--signing-keys",
ALBERT_KEY,
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 0"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
ALBERT,
"--target",
AA_PAYMENT_ADDRESS,
"--token",
BTC,
"--amount",
"1",
"--signing-keys",
ALBERT_KEY,
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
AB_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 1"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0"));
node.next_masp_epoch();
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 1"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0.25316"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"unshield",
"--source",
AA_VIEWING_KEY,
"--target",
ALBERT,
"--token",
BTC,
"--amount",
"1",
"--signing-keys",
ALBERT_KEY,
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"unshield",
"--source",
AA_VIEWING_KEY,
"--target",
ALBERT,
"--token",
NAM,
"--amount",
"0.25316",
"--signing-keys",
ALBERT_KEY,
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
MASP,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0.382401"));
Ok(())
}
#[test]
fn masp_incentives() -> Result<()> {
let validator_one_rpc = "http://127.0.0.1:26567";
let _ = FsShieldedUtils::new(PathBuf::new());
let (mut node, _services) = setup::setup()?;
node.next_masp_epoch();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
ALBERT,
"--target",
AA_PAYMENT_ADDRESS,
"--token",
BTC,
"--amount",
"1",
"--signing-keys",
ALBERT_KEY,
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
AB_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 1"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"estimate-shielding-rewards",
"--key",
AA_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(
captured.contains(
"Estimated native token rewards for the next MASP epoch: 0"
)
);
node.next_masp_epoch();
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 1"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0.063"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"estimate-shielding-rewards",
"--key",
AA_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(
"Estimated native token rewards for the next MASP epoch: 0.063"
));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
MASP,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0.063"));
node.next_masp_epoch();
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 1"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0.18887"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"estimate-shielding-rewards",
"--key",
AA_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(
captured.contains(
"Estimated native token rewards for the next MASP epoch: 0"
)
);
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
MASP,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0.18887"));
node.next_masp_epoch();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
ALBERT,
"--target",
AB_PAYMENT_ADDRESS,
"--token",
ETH,
"--amount",
"0.001",
"--signing-keys",
ALBERT_KEY,
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AB_VIEWING_KEY,
"--token",
ETH,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("eth: 0.001"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AB_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0"));
node.next_masp_epoch();
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AB_VIEWING_KEY,
"--token",
ETH,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("eth: 0.001"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AB_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0.750883"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
MASP,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 1.383286"));
node.next_masp_epoch();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"unshield",
"--source",
B_SPENDING_KEY,
"--target",
CHRISTEL,
"--token",
ETH,
"--amount",
"0.001",
"--signing-keys",
BERTHA_KEY,
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AB_VIEWING_KEY,
"--token",
ETH,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("eth: 0"));
node.next_masp_epoch();
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AB_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 1.502496"));
node.next_masp_epoch();
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
MASP,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 3.267817"));
node.next_masp_epoch();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"unshield",
"--source",
A_SPENDING_KEY,
"--target",
CHRISTEL,
"--token",
BTC,
"--amount",
"1",
"--signing-keys",
ALBERT_KEY,
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 0"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 2.268662"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
MASP,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 3.77117"));
node.next_masp_epoch();
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 2.268662"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AB_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 1.502496"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
MASP,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 3.77117"));
node.next_masp_epoch();
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"unshield",
"--source",
B_SPENDING_KEY,
"--target",
CHRISTEL,
"--token",
NAM,
"--amount",
"1.502496",
"--gas-limit",
"60000",
"--signing-keys",
BERTHA_KEY,
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
node.next_masp_epoch();
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"unshield",
"--source",
A_SPENDING_KEY,
"--target",
BERTHA,
"--token",
NAM,
"--amount",
"2.268662",
"--signing-keys",
ALBERT_KEY,
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0"));
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AB_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
MASP,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0.000012"));
Ok(())
}
#[test]
fn spend_unconverted_asset_type() -> Result<()> {
let validator_one_rpc = "http://127.0.0.1:26567";
let _ = FsShieldedUtils::new(PathBuf::new());
let (mut node, _services) = setup::setup()?;
let _ep0 = node.next_epoch();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
ALBERT,
"--target",
AA_PAYMENT_ADDRESS,
"--token",
BTC,
"--amount",
"20",
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
node.next_epoch();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
ALBERT,
"--target",
AB_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"0.000001",
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
for _ in 0..5 {
node.next_epoch();
}
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
AB_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AB_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0.000001"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"transfer",
"--source",
B_SPENDING_KEY,
"--target",
AA_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"0.000001",
"--gas-payer",
CHRISTEL_KEY,
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
Ok(())
}
#[test]
fn masp_txs_and_queries() -> Result<()> {
let validator_one_rpc = "http://127.0.0.1:26567";
let _ = FsShieldedUtils::new(PathBuf::new());
enum Response {
Ok(&'static str),
Err(&'static str),
}
let (mut node, _services) = setup::setup()?;
_ = node.next_epoch();
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
AB_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)?;
let txs_args = vec![
(
apply_use_device(vec![
"transfer",
"--source",
A_SPENDING_KEY,
"--target",
AB_PAYMENT_ADDRESS,
"--token",
BTC,
"--amount",
"10",
"--gas-payer",
CHRISTEL_KEY,
"--node",
validator_one_rpc,
]),
Response::Err(""),
),
(
apply_use_device(vec![
"unshield",
"--source",
A_SPENDING_KEY,
"--target",
BERTHA,
"--token",
BTC,
"--amount",
"15",
"--gas-payer",
CHRISTEL_KEY,
"--node",
validator_one_rpc,
]),
Response::Err(""),
),
(
apply_use_device(vec![
"shield",
"--source",
ALBERT,
"--target",
AA_PAYMENT_ADDRESS,
"--token",
BTC,
"--amount",
"20",
"--node",
validator_one_rpc,
]),
Response::Ok(TX_APPLIED_SUCCESS),
),
(
apply_use_device(vec![
"transfer",
"--source",
A_SPENDING_KEY,
"--target",
AB_PAYMENT_ADDRESS,
"--token",
ETH,
"--amount",
"10",
"--gas-payer",
CHRISTEL_KEY,
"--node",
validator_one_rpc,
]),
Response::Err(""),
),
(
apply_use_device(vec![
"transfer",
"--source",
A_SPENDING_KEY,
"--target",
AB_PAYMENT_ADDRESS,
"--token",
BTC,
"--amount",
"7",
"--gas-payer",
CHRISTEL_KEY,
"--node",
validator_one_rpc,
]),
Response::Ok(TX_APPLIED_SUCCESS),
),
(
apply_use_device(vec![
"transfer",
"--source",
A_SPENDING_KEY,
"--target",
BB_PAYMENT_ADDRESS,
"--token",
BTC,
"--amount",
"7",
"--gas-payer",
CHRISTEL_KEY,
"--node",
validator_one_rpc,
]),
Response::Ok(TX_APPLIED_SUCCESS),
),
(
apply_use_device(vec![
"transfer",
"--source",
A_SPENDING_KEY,
"--target",
BB_PAYMENT_ADDRESS,
"--token",
BTC,
"--amount",
"7",
"--gas-payer",
CHRISTEL_KEY,
"--node",
validator_one_rpc,
]),
Response::Err(""),
),
(
apply_use_device(vec![
"transfer",
"--source",
A_SPENDING_KEY,
"--target",
BB_PAYMENT_ADDRESS,
"--token",
BTC,
"--amount",
"6",
"--gas-payer",
CHRISTEL_KEY,
"--node",
validator_one_rpc,
]),
Response::Ok(TX_APPLIED_SUCCESS),
),
(
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
Response::Ok("btc: 0"),
),
(
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
ETH,
"--node",
validator_one_rpc,
],
Response::Ok("eth: 0"),
),
(
vec![
"balance",
"--owner",
AB_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
Response::Ok("btc: 20"),
),
(
apply_use_device(vec![
"unshield",
"--source",
B_SPENDING_KEY,
"--target",
BERTHA,
"--token",
BTC,
"--amount",
"20",
"--gas-limit",
"60000",
"--gas-payer",
CHRISTEL_KEY,
"--node",
validator_one_rpc,
]),
Response::Ok(TX_APPLIED_SUCCESS),
),
];
for (tx_args, tx_result) in &txs_args {
let dry_run_args = if tx_args[0] == "transfer"
|| tx_args[0] == "shield"
|| tx_args[0] == "unshield"
{
node.next_epoch();
vec![true, false]
} else {
vec![false]
};
for &dry_run in &dry_run_args {
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let tx_args = if dry_run && is_use_device() {
continue;
} else if dry_run {
[tx_args.clone(), vec!["--dry-run"]].concat()
} else {
tx_args.clone()
};
println!(
"{}: {:?}\n\n",
"Running".green().underline(),
tx_args.join(" ").yellow().underline()
);
let captured =
CapturedOutput::of(|| run(&node, Bin::Client, tx_args.clone()));
match tx_result {
Response::Ok(TX_APPLIED_SUCCESS) => {
assert!(
captured.result.is_ok(),
"{:?} failed with result {:?}.\n Unread output: {}",
tx_args,
captured.result,
captured.output,
);
assert!(
captured.contains(TX_APPLIED_SUCCESS),
"{:?} failed to contain needle 'Transaction is \
valid',\nGot output '{}'",
tx_args,
captured.output
);
}
Response::Ok(out) => {
assert!(
captured.result.is_ok(),
"{:?} failed with result {:?}.\n Unread output: {}",
tx_args,
captured.result,
captured.output,
);
assert!(
captured.contains(out),
"{:?} failed to contain needle '{}',\nGot output '{}'",
tx_args,
out,
captured.output
);
}
Response::Err(msg) => {
assert!(
captured.result.is_err(),
"{:?} unexpectedly succeeded",
tx_args
);
assert!(
captured.contains(msg),
"{:?} failed to contain needle {},\nGot output {}",
tx_args,
msg,
captured.output
);
}
}
}
}
Ok(())
}
#[test]
fn multiple_unfetched_txs_same_block() -> Result<()> {
let validator_one_rpc = "http://127.0.0.1:26567";
let _ = FsShieldedUtils::new(PathBuf::new());
let (mut node, _services) = setup::setup()?;
_ = node.next_epoch();
let (cooper_alias, cooper_key) =
make_temp_account(&node, validator_one_rpc, "Cooper", NAM, 500_000)?;
_ = node.next_epoch();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
ALBERT_KEY,
"--target",
AA_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"100",
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
_ = node.next_epoch();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
ALBERT_KEY,
"--target",
AA_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"200",
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
_ = node.next_epoch();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
ALBERT_KEY,
"--target",
AB_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"100",
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let tempdir = tempfile::tempdir().unwrap();
let mut txs_bytes = vec![];
_ = node.next_epoch();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"transfer",
"--source",
A_SPENDING_KEY,
"--target",
AC_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"50",
"--gas-payer",
ALBERT_KEY,
"--output-folder-path",
tempdir.path().to_str().unwrap(),
"--dump-tx",
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
let file_path = tempdir
.path()
.read_dir()
.unwrap()
.next()
.unwrap()
.unwrap()
.path();
txs_bytes.push(std::fs::read(&file_path).unwrap());
std::fs::remove_file(&file_path).unwrap();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"transfer",
"--source",
A_SPENDING_KEY,
"--target",
AC_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"50",
"--gas-payer",
cooper_alias.as_ref(),
"--output-folder-path",
tempdir.path().to_str().unwrap(),
"--dump-tx",
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
let file_path = tempdir
.path()
.read_dir()
.unwrap()
.next()
.unwrap()
.unwrap()
.path();
txs_bytes.push(std::fs::read(&file_path).unwrap());
std::fs::remove_file(&file_path).unwrap();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"transfer",
"--source",
B_SPENDING_KEY,
"--target",
AC_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"50",
"--gas-payer",
cooper_alias.as_ref(),
"--output-folder-path",
tempdir.path().to_str().unwrap(),
"--dump-tx",
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
let file_path = tempdir
.path()
.read_dir()
.unwrap()
.next()
.unwrap()
.unwrap()
.path();
txs_bytes.push(std::fs::read(&file_path).unwrap());
std::fs::remove_file(&file_path).unwrap();
let sk = cooper_key;
let pk = sk.to_public();
let native_token = node
.shell
.lock()
.unwrap()
.state
.in_mem()
.native_token
.clone();
let mut txs = vec![];
for bytes in txs_bytes {
let mut tx = Tx::try_from_json_bytes(&bytes).unwrap();
tx.add_wrapper(
tx::data::wrapper::Fee {
amount_per_gas_unit: DenominatedAmount::native(100.into()),
token: native_token.clone(),
},
pk.clone(),
DEFAULT_GAS_LIMIT.into(),
);
tx.sign_wrapper(sk.clone());
txs.push(tx.to_bytes());
}
node.clear_results();
node.submit_txs(txs);
assert!(!node.tx_result_codes.lock().unwrap().is_empty());
node.assert_success();
Ok(())
}
#[test]
fn expired_masp_tx() -> Result<()> {
let validator_one_rpc = "http://127.0.0.1:26567";
let _ = FsShieldedUtils::new(PathBuf::new());
let (mut node, _services) = setup::setup()?;
_ = node.next_epoch();
let (cooper_alias, cooper_key) =
make_temp_account(&node, validator_one_rpc, "Cooper", NAM, 500_000)?;
_ = node.next_epoch();
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
ALBERT_KEY,
"--target",
AA_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"100",
"--ledger-address",
validator_one_rpc,
]),
)?;
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let tempdir = tempfile::tempdir().unwrap();
_ = node.next_epoch();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"transfer",
"--source",
A_SPENDING_KEY,
"--target",
AC_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"50",
"--gas-payer",
cooper_alias.as_ref(),
"--expiration",
#[allow(clippy::disallowed_methods)]
&DateTimeUtc::now().to_string(),
"--output-folder-path",
tempdir.path().to_str().unwrap(),
"--dump-tx",
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
let file_path = tempdir
.path()
.read_dir()
.unwrap()
.next()
.unwrap()
.unwrap()
.path();
let tx_bytes = std::fs::read(&file_path).unwrap();
std::fs::remove_file(&file_path).unwrap();
let sk = cooper_key;
let pk = sk.to_public();
let native_token = node
.shell
.lock()
.unwrap()
.state
.in_mem()
.native_token
.clone();
let mut tx = Tx::try_from_json_bytes(&tx_bytes).unwrap();
tx.header.expiration = None;
tx.add_wrapper(
namada_sdk::tx::data::wrapper::Fee {
amount_per_gas_unit: DenominatedAmount::native(100.into()),
token: native_token.clone(),
},
pk.clone(),
DEFAULT_GAS_LIMIT.into(),
);
tx.sign_wrapper(sk.clone());
let wrapper_hash = tx.wrapper_hash();
let inner_cmt = tx.first_commitments().unwrap();
for _ in 0..=20 {
node.finalize_and_commit(None);
}
node.clear_results();
node.submit_txs(vec![tx.to_bytes()]);
{
let codes = node.tx_result_codes.lock().unwrap();
assert!(!codes.is_empty());
for code in codes.iter() {
assert!(matches!(code, NodeResults::Ok));
}
let results = node.tx_results.lock().unwrap();
assert_eq!(results.len(), 1);
for result in results.iter() {
assert_eq!(result.len(), 1);
let inner_tx_result = result
.get_inner_tx_result(
wrapper_hash.as_ref(),
itertools::Either::Right(inner_cmt),
)
.expect("Missing expected tx result")
.as_ref()
.expect("Result is supposed to be Ok");
assert!(
inner_tx_result
.vps_result
.rejected_vps
.contains(&namada_sdk::address::MASP)
);
assert!(inner_tx_result.vps_result.errors.contains(&(
namada_sdk::address::MASP,
"Native VP error: MASP transaction is expired".to_string()
)));
}
}
Ok(())
}
#[test]
fn cross_epoch_unshield() -> Result<()> {
let validator_one_rpc = "http://127.0.0.1:26567";
let _ = FsShieldedUtils::new(PathBuf::new());
let (mut node, _services) = setup::setup()?;
_ = node.next_epoch();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
ALBERT,
"--target",
AA_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"1000",
"--signing-keys",
ALBERT_KEY,
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)?;
let tempdir = tempfile::tempdir().unwrap();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"unshield",
"--source",
A_SPENDING_KEY,
"--target",
BERTHA,
"--token",
NAM,
"--amount",
"100",
"--gas-payer",
ALBERT_KEY,
"--output-folder-path",
tempdir.path().to_str().unwrap(),
"--dump-tx",
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
let tx_path = tempdir
.path()
.read_dir()
.unwrap()
.next()
.unwrap()
.unwrap()
.path();
_ = node.next_epoch();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"tx",
"--owner",
ALBERT_KEY,
"--tx-path",
tx_path.to_str().unwrap(),
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
Ok(())
}
#[test]
fn dynamic_assets() -> Result<()> {
let validator_one_rpc = "http://127.0.0.1:26567";
let _ = FsShieldedUtils::new(PathBuf::new());
let (mut node, _services) = setup::setup()?;
let btc = BTC.to_lowercase();
let nam = NAM.to_lowercase();
let token_map_key = masp_token_map_key();
let test_tokens = {
let mut tokens: TokenMap = node
.shell
.lock()
.unwrap()
.state
.read(&token_map_key)
.unwrap()
.unwrap_or_default();
let test_tokens = tokens.clone();
tokens.retain(|k, _v| *k == nam);
node.shell
.lock()
.unwrap()
.state
.write(&token_map_key, tokens.clone())
.unwrap();
test_tokens
};
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)?;
node.next_masp_epoch();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
ALBERT,
"--target",
AA_PAYMENT_ADDRESS,
"--token",
BTC,
"--amount",
"1",
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 1"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0"));
{
let mut tokens: TokenMap = node
.shell
.lock()
.unwrap()
.state
.read(&token_map_key)
.unwrap()
.unwrap_or_default();
tokens.insert(btc.clone(), test_tokens[&btc].clone());
node.shell
.lock()
.unwrap()
.state
.write(&token_map_key, tokens)
.unwrap();
}
node.next_masp_epoch();
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 1"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
ALBERT,
"--target",
AA_PAYMENT_ADDRESS,
"--token",
BTC,
"--amount",
"1",
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 2"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0"));
node.next_masp_epoch();
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0.063"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 2"));
{
let storage = &mut node.shell.lock().unwrap().state;
storage
.write(
&token::storage_key::masp_max_reward_rate_key(
&test_tokens[&nam],
),
Dec::zero(),
)
.unwrap();
}
node.next_masp_epoch();
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 2"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0.189"));
{
let mut tokens: TokenMap = node
.shell
.lock()
.unwrap()
.state
.read(&token_map_key)
.unwrap()
.unwrap_or_default();
tokens.remove(&btc);
node.shell
.lock()
.unwrap()
.state
.write(&token_map_key, tokens)
.unwrap();
}
node.next_masp_epoch();
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 2"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0.189"));
node.next_masp_epoch();
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 2"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0.189"));
{
let storage = &mut node.shell.lock().unwrap().state;
storage
.write(
&token::storage_key::masp_max_reward_rate_key(
&test_tokens[&nam],
),
Dec::from_str("0.1").unwrap(),
)
.unwrap();
}
node.next_masp_epoch();
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 2"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0.189567"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"unshield",
"--source",
A_SPENDING_KEY,
"--target",
BERTHA,
"--token",
NAM,
"--amount",
"0.189567",
"--gas-payer",
BERTHA_KEY,
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"unshield",
"--source",
A_SPENDING_KEY,
"--target",
BERTHA,
"--token",
BTC,
"--amount",
"2",
"--gas-payer",
BERTHA_KEY,
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 0"));
Ok(())
}
#[test]
fn masp_fee_payment() -> Result<()> {
let validator_one_rpc = "http://127.0.0.1:26567";
let _ = FsShieldedUtils::new(PathBuf::new());
let (mut node, _services) = setup::setup()?;
_ = node.next_masp_epoch();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
ALBERT_KEY,
"--target",
AA_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"500000",
"--gas-payer",
CHRISTEL_KEY,
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
_ = node.next_masp_epoch();
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 500000"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"transfer",
"--source",
A_SPENDING_KEY,
"--target",
AB_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"1",
"--gas-limit",
"20000",
"--gas-price",
"1",
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_err());
_ = node.next_masp_epoch();
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 500000"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"transparent-transfer",
"--source",
ALBERT_KEY,
"--target",
BERTHA_KEY,
"--token",
NAM,
"--amount",
"1500000",
"--gas-payer",
CHRISTEL_KEY,
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
ALBERT_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"transparent-transfer",
"--source",
BERTHA_KEY,
"--target",
ALBERT_KEY,
"--token",
NAM,
"--amount",
"200000",
"--gas-payer",
ALBERT_KEY,
"--ledger-address",
validator_one_rpc,
"--force",
]),
)
});
assert!(captured.result.is_err());
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
ALBERT_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"transfer",
"--source",
A_SPENDING_KEY,
"--target",
AB_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"10000",
"--gas-price",
"1",
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 440000"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AB_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 10000"));
Ok(())
}
#[test]
fn masp_fee_payment_gas_limit() -> Result<()> {
let validator_one_rpc = "http://127.0.0.1:26567";
let _ = FsShieldedUtils::new(PathBuf::new());
let (mut node, _services) = setup::initialize_genesis(|mut genesis| {
genesis.parameters.parameters.masp_fee_payment_gas_limit = 10_000;
genesis
})?;
_ = node.next_masp_epoch();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
ALBERT_KEY,
"--target",
AA_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"1000000",
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
_ = node.next_masp_epoch();
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 1000000"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"unshield",
"--source",
A_SPENDING_KEY,
"--target",
BERTHA,
"--token",
NAM,
"--amount",
"1",
"--gas-price",
"1",
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_err());
_ = node.next_masp_epoch();
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 1000000"));
Ok(())
}
#[test]
fn masp_fee_payment_with_non_disposable() -> Result<()> {
let validator_one_rpc = "http://127.0.0.1:26567";
let _ = FsShieldedUtils::new(PathBuf::new());
let (mut node, _services) = setup::setup()?;
_ = node.next_masp_epoch();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
ALBERT_KEY,
"--target",
AA_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"1999999",
"--gas-payer",
BERTHA_KEY,
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
_ = node.next_masp_epoch();
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 1999999"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
ALBERT_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 1"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"unshield",
"--source",
A_SPENDING_KEY,
"--target",
BERTHA,
"--token",
NAM,
"--amount",
"1",
"--gas-price",
"1",
"--gas-limit",
"60000",
"--gas-payer",
ALBERT_KEY,
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
_ = node.next_masp_epoch();
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 1939999"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
ALBERT_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0"));
Ok(())
}
#[test]
fn masp_fee_payment_with_custom_spending_key() -> Result<()> {
let validator_one_rpc = "http://127.0.0.1:26567";
let _ = FsShieldedUtils::new(PathBuf::new());
let (mut node, _services) = setup::setup()?;
_ = node.next_masp_epoch();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
ALBERT_KEY,
"--target",
AA_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"10000",
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
ALBERT_KEY,
"--target",
AB_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"300000",
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
_ = node.next_masp_epoch();
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 10000"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AB_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 300000"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"transfer",
"--source",
A_SPENDING_KEY,
"--target",
AC_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"9000",
"--gas-limit",
"60000",
"--gas-price",
"1",
"--gas-spending-key",
B_SPENDING_KEY,
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
_ = node.next_masp_epoch();
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 1000"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AB_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 240000"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AC_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 9000"));
Ok(())
}
#[test]
fn masp_fee_payment_with_different_token() -> Result<()> {
let validator_one_rpc = "http://127.0.0.1:26567";
let _ = FsShieldedUtils::new(PathBuf::new());
let (mut node, _services) = setup::initialize_genesis(|mut genesis| {
genesis.parameters.parameters.minimum_gas_price.insert(
"btc".into(),
DenominatedAmount::new(1.into(), token::Denomination(6)),
);
genesis
})?;
_ = node.next_masp_epoch();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
ALBERT_KEY,
"--target",
AA_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"1",
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
ALBERT,
"--target",
AA_PAYMENT_ADDRESS,
"--token",
BTC,
"--amount",
"1000",
"--gas-payer",
ALBERT_KEY,
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
ALBERT,
"--target",
AB_PAYMENT_ADDRESS,
"--token",
BTC,
"--amount",
"300000",
"--gas-payer",
ALBERT_KEY,
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
_ = node.next_masp_epoch();
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 1"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 1000"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AB_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 300000"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"transfer",
"--source",
A_SPENDING_KEY,
"--target",
AB_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"1",
"--gas-limit",
"60000",
"--gas-token",
BTC,
"--gas-price",
"1",
"--gas-spending-key",
B_SPENDING_KEY,
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
_ = node.next_masp_epoch();
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AB_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 1"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 1000"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AB_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 240000"));
Ok(())
}
#[test]
fn identical_output_descriptions() -> Result<()> {
let validator_one_rpc = "http://127.0.0.1:26567";
let _ = FsShieldedUtils::new(PathBuf::new());
let (mut node, _services) = setup::setup()?;
_ = node.next_masp_epoch();
let tempdir = tempfile::tempdir().unwrap();
let (adam_alias, adam_key) =
make_temp_account(&node, validator_one_rpc, "Adam", NAM, 500_000)?;
let (bradley_alias, bradley_key) =
make_temp_account(&node, validator_one_rpc, "Bradley", NAM, 500_000)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
adam_alias.as_ref(),
"--target",
AA_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"1000",
"--gas-payer",
bradley_alias.as_ref(),
"--gas-limit",
"60000",
"--output-folder-path",
tempdir.path().to_str().unwrap(),
"--dump-wrapper-tx",
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
let file_path = tempdir
.path()
.read_dir()
.unwrap()
.next()
.unwrap()
.unwrap()
.path();
let tx_bytes = std::fs::read(&file_path).unwrap();
std::fs::remove_file(&file_path).unwrap();
let tx: namada_sdk::tx::Tx = serde_json::from_slice(&tx_bytes).unwrap();
let mut tx_clone = tx.clone();
tx_clone.add_memo(&[1, 2, 3]);
let signing_data = SigningTxData {
owner: None,
public_keys: [adam_key.to_public()].into(),
threshold: 1,
account_public_keys_map: None,
fee_payer: Either::Left((adam_key.to_public(), false)),
shielded_hash: None,
signatures: vec![],
};
let (mut batched_tx, _signing_data) = namada_sdk::tx::build_batch(vec![
(tx, signing_data.clone()),
(tx_clone, signing_data),
])
.unwrap();
batched_tx.sign_raw(
vec![adam_key.clone()],
AccountPublicKeysMap::from_iter(
vec![(adam_key.to_public())].into_iter(),
),
None,
);
batched_tx.sign_wrapper(bradley_key);
let wrapper_hash = batched_tx.wrapper_hash();
let inner_cmts = batched_tx.commitments();
let txs = vec![batched_tx.to_bytes()];
node.clear_results();
node.submit_txs(txs);
{
let codes = node.tx_result_codes.lock().unwrap();
assert!(!codes.is_empty());
for code in codes.iter() {
assert!(matches!(code, NodeResults::Ok));
}
let results = node.tx_results.lock().unwrap();
assert_eq!(results.len(), 1);
for result in results.iter() {
assert_eq!(result.len(), 2);
for inner_cmt in inner_cmts {
let inner_tx_result = result
.get_inner_tx_result(
wrapper_hash.as_ref(),
itertools::Either::Right(inner_cmt),
)
.expect("Missing expected tx result")
.as_ref()
.expect("Result is supposed to be Ok");
assert!(inner_tx_result.is_accepted());
}
}
}
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 2000"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
adam_alias.as_ref(),
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 498000"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
CHRISTEL,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 2000000"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"unshield",
"--source",
A_SPENDING_KEY,
"--target",
CHRISTEL,
"--token",
NAM,
"--amount",
"2000",
"--gas-payer",
BERTHA_KEY,
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
CHRISTEL,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 2002000"));
Ok(())
}
fn get_shielded_hash(tx: &namada_sdk::tx::Tx) -> Option<MaspTxId> {
for section in &tx.sections {
if let Section::MaspTx(masp) = section {
return Some(MaspTxId::from(masp.txid()));
}
}
None
}
#[test]
fn masp_batch() -> Result<()> {
let validator_one_rpc = "http://127.0.0.1:26567";
let _ = FsShieldedUtils::new(PathBuf::new());
let (mut node, _services) = setup::setup()?;
_ = node.next_masp_epoch();
let tempdir = tempfile::tempdir().unwrap();
let (adam_alias, adam_key) =
make_temp_account(&node, validator_one_rpc, "Adam", NAM, 500_000)?;
let (bradley_alias, _bradley_key) =
make_temp_account(&node, validator_one_rpc, "Bradley", NAM, 500_000)?;
let (cooper_alias, cooper_key) =
make_temp_account(&node, validator_one_rpc, "Cooper", NAM, 500_000)?;
for (owner, balance) in [
(AA_VIEWING_KEY, 0),
(adam_alias.as_ref(), 500_000),
(bradley_alias.as_ref(), 500_000),
] {
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
owner,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(&format!("nam: {balance}")));
}
let mut batch = vec![];
for source in [adam_alias.as_ref(), bradley_alias.as_ref()] {
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"shield",
"--source",
source,
"--target",
AA_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"1000",
"--gas-limit",
"60000",
"--gas-payer",
cooper_alias.as_ref(),
"--output-folder-path",
tempdir.path().to_str().unwrap(),
"--dump-wrapper-tx",
"--ledger-address",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
let file_path = tempdir
.path()
.read_dir()
.unwrap()
.next()
.unwrap()
.unwrap()
.path();
batch.push(std::fs::read(&file_path).unwrap());
std::fs::remove_file(&file_path).unwrap();
}
let tx0: namada_sdk::tx::Tx = serde_json::from_slice(&batch[0]).unwrap();
let tx1: namada_sdk::tx::Tx = serde_json::from_slice(&batch[1]).unwrap();
let signing_data = SigningTxData {
owner: None,
public_keys: [adam_key.to_public()].into(),
threshold: 1,
account_public_keys_map: None,
fee_payer: Either::Left((adam_key.to_public(), false)),
shielded_hash: None,
signatures: vec![],
};
let mut txs = vec![];
let mut inner_cmts = vec![];
let mut wrapper_hashes = vec![];
for (tx0, tx1) in [(tx0.clone(), tx1.clone()), (tx1, tx0)] {
let (mut batched_tx, _signing_data) =
namada_sdk::tx::build_batch(vec![
(
tx0.clone(),
SigningTxData {
shielded_hash: get_shielded_hash(&tx0),
..signing_data.clone()
},
),
(
tx1.clone(),
SigningTxData {
shielded_hash: get_shielded_hash(&tx1),
..signing_data.clone()
},
),
])
.unwrap();
batched_tx.header.atomic = false;
batched_tx.sign_raw(
vec![adam_key.clone()],
AccountPublicKeysMap::from_iter(
vec![(adam_key.to_public())].into_iter(),
),
None,
);
batched_tx.sign_wrapper(cooper_key.clone());
wrapper_hashes.push(batched_tx.wrapper_hash());
for cmt in batched_tx.commitments() {
inner_cmts.push(cmt.to_owned());
}
txs.push(batched_tx.to_bytes());
}
node.clear_results();
node.submit_txs(txs);
{
let codes = node.tx_result_codes.lock().unwrap();
assert!(!codes.is_empty());
for code in codes.iter() {
assert!(matches!(code, NodeResults::Ok))
}
let results = node.tx_results.lock().unwrap();
assert_eq!(results.len(), 2);
let res0 = &results[0];
assert_eq!(res0.len(), 2);
let inner_tx_result = res0
.get_inner_tx_result(
wrapper_hashes[0].as_ref(),
itertools::Either::Right(&inner_cmts[0]),
)
.expect("Missing expected tx result")
.as_ref()
.expect("Result is supposed to be Ok");
assert!(inner_tx_result.is_accepted());
let inner_tx_result = res0
.get_inner_tx_result(
wrapper_hashes[0].as_ref(),
itertools::Either::Right(&inner_cmts[1]),
)
.expect("Missing expected tx result")
.as_ref()
.expect("Result is supposed to be Ok");
assert!(!inner_tx_result.is_accepted());
let res1 = &results[1];
assert_eq!(res1.len(), 2);
let inner_tx_result = res1
.get_inner_tx_result(
wrapper_hashes[1].as_ref(),
itertools::Either::Right(&inner_cmts[2]),
)
.expect("Missing expected tx result")
.as_ref()
.expect("Result is supposed to be Ok");
assert!(!inner_tx_result.is_accepted());
let inner_tx_result = res1
.get_inner_tx_result(
wrapper_hashes[1].as_ref(),
itertools::Either::Right(&inner_cmts[3]),
)
.expect("Missing expected tx result")
.as_ref()
.expect("Result is supposed to be Ok");
assert!(inner_tx_result.is_accepted());
}
node.clear_results();
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)?;
for (owner, balance) in [
(AA_VIEWING_KEY, 2_000),
(adam_alias.as_ref(), 498_000),
(bradley_alias.as_ref(), 500_000),
] {
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
owner,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(&format!("nam: {balance}")));
}
Ok(())
}
#[test]
fn masp_atomic_batch() -> Result<()> {
let validator_one_rpc = "http://127.0.0.1:26567";
let _ = FsShieldedUtils::new(PathBuf::new());
let (mut node, _services) = setup::setup()?;
_ = node.next_masp_epoch();
let tempdir = tempfile::tempdir().unwrap();
let (adam_alias, adam_key) =
make_temp_account(&node, validator_one_rpc, "Adam", NAM, 500_000)?;
let (bradley_alias, _bradley_key) =
make_temp_account(&node, validator_one_rpc, "Bradley", NAM, 500_000)?;
let (cooper_alias, cooper_key) =
make_temp_account(&node, validator_one_rpc, "Cooper", NAM, 500_000)?;
for (owner, balance) in [
(AA_VIEWING_KEY, 0),
(adam_alias.as_ref(), 500_000),
(bradley_alias.as_ref(), 500_000),
] {
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
owner,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(&format!("nam: {balance}")));
}
let mut batch = vec![];
for source in [adam_alias.as_ref(), bradley_alias.as_ref()] {
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"shield",
"--source",
source,
"--target",
AA_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"1000",
"--gas-limit",
"60000",
"--gas-payer",
cooper_alias.as_ref(),
"--output-folder-path",
tempdir.path().to_str().unwrap(),
"--dump-wrapper-tx",
"--ledger-address",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
let file_path = tempdir
.path()
.read_dir()
.unwrap()
.next()
.unwrap()
.unwrap()
.path();
batch.push(std::fs::read(&file_path).unwrap());
std::fs::remove_file(&file_path).unwrap();
}
let tx0: namada_sdk::tx::Tx = serde_json::from_slice(&batch[0]).unwrap();
let tx1: namada_sdk::tx::Tx = serde_json::from_slice(&batch[1]).unwrap();
let signing_data = SigningTxData {
owner: None,
public_keys: [adam_key.to_public()].into(),
threshold: 1,
account_public_keys_map: None,
fee_payer: Either::Left((adam_key.to_public(), false)),
shielded_hash: None,
signatures: vec![],
};
let mut txs = vec![];
let mut inner_cmts = vec![];
let mut wrapper_hashes = vec![];
for (tx0, tx1) in [(tx0.clone(), tx1.clone()), (tx1, tx0)] {
let (mut batched_tx, _signing_data) =
namada_sdk::tx::build_batch(vec![
(
tx0.clone(),
SigningTxData {
shielded_hash: get_shielded_hash(&tx0),
..signing_data.clone()
},
),
(
tx1.clone(),
SigningTxData {
shielded_hash: get_shielded_hash(&tx1),
..signing_data.clone()
},
),
])
.unwrap();
batched_tx.header.atomic = true;
batched_tx.sign_raw(
vec![adam_key.clone()],
AccountPublicKeysMap::from_iter(
vec![(adam_key.to_public())].into_iter(),
),
None,
);
batched_tx.sign_wrapper(cooper_key.clone());
wrapper_hashes.push(batched_tx.wrapper_hash());
for cmt in batched_tx.commitments() {
inner_cmts.push(cmt.to_owned());
}
txs.push(batched_tx.to_bytes());
}
node.clear_results();
node.submit_txs(txs);
{
let codes = node.tx_result_codes.lock().unwrap();
assert_eq!(codes.len(), 2);
for code in codes.iter() {
assert!(matches!(
code,
NodeResults::Failed(
namada_node::shell::ResultCode::WasmRuntimeError
)
))
}
let results = node.tx_results.lock().unwrap();
assert_eq!(results.len(), 2);
let res0 = &results[0];
assert_eq!(res0.len(), 2);
let inner_tx_result = res0
.get_inner_tx_result(
wrapper_hashes[0].as_ref(),
itertools::Either::Right(&inner_cmts[0]),
)
.expect("Missing expected tx result")
.as_ref()
.expect("Result is supposed to be Ok");
assert!(inner_tx_result.is_accepted());
let inner_tx_result = res0
.get_inner_tx_result(
wrapper_hashes[0].as_ref(),
itertools::Either::Right(&inner_cmts[1]),
)
.expect("Missing expected tx result")
.as_ref()
.expect("Result is supposed to be Ok");
assert!(!inner_tx_result.is_accepted());
let res1 = &results[1];
assert_eq!(res1.len(), 1);
let inner_tx_result = res1
.get_inner_tx_result(
wrapper_hashes[1].as_ref(),
itertools::Either::Right(&inner_cmts[2]),
)
.expect("Missing expected tx result")
.as_ref()
.expect("Result is supposed to be Ok");
assert!(!inner_tx_result.is_accepted());
}
node.clear_results();
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)?;
for (owner, balance) in [
(AA_VIEWING_KEY, 0),
(adam_alias.as_ref(), 500_000),
(bradley_alias.as_ref(), 500_000),
] {
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
owner,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(&format!("nam: {balance}")));
}
Ok(())
}
#[test]
fn masp_failing_atomic_batch() -> Result<()> {
let validator_one_rpc = "http://127.0.0.1:26567";
let _ = FsShieldedUtils::new(PathBuf::new());
let (mut node, _services) = setup::setup()?;
_ = node.next_masp_epoch();
let tempdir = tempfile::tempdir().unwrap();
let (adam_alias, adam_key) =
make_temp_account(&node, validator_one_rpc, "Adam", NAM, 0)?;
for owner in [AA_VIEWING_KEY, adam_alias.as_ref()] {
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
owner,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0"));
}
for target in [AA_PAYMENT_ADDRESS, AC_PAYMENT_ADDRESS] {
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
ALBERT,
"--target",
target,
"--token",
NAM,
"--amount",
"1000",
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
}
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
AC_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)?;
for owner in [AA_VIEWING_KEY, AC_VIEWING_KEY] {
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
owner,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 1000"));
}
let mut batch = vec![];
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"unshield",
"--source",
A_SPENDING_KEY,
"--target",
adam_alias.as_ref(),
"--token",
NAM,
"--amount",
"1",
"--gas-limit",
"50000",
"--gas-price",
"0.00001",
"--gas-spending-key",
A_SPENDING_KEY,
"--gas-payer",
adam_alias.as_ref(),
"--output-folder-path",
tempdir.path().to_str().unwrap(),
"--dump-wrapper-tx",
"--ledger-address",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
let file_path = tempdir
.path()
.read_dir()
.unwrap()
.next()
.unwrap()
.unwrap()
.path();
batch.push(std::fs::read(&file_path).unwrap());
std::fs::remove_file(&file_path).unwrap();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"transfer",
"--source",
C_SPENDING_KEY,
"--target",
AB_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"1",
"--gas-payer",
CHRISTEL_KEY,
"--output-folder-path",
tempdir.path().to_str().unwrap(),
"--dump-wrapper-tx",
"--ledger-address",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
let file_path = tempdir
.path()
.read_dir()
.unwrap()
.next()
.unwrap()
.unwrap()
.path();
batch.push(std::fs::read(&file_path).unwrap());
std::fs::remove_file(&file_path).unwrap();
let tx0: namada_sdk::tx::Tx = serde_json::from_slice(&batch[0]).unwrap();
let tx1: namada_sdk::tx::Tx = serde_json::from_slice(&batch[1]).unwrap();
let signing_data = SigningTxData {
owner: None,
public_keys: [adam_key.to_public()].into(),
threshold: 1,
account_public_keys_map: None,
fee_payer: Either::Left((adam_key.to_public(), false)),
shielded_hash: None,
signatures: vec![],
};
let (mut batched_tx, _signing_data) = namada_sdk::tx::build_batch(vec![
(
tx0.clone(),
SigningTxData {
shielded_hash: get_shielded_hash(&tx0),
..signing_data.clone()
},
),
(
tx1.clone(),
SigningTxData {
shielded_hash: get_shielded_hash(&tx1),
..signing_data.clone()
},
),
])
.unwrap();
batched_tx.header.atomic = true;
batched_tx.sign_wrapper(adam_key.clone());
let wrapper_hash = batched_tx.wrapper_hash();
let mut inner_cmts = vec![];
for cmt in batched_tx.commitments() {
inner_cmts.push(cmt.to_owned());
}
node.clear_results();
node.submit_txs(vec![batched_tx.to_bytes()]);
{
let codes = node.tx_result_codes.lock().unwrap();
assert_eq!(codes.len(), 1);
assert!(matches!(
codes[0],
NodeResults::Failed(
namada_node::shell::ResultCode::WasmRuntimeError
)
));
let results = node.tx_results.lock().unwrap();
assert_eq!(results.len(), 1);
let res0 = &results[0];
assert_eq!(res0.len(), 2);
let inner_tx_result = res0
.get_inner_tx_result(
wrapper_hash.as_ref(),
itertools::Either::Right(&inner_cmts[0]),
)
.expect("Missing expected tx result")
.as_ref()
.expect("Result is supposed to be Ok");
assert!(inner_tx_result.is_accepted());
let inner_tx_result = res0
.get_inner_tx_result(
wrapper_hash.as_ref(),
itertools::Either::Right(&inner_cmts[1]),
)
.expect("Missing expected tx result")
.as_ref();
assert!(inner_tx_result.is_err());
}
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
AB_VIEWING_KEY,
AC_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)?;
for (owner, balance) in [
(AA_VIEWING_KEY, 998.5),
(adam_alias.as_ref(), 1.0),
(AB_VIEWING_KEY, 0.0),
(AC_VIEWING_KEY, 1000.0),
] {
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
owner,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(&format!("nam: {balance}")));
}
Ok(())
}
#[test]
fn tricky_masp_txs() -> Result<()> {
let validator_one_rpc = "http://127.0.0.1:26567";
let _ = FsShieldedUtils::new(PathBuf::new());
let (mut node, _services) = setup::initialize_genesis(|mut genesis| {
genesis.parameters.parameters.epochs_per_year = 15_768_000;
genesis
})?;
_ = node.next_masp_epoch();
let tempdir = tempfile::tempdir().unwrap();
let (adam_alias, _adam_key) =
make_temp_account(&node, validator_one_rpc, "Adam", NAM, 500_000)?;
let (arthur_alias, arthur_key) =
make_temp_account(&node, validator_one_rpc, "Arthur", NAM, 500_000)?;
let (bradley_alias, bradley_key) =
make_temp_account(&node, validator_one_rpc, "Bradley", NAM, 500_000)?;
let (cooper_alias, _cooper_key) =
make_temp_account(&node, validator_one_rpc, "Cooper", NAM, 500_000)?;
for (owner, balance) in [
(AA_VIEWING_KEY, 0),
(arthur_alias.as_ref(), 500_000),
(bradley_alias.as_ref(), 500_000),
(adam_alias.as_ref(), 500_000),
(cooper_alias.as_ref(), 500_000),
] {
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
owner,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(&format!("nam: {balance}")));
}
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"shield",
"--source",
adam_alias.as_ref(),
"--target",
AA_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"1000",
"--gas-payer",
cooper_alias.as_ref(),
"--output-folder-path",
tempdir.path().to_str().unwrap(),
"--dump-tx",
"--ledger-address",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
let file_path = tempdir
.path()
.read_dir()
.unwrap()
.next()
.unwrap()
.unwrap()
.path();
let masp_tx_bytes = std::fs::read(&file_path).unwrap();
std::fs::remove_file(&file_path).unwrap();
let masp_tx: namada_sdk::tx::Tx =
serde_json::from_slice(&masp_tx_bytes).unwrap();
let masp_transaction = masp_tx
.sections
.into_iter()
.find_map(|sec| sec.masp_tx())
.unwrap();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"transparent-transfer",
"--source",
arthur_alias.as_ref(),
"--target",
cooper_alias.as_ref(),
"--token",
NAM,
"--amount",
"1000",
"--gas-payer",
FRANK_KEY,
"--output-folder-path",
tempdir.path().to_str().unwrap(),
"--dump-wrapper-tx",
"--ledger-address",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
let file_path = tempdir
.path()
.read_dir()
.unwrap()
.next()
.unwrap()
.unwrap()
.path();
let tx_bytes = std::fs::read(&file_path).unwrap();
std::fs::remove_file(&file_path).unwrap();
let mut tx0: namada_sdk::tx::Tx =
serde_json::from_slice(&tx_bytes).unwrap();
tx0.add_masp_tx_section(masp_transaction.clone());
tx0.sign_raw(
vec![arthur_key.clone()],
AccountPublicKeysMap::from_iter(
vec![(arthur_key.to_public())].into_iter(),
),
None,
);
tx0.sign_wrapper(get_unencrypted_keypair("frank-key"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"shield",
"--source",
bradley_alias.as_ref(),
"--target",
AA_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"1000",
"--gas-payer",
FRANK_KEY,
"--output-folder-path",
tempdir.path().to_str().unwrap(),
"--dump-wrapper-tx",
"--ledger-address",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
let file_path = tempdir
.path()
.read_dir()
.unwrap()
.next()
.unwrap()
.unwrap()
.path();
let tx_bytes = std::fs::read(&file_path).unwrap();
std::fs::remove_file(&file_path).unwrap();
let mut tx1: namada_sdk::tx::Tx =
serde_json::from_slice(&tx_bytes).unwrap();
tx1.add_masp_tx_section(masp_transaction);
tx1.sign_raw(
vec![bradley_key.clone()],
AccountPublicKeysMap::from_iter(
vec![(bradley_key.to_public())].into_iter(),
),
None,
);
tx1.sign_wrapper(get_unencrypted_keypair("frank-key"));
let txs = vec![tx0.to_bytes(), tx1.to_bytes()];
node.clear_results();
node.submit_txs(txs);
node.assert_success();
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)?;
for (owner, balance) in [
(AA_VIEWING_KEY, 1_000),
(arthur_alias.as_ref(), 499_000),
(bradley_alias.as_ref(), 499_000),
(adam_alias.as_ref(), 500_000),
(cooper_alias.as_ref(), 501_000),
] {
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
owner,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(&format!("nam: {balance}")));
}
Ok(())
}
#[test]
fn speculative_context() -> Result<()> {
let validator_one_rpc = "http://127.0.0.1:26567";
let _ = FsShieldedUtils::new(PathBuf::new());
let (mut node, _services) = setup::setup()?;
_ = node.next_masp_epoch();
for _ in 0..2 {
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
ALBERT,
"--target",
AA_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"100",
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
}
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 200"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"transfer",
"--source",
A_SPENDING_KEY,
"--target",
AB_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"90",
"--gas-payer",
ALBERT_KEY,
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 100"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"transfer",
"--source",
A_SPENDING_KEY,
"--target",
AB_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"90",
"--gas-payer",
ALBERT_KEY,
"--gas-limit",
"10000",
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(
"Gas error: Transaction gas exceeded the limit of 10000 gas units"
));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 100"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"transfer",
"--source",
A_SPENDING_KEY,
"--target",
AB_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"90",
"--gas-payer",
ALBERT_KEY,
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0"));
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
AB_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 20"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AB_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 180"));
Ok(())
}
#[test]
fn masp_events() -> Result<()> {
let validator_one_rpc = "http://127.0.0.1:26567";
let _ = FsShieldedUtils::new(PathBuf::new());
let (mut node, _services) = setup::setup()?;
_ = node.next_masp_epoch();
let native_token = node
.shell
.lock()
.unwrap()
.state
.in_mem()
.native_token
.clone();
let (adam_alias, adam_key) =
make_temp_account(&node, validator_one_rpc, "Adam", NAM, 100_000)?;
let adam_pk = adam_key.to_public();
let (bradley_alias, bradley_key) =
make_temp_account(&node, validator_one_rpc, "Bradley", NAM, 0)?;
let bradley_pk = bradley_key.to_public();
let (cooper_alias, cooper_key) =
make_temp_account(&node, validator_one_rpc, "Cooper", NAM, 0)?;
let cooper_pk = cooper_key.to_public();
for target in [AA_PAYMENT_ADDRESS, AC_PAYMENT_ADDRESS] {
for _ in 0..2 {
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
ALBERT,
"--target",
target,
"--token",
NAM,
"--amount",
"500",
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
}
}
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
AC_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)?;
for owner in [AA_VIEWING_KEY, AC_VIEWING_KEY] {
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
owner,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 1000"));
}
let tempdir = tempfile::tempdir().unwrap();
let mut txs_bytes = vec![];
let mut notes = BTreeMap::new();
let tree_key = token::storage_key::masp_commitment_tree_key();
let mut commitment_tree: CommitmentTree<Node> = node
.shell
.lock()
.unwrap()
.state
.read(&tree_key)
.unwrap()
.unwrap();
assert_eq!(commitment_tree.size(), 4);
_ = node.next_epoch();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
adam_alias.as_ref(),
"--target",
AA_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"1000",
"--output-folder-path",
tempdir.path().to_str().unwrap(),
"--dump-tx",
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
let file_path = tempdir
.path()
.read_dir()
.unwrap()
.next()
.unwrap()
.unwrap()
.path();
let bytes = std::fs::read(&file_path).unwrap();
let tx = Tx::try_from_json_bytes(&bytes).unwrap();
let outputs = tx
.sections
.iter()
.find_map(|section| section.masp_tx())
.unwrap()
.sapling_bundle()
.unwrap()
.shielded_outputs
.clone();
notes.insert(2, outputs);
txs_bytes.push(bytes);
std::fs::remove_file(&file_path).unwrap();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"unshield",
"--source",
C_SPENDING_KEY,
"--target",
cooper_alias.as_ref(),
"--token",
NAM,
"--amount",
"1",
"--gas-limit",
"100000",
"--gas-price",
"0.00001",
"--gas-payer",
cooper_alias.as_ref(),
"--gas-spending-key",
C_SPENDING_KEY,
"--output-folder-path",
tempdir.path().to_str().unwrap(),
"--dump-tx",
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
let file_path = tempdir
.path()
.read_dir()
.unwrap()
.next()
.unwrap()
.unwrap()
.path();
let batch_tx0_bytes = std::fs::read(&file_path).unwrap();
std::fs::remove_file(&file_path).unwrap();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"transfer",
"--source",
C_SPENDING_KEY,
"--target",
AB_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"1",
"--gas-payer",
CHRISTEL_KEY,
"--output-folder-path",
tempdir.path().to_str().unwrap(),
"--dump-tx",
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
let file_path = tempdir
.path()
.read_dir()
.unwrap()
.next()
.unwrap()
.unwrap()
.path();
let batch_tx1_bytes = std::fs::read(&file_path).unwrap();
std::fs::remove_file(&file_path).unwrap();
let tx0: namada_sdk::tx::Tx =
serde_json::from_slice(&batch_tx0_bytes).unwrap();
let tx1: namada_sdk::tx::Tx =
serde_json::from_slice(&batch_tx1_bytes).unwrap();
let outputs = tx0
.sections
.iter()
.find_map(|section| section.masp_tx())
.unwrap()
.sapling_bundle()
.unwrap()
.shielded_outputs
.clone();
notes.insert(0, outputs);
let outputs = tx1
.sections
.iter()
.find_map(|section| section.masp_tx())
.unwrap()
.sapling_bundle()
.unwrap()
.shielded_outputs
.clone();
notes.insert(3, outputs);
let signing_data = SigningTxData {
owner: None,
public_keys: [cooper_pk.clone()].into(),
threshold: 1,
account_public_keys_map: None,
fee_payer: Either::Left((cooper_pk.clone(), false)),
shielded_hash: None,
signatures: vec![],
};
let (batched_tx, _signing_data) = namada_sdk::tx::build_batch(vec![
(
tx0.clone(),
SigningTxData {
shielded_hash: get_shielded_hash(&tx0),
..signing_data.clone()
},
),
(
tx1.clone(),
SigningTxData {
shielded_hash: get_shielded_hash(&tx1),
..signing_data.clone()
},
),
])
.unwrap();
let mut buffer = vec![];
batched_tx.to_writer_json(&mut buffer).unwrap();
txs_bytes.push(buffer);
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"transfer",
"--source",
A_SPENDING_KEY,
"--target",
AB_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"1",
"--gas-spending-key",
A_SPENDING_KEY,
"--gas-payer",
bradley_alias.as_ref(),
"--gas-limit",
"100000",
"--gas-price",
"0.00001",
"--output-folder-path",
tempdir.path().to_str().unwrap(),
"--dump-tx",
"--ledger-address",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
let file_path = tempdir
.path()
.read_dir()
.unwrap()
.next()
.unwrap()
.unwrap()
.path();
let bytes = std::fs::read(&file_path).unwrap();
let tx = Tx::try_from_json_bytes(&bytes).unwrap();
let outputs = tx
.sections
.iter()
.find_map(|section| section.masp_tx())
.unwrap()
.sapling_bundle()
.unwrap()
.shielded_outputs
.clone();
notes.insert(1, outputs);
txs_bytes.push(bytes);
std::fs::remove_file(&file_path).unwrap();
let mut txs = vec![];
for (idx, bytes) in txs_bytes.iter().enumerate() {
let (sk, pk) = if idx == 0 {
(adam_key.clone(), adam_pk.clone())
} else if idx == 1 {
(cooper_key.clone(), cooper_pk.clone())
} else {
(bradley_key.clone(), bradley_pk.clone())
};
let mut tx = Tx::try_from_json_bytes(bytes).unwrap();
tx.add_wrapper(
tx::data::wrapper::Fee {
amount_per_gas_unit: DenominatedAmount::native(10.into()),
token: native_token.clone(),
},
pk.clone(),
100_000.into(),
);
tx.sign_raw(
vec![sk.clone()],
AccountPublicKeysMap::from_iter(vec![(pk)].into_iter()),
None,
);
tx.sign_wrapper(sk);
txs.push(tx.to_bytes());
}
node.clear_results();
node.submit_txs(txs);
assert!(!node.tx_result_codes.lock().unwrap().is_empty());
node.assert_success();
for (_, note_collection) in notes {
for description in note_collection {
commitment_tree
.append(Node::from_scalar(description.cmu))
.unwrap();
}
}
let storage_commitment_tree: CommitmentTree<Node> = node
.shell
.lock()
.unwrap()
.state
.read(&tree_key)
.unwrap()
.unwrap();
assert_eq!(commitment_tree, storage_commitment_tree);
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
AB_VIEWING_KEY,
AC_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 1998"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AB_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 2"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AC_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 997"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"unshield",
"--source",
A_SPENDING_KEY,
"--target",
bradley_alias.as_ref(),
"--token",
NAM,
"--amount",
"1998",
"--gas-limit",
"100000",
"--gas-payer",
CHRISTEL_KEY,
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"unshield",
"--source",
B_SPENDING_KEY,
"--target",
bradley_alias.as_ref(),
"--token",
NAM,
"--amount",
"2",
"--gas-limit",
"100000",
"--gas-payer",
CHRISTEL_KEY,
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"unshield",
"--source",
C_SPENDING_KEY,
"--target",
bradley_alias.as_ref(),
"--token",
NAM,
"--amount",
"997",
"--gas-limit",
"100000",
"--gas-payer",
CHRISTEL_KEY,
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
AB_VIEWING_KEY,
AC_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)?;
for owner in [AA_VIEWING_KEY, AB_VIEWING_KEY, AC_VIEWING_KEY] {
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
owner,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0"));
}
Ok(())
}
#[test]
fn multiple_inputs_from_single_note() -> Result<()> {
let validator_one_rpc = "http://127.0.0.1:26567";
let _ = FsShieldedUtils::new(PathBuf::new());
let (mut node, _services) = setup::initialize_genesis(|mut genesis| {
genesis.parameters.parameters.minimum_gas_price.insert(
"btc".into(),
DenominatedAmount::new(1.into(), token::Denomination(6)),
);
genesis
})?;
node.next_masp_epoch();
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"shield",
"--source",
ALBERT,
"--target",
AA_PAYMENT_ADDRESS,
"--token",
BTC,
"--amount",
"10",
"--signing-keys",
ALBERT_KEY,
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok());
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
AB_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 10"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0"));
node.next_masp_epoch();
run(
&node,
Bin::Client,
vec!["shielded-sync", "--node", validator_one_rpc],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 10"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0.06"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
MASP,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0.06"));
let tree_key = token::storage_key::masp_commitment_tree_key();
let commitment_tree: CommitmentTree<Node> = node
.shell
.lock()
.unwrap()
.state
.read(&tree_key)
.unwrap()
.unwrap();
assert_eq!(commitment_tree.size(), 1);
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
apply_use_device(vec![
"transfer",
"--source",
A_SPENDING_KEY,
"--target",
AB_PAYMENT_ADDRESS,
"--token",
NAM,
"--amount",
"0.06",
"--gas-token",
BTC,
"--gas-limit",
"100000",
"--gas-price",
"0.000001",
"--node",
validator_one_rpc,
]),
)
});
assert!(captured.result.is_ok(), "{:?}", captured.result);
assert!(captured.contains(TX_APPLIED_SUCCESS));
run(
&node,
Bin::Client,
vec![
"shielded-sync",
"--viewing-keys",
AA_VIEWING_KEY,
AB_VIEWING_KEY,
"--node",
validator_one_rpc,
],
)?;
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AB_VIEWING_KEY,
"--token",
NAM,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("nam: 0.06"));
let captured = CapturedOutput::of(|| {
run(
&node,
Bin::Client,
vec![
"balance",
"--owner",
AA_VIEWING_KEY,
"--token",
BTC,
"--node",
validator_one_rpc,
],
)
});
assert!(captured.result.is_ok());
assert!(captured.contains("btc: 9.9"));
Ok(())
}