diff --git a/.github/workflows/scripts/e2e.json b/.github/workflows/scripts/e2e.json index 51ac256df2..935ad33ddf 100644 --- a/.github/workflows/scripts/e2e.json +++ b/.github/workflows/scripts/e2e.json @@ -33,5 +33,6 @@ "e2e::wallet_tests::wallet_unencrypted_key_cmds": 1, "e2e::ledger_tests::masp_txs_and_queries": 82, "e2e::ledger_tests::test_genesis_chain_id_change": 35, - "e2e::ledger_tests::test_genesis_manipulation": 103 -} + "e2e::ledger_tests::test_genesis_manipulation": 103, + "e2e::ledger_tests::test_mainnet_phases": 1706 +} \ No newline at end of file diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index e2b1e56600..ccb2a9e3fd 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs @@ -27,8 +27,12 @@ pub enum TestWasms { TxInfiniteGuestGas, TxInfiniteHostGas, TxProposalCode, - TxProposalMaspRewards, TxProposalIbcTokenInflation, + TxProposalMaspRewards, + TxProposalPhase2, + TxProposalPhase3, + TxProposalPhase4, + TxProposalPhase5, TxProposalTokenGas, TxReadStorageKey, TxWriteStorageKey, @@ -56,6 +60,10 @@ impl TestWasms { TestWasms::TxInfiniteGuestGas => "tx_infinite_guest_gas.wasm", TestWasms::TxInfiniteHostGas => "tx_infinite_host_gas.wasm", TestWasms::TxProposalCode => "tx_proposal_code.wasm", + TestWasms::TxProposalPhase2 => "tx_proposal_phase2.wasm", + TestWasms::TxProposalPhase3 => "tx_proposal_phase3.wasm", + TestWasms::TxProposalPhase4 => "tx_proposal_phase4.wasm", + TestWasms::TxProposalPhase5 => "tx_proposal_phase5.wasm", TestWasms::TxProposalMaspRewards => "tx_proposal_masp_reward.wasm", TestWasms::TxProposalIbcTokenInflation => { "tx_proposal_ibc_token_inflation.wasm" diff --git a/crates/tests/src/e2e/helpers.rs b/crates/tests/src/e2e/helpers.rs index ab3579a1a2..5d1024a242 100644 --- a/crates/tests/src/e2e/helpers.rs +++ b/crates/tests/src/e2e/helpers.rs @@ -9,15 +9,14 @@ use std::str::FromStr; use std::time::{Duration, Instant}; use std::{env, time}; -use borsh::BorshDeserialize; use color_eyre::eyre::Result; use color_eyre::owo_colors::OwoColorize; -use data_encoding::HEXLOWER; use escargot::CargoBuild; use eyre::eyre; use namada_apps_lib::cli::context::ENV_VAR_CHAIN_ID; use namada_apps_lib::config::utils::convert_tm_addr_to_socket_addr; use namada_apps_lib::config::{Config, TendermintMode}; +use namada_apps_lib::wallet; use namada_core::token::NATIVE_MAX_DECIMAL_PLACES; use namada_sdk::address::Address; use namada_sdk::chain::Epoch; @@ -128,38 +127,30 @@ pub fn find_address(test: &Test, alias: impl AsRef) -> Result
{ Ok(address) } -/// Find the balance of specific token for an account. -#[allow(dead_code)] +/// Find balance of specific token for an account. pub fn find_balance( test: &Test, node: Who, - token: &Address, - owner: &Address, + token: &str, + owner: &str, + denom: Option, + balance_pattern: Option<&str>, ) -> Result { - let ledger_address = get_actor_rpc(test, node); - let balance_key = token::storage_key::balance_key(token, owner); - let mut bytes = run!( - test, - Bin::Client, - &[ - "query-bytes", - "--storage-key", - &balance_key.to_string(), - "--ledger-address", - &ledger_address, - ], - Some(10) - )?; - let (_, matched) = bytes.exp_regex("Found data: 0x.*")?; + let rpc = get_actor_rpc(test, node); + let query_args = vec![ + "balance", "--owner", &owner, "--token", &token, "--node", &rpc, + ]; + let mut client = run!(test, Bin::Client, query_args, Some(40))?; + let token = wallet::Alias::from(token).to_string(); + let balance_pattern = balance_pattern.unwrap_or(&token); + let (_unread, matched) = + client.exp_regex(&format!(r"{balance_pattern}: [0-9.]+"))?; let data_str = strip_trailing_newline(&matched) .trim() .rsplit_once(' ') .unwrap() - .1[2..] - .to_string(); - let amount = - token::Amount::try_from_slice(&HEXLOWER.decode(data_str.as_bytes())?)?; - bytes.assert_success(); + .1; + let amount = token::Amount::from_str(data_str, denom.unwrap_or(6)).unwrap(); Ok(amount) } @@ -738,3 +729,46 @@ pub fn get_cosmos_gov_address(test: &Test) -> Result { Ok(matched.trim().to_string()) } + +pub fn check_balance( + test: &Test, + owner: impl AsRef, + token: impl AsRef, + expected_amount: u64, +) -> Result<()> { + let rpc = get_actor_rpc(test, Who::Validator(0)); + + if owner.as_ref().starts_with("zvk") { + shielded_sync(test, owner.as_ref())?; + } + + let query_args = vec![ + "balance", + "--owner", + owner.as_ref(), + "--token", + token.as_ref(), + "--node", + &rpc, + ]; + let mut client = run!(test, Bin::Client, query_args, Some(40))?; + let expected = + format!("{}: {expected_amount}", token.as_ref().to_lowercase()); + client.exp_string(&expected)?; + client.assert_success(); + Ok(()) +} + +pub fn shielded_sync(test: &Test, viewing_key: impl AsRef) -> Result<()> { + let rpc = get_actor_rpc(test, Who::Validator(0)); + let tx_args = vec![ + "shielded-sync", + "--viewing-keys", + viewing_key.as_ref(), + "--node", + &rpc, + ]; + let mut client = run!(test, Bin::Client, tx_args, Some(120))?; + client.assert_success(); + Ok(()) +} diff --git a/crates/tests/src/e2e/ibc_tests.rs b/crates/tests/src/e2e/ibc_tests.rs index bde2ca2fa3..c8be6c45b4 100644 --- a/crates/tests/src/e2e/ibc_tests.rs +++ b/crates/tests/src/e2e/ibc_tests.rs @@ -45,9 +45,11 @@ use prost::Message; use setup::constants::*; use sha2::{Digest, Sha256}; +use super::helpers::check_balance; use crate::e2e::helpers::{ epoch_sleep, epochs_per_year_from_min_duration, find_address, find_cosmos_address, get_actor_rpc, get_cosmos_gov_address, get_epoch, + shielded_sync, }; use crate::e2e::ledger_tests::{ start_namada_ledger_node_wait_wasm, write_json_file, @@ -100,7 +102,7 @@ fn ibc_transfers() -> Result<()> { setup::set_validators(1, genesis, base_dir, |_| 0, vec![]) }; let (ledger, gaia, test, test_gaia) = - run_namada_cosmos(CosmosChainType::Gaia, update_genesis)?; + run_namada_cosmos(CosmosChainType::Gaia, update_genesis, None)?; let _bg_ledger = ledger.background(); let _bg_gaia = gaia.background(); @@ -473,7 +475,7 @@ fn ibc_nft_transfers() -> Result<()> { setup::set_validators(1, genesis, base_dir, |_| 0, vec![]) }; let (ledger, cosmwasm, test, test_cosmwasm) = - run_namada_cosmos(CosmosChainType::CosmWasm, update_genesis)?; + run_namada_cosmos(CosmosChainType::CosmWasm, update_genesis, None)?; let _bg_ledger = ledger.background(); let _bg_wasmd = cosmwasm.background(); @@ -607,7 +609,7 @@ fn pgf_over_ibc() -> Result<()> { setup::set_validators(1, genesis, base_dir, |_| 0, vec![]) }; let (ledger, gaia, test, test_gaia) = - run_namada_cosmos(CosmosChainType::Gaia, update_genesis)?; + run_namada_cosmos(CosmosChainType::Gaia, update_genesis, None)?; let _bg_ledger = ledger.background(); let _bg_gaia = gaia.background(); @@ -704,7 +706,7 @@ fn fee_payment_with_ibc_token() -> Result<()> { setup::set_validators(1, genesis, base_dir, |_| 0, vec![]) }; let (ledger, gaia, test, test_gaia) = - run_namada_cosmos(CosmosChainType::Gaia, update_genesis)?; + run_namada_cosmos(CosmosChainType::Gaia, update_genesis, None)?; let _bg_ledger = ledger.background(); let _bg_gaia = gaia.background(); @@ -810,7 +812,7 @@ fn ibc_token_inflation() -> Result<()> { setup::set_validators(1, genesis, base_dir, |_| 0, vec![]) }; let (ledger, gaia, test, test_gaia) = - run_namada_cosmos(CosmosChainType::Gaia, update_genesis)?; + run_namada_cosmos(CosmosChainType::Gaia, update_genesis, None)?; let _bg_ledger = ledger.background(); let _bg_gaia = gaia.background(); @@ -900,7 +902,7 @@ fn ibc_upgrade_client() -> Result<()> { setup::set_validators(1, genesis, base_dir, |_| 0, vec![]) }; let (ledger, gaia, test, test_gaia) = - run_namada_cosmos(CosmosChainType::Gaia, update_genesis)?; + run_namada_cosmos(CosmosChainType::Gaia, update_genesis, None)?; let _bg_ledger = ledger.background(); let _bg_gaia = gaia.background(); @@ -971,7 +973,7 @@ fn ibc_rate_limit() -> Result<()> { setup::set_validators(1, genesis, base_dir, |_| 0, vec![]) }; let (ledger, gaia, test, test_gaia) = - run_namada_cosmos(CosmosChainType::Gaia, update_genesis)?; + run_namada_cosmos(CosmosChainType::Gaia, update_genesis, None)?; let _bg_ledger = ledger.background(); let _bg_gaia = gaia.background(); @@ -1088,12 +1090,13 @@ fn ibc_rate_limit() -> Result<()> { Ok(()) } -fn run_namada_cosmos( +pub fn run_namada_cosmos( chain_type: CosmosChainType, mut update_genesis: impl FnMut( templates::All, &Path, ) -> templates::All, + cosmos_user_balance: Option, ) -> Result<(NamadaCmd, NamadaCmd, Test, Test)> { let test = setup::network(&mut update_genesis, None)?; @@ -1108,14 +1111,14 @@ fn run_namada_cosmos( let ledger = start_namada_ledger_node_wait_wasm(&test, Some(0), Some(40))?; // Cosmos - let test_cosmos = setup_cosmos(chain_type)?; + let test_cosmos = setup_cosmos(chain_type, cosmos_user_balance)?; let cosmos = run_cosmos(&test_cosmos)?; sleep(5); Ok((ledger, cosmos, test, test_cosmos)) } -fn create_channel_with_hermes( +pub fn create_channel_with_hermes( test_a: &Test, test_b: &Test, port_id_a: &PortId, @@ -1167,7 +1170,7 @@ fn get_channel_ids_from_hermes_output( Ok((channel_id_a, channel_id_b)) } -fn run_hermes(test: &Test) -> Result { +pub fn run_hermes(test: &Test) -> Result { let args = ["start"]; let mut hermes = run_hermes_cmd(test, args, Some(40))?; hermes.exp_string("Hermes has started")?; @@ -1195,7 +1198,7 @@ fn run_cosmos(test: &Test) -> Result { Ok(cosmos) } -fn wait_for_packet_relay( +pub fn wait_for_packet_relay( port_id: &PortId, channel_id: &ChannelId, test: &Test, @@ -1234,7 +1237,7 @@ fn wait_for_packet_relay( Err(eyre!("Pending packet is still left")) } -fn clear_packet( +pub fn clear_packet( port_id: &PortId, channel_id: &ChannelId, test: &Test, @@ -1384,7 +1387,7 @@ fn transfer_on_chain( } #[allow(clippy::too_many_arguments)] -fn transfer( +pub fn transfer( test: &Test, sender: impl AsRef, receiver: impl AsRef, @@ -1797,7 +1800,7 @@ fn submit_votes(test: &Test) -> Result<()> { } #[allow(clippy::too_many_arguments)] -fn transfer_from_cosmos( +pub fn transfer_from_cosmos( test: &Test, sender: impl AsRef, receiver: impl AsRef, @@ -1898,35 +1901,6 @@ fn query_height(test: &Test) -> Result { Ok(Height::new(0, status.sync_info.latest_block_height.into()).unwrap()) } -fn check_balance( - test: &Test, - owner: impl AsRef, - token: impl AsRef, - expected_amount: u64, -) -> Result<()> { - let rpc = get_actor_rpc(test, Who::Validator(0)); - - if owner.as_ref().starts_with("zvk") { - shielded_sync(test, owner.as_ref())?; - } - - let query_args = vec![ - "balance", - "--owner", - owner.as_ref(), - "--token", - token.as_ref(), - "--node", - &rpc, - ]; - let mut client = run!(test, Bin::Client, query_args, Some(40))?; - let expected = - format!("{}: {expected_amount}", token.as_ref().to_lowercase()); - client.exp_string(&expected)?; - client.assert_success(); - Ok(()) -} - fn get_gaia_denom_hash(denom: impl AsRef) -> String { let mut hasher = Sha256::new(); hasher.update(denom.as_ref()); @@ -1934,7 +1908,7 @@ fn get_gaia_denom_hash(denom: impl AsRef) -> String { format!("ibc/{hash:X}") } -fn check_cosmos_balance( +pub fn check_cosmos_balance( test: &Test, owner: impl AsRef, denom: impl AsRef, @@ -1987,23 +1961,9 @@ fn check_inflated_balance( Ok(()) } -fn shielded_sync(test: &Test, viewing_key: impl AsRef) -> Result<()> { - let rpc = get_actor_rpc(test, Who::Validator(0)); - let tx_args = vec![ - "shielded-sync", - "--viewing-keys", - viewing_key.as_ref(), - "--node", - &rpc, - ]; - let mut client = run!(test, Bin::Client, tx_args, Some(120))?; - client.assert_success(); - Ok(()) -} - /// Get IBC shielding data for the following IBC transfer from the destination /// chain -fn gen_ibc_shielding_data( +pub fn gen_ibc_shielding_data( dst_test: &Test, receiver: impl AsRef, token: impl AsRef, diff --git a/crates/tests/src/e2e/ledger_tests.rs b/crates/tests/src/e2e/ledger_tests.rs index cdccd01470..2b8d51d451 100644 --- a/crates/tests/src/e2e/ledger_tests.rs +++ b/crates/tests/src/e2e/ledger_tests.rs @@ -10,6 +10,7 @@ //! `NAMADA_E2E_KEEP_TEMP=true`. #![allow(clippy::type_complexity)] +use std::collections::BTreeSet; use std::env; use std::fmt::Display; use std::path::PathBuf; @@ -27,11 +28,15 @@ use namada_apps_lib::config::genesis::templates::TokenBalances; use namada_apps_lib::config::utils::convert_tm_addr_to_socket_addr; use namada_apps_lib::config::{self, ethereum_bridge}; use namada_apps_lib::tendermint_config::net::Address as TendermintAddress; -use namada_apps_lib::wallet::{self, Alias}; +use namada_apps_lib::wallet::{self, defaults, Alias}; use namada_core::chain::ChainId; use namada_core::token::NATIVE_MAX_DECIMAL_PLACES; use namada_sdk::address::Address; use namada_sdk::chain::{ChainIdPrefix, Epoch}; +use namada_sdk::dec::Dec; +use namada_sdk::governance::cli::onchain::StewardsUpdate; +use namada_sdk::ibc::core::host::types::identifiers::PortId; +use namada_sdk::ibc::trace::ibc_token; use namada_sdk::time::DateTimeUtc; use namada_sdk::token; use namada_test_utils::TestWasms; @@ -46,12 +51,14 @@ use super::helpers::{ }; use super::setup::{set_ethereum_bridge_mode, working_dir, NamadaCmd}; use crate::e2e::helpers::{ - epoch_sleep, find_address, find_bonded_stake, get_actor_rpc, get_epoch, - is_debug_mode, parse_reached_epoch, + check_balance, epoch_sleep, find_address, find_balance, find_bonded_stake, + find_cosmos_address, get_actor_rpc, get_epoch, is_debug_mode, + parse_reached_epoch, shielded_sync, }; +use crate::e2e::ibc_tests; use crate::e2e::setup::{ self, allow_duplicate_ips, apply_use_device, default_port_offset, sleep, - speculos_app_elf, speculos_path, Bin, Who, + speculos_app_elf, speculos_path, Bin, CosmosChainType, Who, }; use crate::strings::{ LEDGER_SHUTDOWN, LEDGER_STARTED, NON_VALIDATOR_NODE, TX_APPLIED_SUCCESS, @@ -1527,7 +1534,11 @@ pub fn prepare_proposal_data( source: Address, data: impl serde::Serialize, start_epoch: u64, + voting_end_offset: Option, + activation_offset: Option, ) -> PathBuf { + let voting_end_offset = voting_end_offset.unwrap_or(12); + let activation_offset = activation_offset.unwrap_or(6); let valid_proposal_json = json!({ "proposal": { "content": { @@ -1543,8 +1554,8 @@ pub fn prepare_proposal_data( }, "author": source, "voting_start_epoch": start_epoch, - "voting_end_epoch": start_epoch + 12_u64, - "activation_epoch": start_epoch + 12u64 + 6_u64, + "voting_end_epoch": start_epoch + voting_end_offset, + "activation_epoch": start_epoch + voting_end_offset + activation_offset, }, "data": data }); @@ -2075,6 +2086,8 @@ fn proposal_change_shielded_reward() -> Result<()> { albert, TestWasms::TxProposalMaspRewards.read_bytes(), 12, + None, + None, ); let validator_one_rpc = get_actor_rpc(&test, Who::Validator(0)); @@ -2808,3 +2821,601 @@ fn test_genesis_manipulation() -> Result<()> { Ok(()) } + +#[test] +fn test_mainnet_phases() -> Result<()> { + // Use minimum offsets for faster proposals + const VOTING_END_OFFSET: u64 = 1; + const ACTIVATION_OFFSET: u64 = 1; + + let (ledger, gaia, test, test_gaia) = ibc_tests::run_namada_cosmos( + CosmosChainType::Gaia, + |genesis, base_dir| { + let mut genesis = + setup::set_validators(1, genesis, base_dir, |_| 0u16, vec![]); + + // an epoch per 1 minute + genesis.parameters.parameters.epochs_per_year = + epochs_per_year_from_min_duration(60); + + // speed-up gov proposals + genesis.parameters.gov_params.min_proposal_voting_period = 1; + genesis.parameters.gov_params.min_proposal_grace_epochs = 1; + + // Disabled until Phase 2: staking rewards and PGF + genesis.parameters.pos_params.max_inflation_rate = Dec::zero(); + genesis.parameters.pos_params.target_staked_ratio = Dec::zero(); + genesis.parameters.pos_params.rewards_gain_p = Dec::zero(); + genesis.parameters.pos_params.rewards_gain_d = Dec::zero(); + + genesis.parameters.pgf_params.stewards_inflation_rate = Dec::zero(); + genesis.parameters.pgf_params.pgf_inflation_rate = Dec::zero(); + + genesis.parameters.pgf_params.stewards = BTreeSet::default(); + + // Disabled until Phase 4: shielding rewards + genesis.tokens.token.retain(|alias, config| { + // Zero-out rewards + let params = config.masp_params.as_mut().unwrap(); + params.kd_gain_nom = Dec::zero(); + params.kp_gain_nom = Dec::zero(); + params.max_reward_rate = Dec::zero(); + params.locked_amount_target = 0; + + // Remove tokens other than NAM + alias == &Alias::from("nam") + }); + genesis.balances.token.retain(|alias, _config| { + // Remove token balances other than NAM + alias == &Alias::from("nam") + }); + + // Disabled until Phase 5: NAM transfers + genesis.parameters.parameters.is_native_token_transferable = false; + + genesis + }, + Some(1_000_000), + ) + .unwrap(); + let _bg_ledger = ledger.background(); + let _bg_gaia = gaia.background(); + + setup::setup_hermes(&test, &test_gaia)?; + let port_id_namada: PortId = "transfer".parse().unwrap(); + let port_id_gaia: PortId = "transfer".parse().unwrap(); + let (channel_id_namada, channel_id_gaia) = + ibc_tests::create_channel_with_hermes( + &test, + &test_gaia, + &port_id_namada, + &port_id_gaia, + )?; + + // Start relaying + let hermes = ibc_tests::run_hermes(&test)?; + let _bg_hermes = hermes.background(); + + let validator_one_rpc = get_actor_rpc(&test, Who::Validator(0)); + + let mut proposal_id: u64 = 0; + + // Submit and activate a proposal to have 1 PGF steward + { + let steward = defaults::albert_address(); + let pgf_stewards = StewardsUpdate { + add: Some(steward.clone()), + remove: vec![], + }; + let next_epoch = get_epoch(&test, &validator_one_rpc)?.next(); + let valid_proposal_json_path = prepare_proposal_data( + test.test_dir.path(), + defaults::albert_address(), + pgf_stewards, + next_epoch.0, + Some(VOTING_END_OFFSET), + Some(ACTIVATION_OFFSET), + ); + let submit_proposal_args = apply_use_device(vec![ + "init-proposal", + "--pgf-stewards", + "--data-path", + valid_proposal_json_path.to_str().unwrap(), + "--ledger-address", + &validator_one_rpc, + ]); + let mut client = + run!(test, Bin::Client, submit_proposal_args, Some(40))?; + client.exp_string(TX_APPLIED_SUCCESS)?; + client.assert_success(); + + // Start the voting epoch + let proposal_id_str = proposal_id.to_string(); + let _epoch = epoch_sleep(&test, &validator_one_rpc, 120)?; + let submit_proposal_vote = apply_use_device(vec![ + "vote-proposal", + "--proposal-id", + &proposal_id_str, + "--vote", + "yay", + "--address", + "validator-0", + "--node", + &validator_one_rpc, + ]); + + let mut client = + run!(test, Bin::Client, submit_proposal_vote, Some(40))?; + client.exp_string(TX_APPLIED_SUCCESS)?; + client.assert_success(); + + proposal_id += 1; + } + + let mut submit_proposal = |tx: TestWasms| -> Result { + let next_epoch = get_epoch(&test, &validator_one_rpc)?.next(); + let activation_epoch = + next_epoch + VOTING_END_OFFSET + ACTIVATION_OFFSET; + + let valid_proposal_json_path = prepare_proposal_data( + test.test_dir.path(), + defaults::albert_address(), + tx.read_bytes(), + next_epoch.0, + Some(VOTING_END_OFFSET), + Some(ACTIVATION_OFFSET), + ); + + let submit_proposal_args = apply_use_device(vec![ + "init-proposal", + "--data-path", + valid_proposal_json_path.to_str().unwrap(), + "--gas-limit", + "2200000", + "--node", + &validator_one_rpc, + ]); + let mut client = + run!(test, Bin::Client, submit_proposal_args, Some(40))?; + client.exp_string(TX_APPLIED_SUCCESS)?; + client.assert_success(); + + // Start the voting epoch + let _epoch = epoch_sleep(&test, &validator_one_rpc, 120)?; + + let proposal_id_str = proposal_id.to_string(); + let submit_proposal_vote = apply_use_device(vec![ + "vote-proposal", + "--proposal-id", + &proposal_id_str, + "--vote", + "yay", + "--address", + "validator-0", + "--node", + &validator_one_rpc, + ]); + + let mut client = + run!(test, Bin::Client, submit_proposal_vote, Some(40))?; + client.exp_string(TX_APPLIED_SUCCESS)?; + client.assert_success(); + + proposal_id += 1; + + Ok(activation_epoch) + }; + + let namada_ibc_receiver = find_address(&test, ALBERT)?.to_string(); + let ibc_denom_on_namada = + format!("{port_id_namada}/{channel_id_namada}/{COSMOS_COIN}"); + let gaia_token = ibc_token(&ibc_denom_on_namada).to_string(); + let gaia_receiver = find_cosmos_address(&test_gaia, COSMOS_USER)?; + + // IBC transfers shouldn't be allowed before phase 3 + ibc_tests::transfer_from_cosmos( + &test_gaia, + COSMOS_USER, + &namada_ibc_receiver, + COSMOS_COIN, + 1, + &port_id_gaia, + &channel_id_gaia, + None, + None, + )?; + let _ = ibc_tests::wait_for_packet_relay( + &port_id_gaia, + &channel_id_gaia, + &test, + ); + // The tokens should NOT have been transferred to transparent address + check_balance(&test, ALBERT, &gaia_token, 0)?; + + ibc_tests::clear_packet(&port_id_gaia, &channel_id_gaia, &test)?; + + // Should not be able transfer NAM before phase 5 + let tx_args = apply_use_device(vec![ + "transparent-transfer", + "--source", + BERTHA, + "--target", + ALBERT, + "--token", + NAM, + "--amount", + "1", + "--node", + &validator_one_rpc, + ]); + let mut client = run!(test, Bin::Client, tx_args, Some(40))?; + client.exp_string(TX_REJECTED)?; + + // Submit a delegation to the first genesis validator + let tx_args = apply_use_device(vec![ + "bond", + "--validator", + "validator-0", + "--source", + BERTHA, + "--amount", + "5000.0", + "--signing-keys", + BERTHA_KEY, + "--node", + &validator_one_rpc, + ]); + let mut client = run!(test, Bin::Client, tx_args, Some(40))?; + client.exp_string(TX_APPLIED_SUCCESS)?; + client.assert_success(); + + let tx_args = apply_use_device(vec![ + "unbond", + "--validator", + "validator-0", + "--source", + BERTHA, + "--amount", + "1600.", + "--signing-keys", + BERTHA_KEY, + "--node", + &validator_one_rpc, + ]); + let mut client = run!(test, Bin::Client, tx_args, Some(40))?; + let expected = "Amount 1600.000000 withdrawable starting from epoch "; + let (_unread, matched) = client.exp_regex(&format!("{expected}.*\n"))?; + let epoch_raw = matched.trim().split_once(expected).unwrap().1; + let delegation_withdrawable_epoch = Epoch::from_str(epoch_raw).unwrap(); + client.assert_success(); + + let mut current_epoch = get_epoch(&test, &validator_one_rpc)?; + while current_epoch < delegation_withdrawable_epoch { + current_epoch = epoch_sleep(&test, &validator_one_rpc, 120)?; + } + + // Submit a withdrawal of the delegation + let tx_args = apply_use_device(vec![ + "withdraw", + "--validator", + "validator-0", + "--source", + BERTHA, + "--signing-keys", + BERTHA_KEY, + "--node", + &validator_one_rpc, + ]); + let mut client = run!(test, Bin::Client, tx_args, Some(40))?; + client.exp_string(TX_APPLIED_SUCCESS)?; + client.assert_success(); + + // Propose phase 2 - staking party 🥳 + let activation_epoch = submit_proposal(TestWasms::TxProposalPhase2)?; + + // Wait for phase 2 proposal activation + let mut current_epoch = get_epoch(&test, &validator_one_rpc)?; + while current_epoch < activation_epoch { + // There should be no staking rewards before phase 2 + let staking_rewards = + query_staking_rewards(&test, "validator-0", &validator_one_rpc)?; + assert_eq!(staking_rewards, token::Amount::zero()); + + // There should be no tokens in PGF address + let balance = find_balance( + &test, + Who::Validator(0), + NAM, + PGF_ADDRESS, + None, + None, + )?; + assert_eq!(balance, token::Amount::zero()); + + current_epoch = epoch_sleep(&test, &validator_one_rpc, 120)?; + } + + // Check staking rewards activation + let staking_rewards = + query_staking_rewards(&test, "validator-0", &validator_one_rpc)?; + assert_ne!(staking_rewards, token::Amount::zero()); + + // There should be some balance now in PGF address + let balance = + find_balance(&test, Who::Validator(0), NAM, PGF_ADDRESS, None, None)?; + assert_ne!(balance, token::Amount::zero()); + + // Propose phase 3 - shielding party 🥳 + let activation_epoch = submit_proposal(TestWasms::TxProposalPhase3)?; + + // IBC transfers shouldn't be allowed before phase 3 + ibc_tests::transfer_from_cosmos( + &test_gaia, + COSMOS_USER, + &namada_ibc_receiver, + COSMOS_COIN, + 1, + &port_id_gaia, + &channel_id_gaia, + None, + None, + )?; + + // // IBC shielding shouldn't be allowed before phase 3 + let shielding_data_path = ibc_tests::gen_ibc_shielding_data( + &test, + AA_PAYMENT_ADDRESS, + COSMOS_COIN, + 1, + &port_id_namada, + &channel_id_namada, + )?; + ibc_tests::transfer_from_cosmos( + &test_gaia, + COSMOS_USER, + AA_PAYMENT_ADDRESS, + COSMOS_COIN, + 1, + &port_id_gaia, + &channel_id_gaia, + Some(shielding_data_path), + None, + )?; + let _ = ibc_tests::wait_for_packet_relay( + &port_id_gaia, + &channel_id_gaia, + &test, + ); + + // Fetch note for the shielding transfer target + shielded_sync(&test, AA_VIEWING_KEY)?; + + // Wait for phase 3 proposal activation + let mut current_epoch = get_epoch(&test, &validator_one_rpc)?; + while current_epoch < activation_epoch { + // The tokens should NOT have been transferred to transparent address + check_balance(&test, ALBERT, &gaia_token, 0)?; + // The tokens should NOT have been transferred to shielded address + check_balance(&test, AA_VIEWING_KEY, &gaia_token, 0)?; + + current_epoch = epoch_sleep(&test, &validator_one_rpc, 120)?; + + // Clear the pending packets before activation + if current_epoch.next() == activation_epoch { + ibc_tests::clear_packet( + &port_id_namada, + &channel_id_namada, + &test, + )?; + ibc_tests::clear_packet(&port_id_gaia, &channel_id_gaia, &test)?; + } + } + + // Make sure we've entered a new MASP epoch before shielding to get rewards + epoch_sleep(&test, &validator_one_rpc, 120)?; + epoch_sleep(&test, &validator_one_rpc, 120)?; + + // IBC transfers should be allowed now + ibc_tests::transfer_from_cosmos( + &test_gaia, + COSMOS_USER, + &namada_ibc_receiver, + COSMOS_COIN, + 1, + &port_id_gaia, + &channel_id_gaia, + None, + None, + )?; + ibc_tests::wait_for_packet_relay(&port_id_gaia, &channel_id_gaia, &test)?; + + // The transparent tokens should have been transferred + let trans_balance = find_balance( + &test, + Who::Validator(0), + &gaia_token, + ALBERT, + Some(0), + Some(&ibc_denom_on_namada), + )?; + assert_ne!(trans_balance, token::Amount::zero()); + + // IBC shielding should be allowed now + // Shield a larger amount of IBC token from gaia to obtain rewards once + // activated + let shielding_data_path = ibc_tests::gen_ibc_shielding_data( + &test, + AA_PAYMENT_ADDRESS, + COSMOS_COIN, + 500_000, + &port_id_namada, + &channel_id_namada, + )?; + ibc_tests::transfer_from_cosmos( + &test_gaia, + COSMOS_USER, + AA_PAYMENT_ADDRESS, + COSMOS_COIN, + 500_000, + &port_id_gaia, + &channel_id_gaia, + Some(shielding_data_path), + None, + )?; + ibc_tests::wait_for_packet_relay( + &port_id_gaia, + &channel_id_gaia, + &test_gaia, + )?; + + shielded_sync(&test, AA_VIEWING_KEY)?; + // The shielding tokens should have been transferred + let balance = find_balance( + &test, + Who::Validator(0), + &gaia_token, + AA_VIEWING_KEY, + Some(0), + Some(&ibc_denom_on_namada), + )?; + assert_eq!(balance, token::Amount::from_u64(500_000)); + + // Send the tokens back to gaia + ibc_tests::transfer( + &test, + ALBERT, + &gaia_receiver, + &ibc_denom_on_namada, + u64::from_str(&trans_balance.to_string()).unwrap(), + Some(ALBERT_KEY), + &port_id_namada, + &channel_id_namada, + None, + None, + None, + )?; + ibc_tests::wait_for_packet_relay( + &port_id_namada, + &channel_id_namada, + &test, + )?; + ibc_tests::check_cosmos_balance( + &test_gaia, + COSMOS_USER, + COSMOS_COIN, + 500_000, + )?; + + // Propose phase 4 - shielding rewards party 🥳 + let activation_epoch = submit_proposal(TestWasms::TxProposalPhase4)?; + + let last_rewards = find_balance( + &test, + Who::Validator(0), + NAM, + AA_VIEWING_KEY, + None, + None, + )?; + + // Wait for phase 4 proposal activation + let mut current_epoch = get_epoch(&test, &validator_one_rpc)?; + while current_epoch < activation_epoch { + // There should be no shielding rewards before phase 4 + shielded_sync(&test, AA_VIEWING_KEY)?; + let current_rewards = find_balance( + &test, + Who::Validator(0), + NAM, + AA_VIEWING_KEY, + None, + None, + )?; + assert_eq!(last_rewards, current_rewards); + + current_epoch = epoch_sleep(&test, &validator_one_rpc, 120)?; + } + + // Make sure we've entered a new MASP epoch before shielding to get rewards + epoch_sleep(&test, &validator_one_rpc, 120)?; + + // There should be some shielding rewards now + shielded_sync(&test, AA_VIEWING_KEY)?; + let current_rewards = find_balance( + &test, + Who::Validator(0), + NAM, + AA_VIEWING_KEY, + None, + None, + )?; + assert!(current_rewards > last_rewards); + + // Propose phase 5 - NAM party 🥳 + let activation_epoch = submit_proposal(TestWasms::TxProposalPhase5)?; + + // Wait for phase 5 proposal activation + let mut current_epoch = get_epoch(&test, &validator_one_rpc)?; + while current_epoch < activation_epoch { + // Should not be able transfer NAM before phase 5 + let tx_args = apply_use_device(vec![ + "transparent-transfer", + "--source", + BERTHA, + "--target", + ALBERT, + "--token", + NAM, + "--amount", + "1", + "--node", + &validator_one_rpc, + ]); + let mut client = run!(test, Bin::Client, tx_args, Some(40))?; + client.exp_string(TX_REJECTED)?; + + current_epoch = epoch_sleep(&test, &validator_one_rpc, 120)?; + } + + // Should be able transfer NAM now + let tx_args = apply_use_device(vec![ + "transparent-transfer", + "--source", + BERTHA, + "--target", + ALBERT, + "--token", + NAM, + "--amount", + "1", + "--node", + &validator_one_rpc, + ]); + let mut client = run!(test, Bin::Client, tx_args, Some(40))?; + client.exp_string(TX_APPLIED_SUCCESS)?; + client.assert_success(); + + Ok(()) +} + +fn query_staking_rewards( + test: &Test, + validator: &str, + rpc: &str, +) -> Result { + // Query the current rewards for the validator self-bond and see that it + // grows + let tx_args = vec!["rewards", "--validator", validator, "--node", &rpc]; + let mut client = run!(test, Bin::Client, tx_args, Some(40))?; + let (_, res) = client + .exp_regex(r"Current rewards available for claim: [0-9\.]+ NAM") + .unwrap(); + + let words = res.split(' ').collect::>(); + let res = words[words.len() - 2]; + Ok(token::Amount::from_str( + res.split(' ').last().unwrap(), + NATIVE_MAX_DECIMAL_PLACES, + ) + .unwrap()) +} diff --git a/crates/tests/src/e2e/setup.rs b/crates/tests/src/e2e/setup.rs index ebd2329aaa..724564a025 100644 --- a/crates/tests/src/e2e/setup.rs +++ b/crates/tests/src/e2e/setup.rs @@ -1379,7 +1379,10 @@ impl CosmosChainType { } } -pub fn setup_cosmos(chain_type: CosmosChainType) -> Result { +pub fn setup_cosmos( + chain_type: CosmosChainType, + user_balance: Option, +) -> Result { let working_dir = working_dir(); let test_dir = TestDir::new(); let chain_id = chain_type.chain_id(); @@ -1425,12 +1428,9 @@ pub fn setup_cosmos(chain_type: CosmosChainType) -> Result { // Add tokens to a user account let account = find_cosmos_address(&test, constants::COSMOS_USER)?; - let args = [ - "genesis", - "add-genesis-account", - &account, - "100000000stake,1000samoleans", - ]; + let user_balance = user_balance.unwrap_or(1000); + let genesis_balance = format!("100000000stake,{user_balance}samoleans"); + let args = ["genesis", "add-genesis-account", &account, &genesis_balance]; let mut cosmos = run_cosmos_cmd(&test, args, Some(10))?; cosmos.assert_success(); diff --git a/crates/tests/src/integration/ledger_tests.rs b/crates/tests/src/integration/ledger_tests.rs index d609aaddd7..da93e07a60 100644 --- a/crates/tests/src/integration/ledger_tests.rs +++ b/crates/tests/src/integration/ledger_tests.rs @@ -734,6 +734,8 @@ fn proposal_submission() -> Result<()> { albert.clone(), TestWasms::TxProposalCode.read_bytes(), 12, + None, + None, ); let submit_proposal_args = apply_use_device(vec![ @@ -1112,8 +1114,14 @@ fn pgf_governance_proposal() -> Result<()> { remove: vec![], }; - let valid_proposal_json_path = - prepare_proposal_data(node.test_dir.path(), albert, pgf_stewards, 12); + let valid_proposal_json_path = prepare_proposal_data( + node.test_dir.path(), + albert, + pgf_stewards, + 12, + None, + None, + ); let submit_proposal_args = apply_use_device(vec![ "init-proposal", "--pgf-stewards", @@ -1324,8 +1332,14 @@ fn pgf_governance_proposal() -> Result<()> { target: christel, })], }; - let valid_proposal_json_path = - prepare_proposal_data(node.test_dir.path(), albert, pgf_funding, 36); + let valid_proposal_json_path = prepare_proposal_data( + node.test_dir.path(), + albert, + pgf_funding, + 36, + None, + None, + ); let submit_proposal_args = apply_use_device(vec![ "init-proposal", @@ -1471,6 +1485,8 @@ fn implicit_account_reveal_pk() -> Result<()> { author, TestWasms::TxProposalCode.read_bytes(), 12, + None, + None, ); vec![ "init-proposal", diff --git a/wasm_for_tests/Cargo.lock b/wasm_for_tests/Cargo.lock index f9acde8215..bc66480392 100644 --- a/wasm_for_tests/Cargo.lock +++ b/wasm_for_tests/Cargo.lock @@ -3784,6 +3784,46 @@ dependencies = [ "rlsf", ] +[[package]] +name = "tx_proposal_phase2" +version = "0.46.0" +dependencies = [ + "getrandom", + "namada_proof_of_stake", + "namada_tx_prelude", + "rlsf", +] + +[[package]] +name = "tx_proposal_phase3" +version = "0.46.0" +dependencies = [ + "getrandom", + "namada_proof_of_stake", + "namada_tx_prelude", + "rlsf", +] + +[[package]] +name = "tx_proposal_phase4" +version = "0.46.0" +dependencies = [ + "getrandom", + "namada_proof_of_stake", + "namada_tx_prelude", + "rlsf", +] + +[[package]] +name = "tx_proposal_phase5" +version = "0.46.0" +dependencies = [ + "getrandom", + "namada_proof_of_stake", + "namada_tx_prelude", + "rlsf", +] + [[package]] name = "tx_proposal_token_gas" version = "0.46.0" diff --git a/wasm_for_tests/Cargo.toml b/wasm_for_tests/Cargo.toml index 61496495bd..4b99565a57 100644 --- a/wasm_for_tests/Cargo.toml +++ b/wasm_for_tests/Cargo.toml @@ -13,6 +13,10 @@ members = [ "tx_proposal_code", "tx_proposal_ibc_token_inflation", "tx_proposal_masp_reward", + "tx_proposal_phase2", + "tx_proposal_phase3", + "tx_proposal_phase4", + "tx_proposal_phase5", "tx_proposal_token_gas", "tx_read_storage_key", "tx_write", diff --git a/wasm_for_tests/Makefile b/wasm_for_tests/Makefile index f54f4ab6eb..491716df9c 100644 --- a/wasm_for_tests/Makefile +++ b/wasm_for_tests/Makefile @@ -16,6 +16,10 @@ wasms += tx_no_op_event wasms += tx_proposal_code wasms += tx_proposal_ibc_token_inflation wasms += tx_proposal_masp_reward +wasms += tx_proposal_phase2 +wasms += tx_proposal_phase3 +wasms += tx_proposal_phase4 +wasms += tx_proposal_phase5 wasms += tx_proposal_token_gas wasms += tx_read_storage_key wasms += tx_write diff --git a/wasm_for_tests/tx_proposal_phase2/Cargo.toml b/wasm_for_tests/tx_proposal_phase2/Cargo.toml new file mode 100644 index 0000000000..3819d19f1f --- /dev/null +++ b/wasm_for_tests/tx_proposal_phase2/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "tx_proposal_phase2" +description = "Wasm transaction used for testing." +authors.workspace = true +edition.workspace = true +license.workspace = true +version.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +namada_tx_prelude.workspace = true +namada_proof_of_stake.workspace = true +rlsf.workspace = true +getrandom.workspace = true + +[lib] +crate-type = ["cdylib"] diff --git a/wasm_for_tests/tx_proposal_phase2/src/lib.rs b/wasm_for_tests/tx_proposal_phase2/src/lib.rs new file mode 100644 index 0000000000..0abe5d73d7 --- /dev/null +++ b/wasm_for_tests/tx_proposal_phase2/src/lib.rs @@ -0,0 +1,31 @@ +use std::str::FromStr; + +use dec::Dec; +use namada_proof_of_stake::storage::{read_pos_params, write_pos_params}; +use namada_tx_prelude::*; + +#[transaction] +fn apply_tx(ctx: &mut Ctx, _tx_data: BatchedTx) -> TxResult { + // PoS inflation + let mut pos_params = + read_pos_params::>(ctx)?.owned; + pos_params.max_inflation_rate = Dec::from_str("0.05").unwrap(); + pos_params.target_staked_ratio = Dec::from_str("0.4").unwrap(); + pos_params.rewards_gain_p = Dec::from_str("0.25").unwrap(); + pos_params.rewards_gain_d = Dec::from_str("0.25").unwrap(); + write_pos_params(ctx, &pos_params)?; + + // PGF inflation + let pgf_inflation_key = + governance::pgf::storage::keys::get_pgf_inflation_rate_key(); + let pgf_inflation_rate = Dec::from_str("0.05").unwrap(); + ctx.write(&pgf_inflation_key, pgf_inflation_rate)?; + + // PGF stewards inflation + let steward_inflation_key = + governance::pgf::storage::keys::get_steward_inflation_rate_key(); + let steward_inflation_rate = Dec::from_str("0.005").unwrap(); + ctx.write(&steward_inflation_key, steward_inflation_rate)?; + + Ok(()) +} diff --git a/wasm_for_tests/tx_proposal_phase3/Cargo.toml b/wasm_for_tests/tx_proposal_phase3/Cargo.toml new file mode 100644 index 0000000000..1f4f6e32a7 --- /dev/null +++ b/wasm_for_tests/tx_proposal_phase3/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "tx_proposal_phase3" +description = "Wasm transaction used for testing." +authors.workspace = true +edition.workspace = true +license.workspace = true +version.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +namada_tx_prelude.workspace = true +namada_proof_of_stake.workspace = true +rlsf.workspace = true +getrandom.workspace = true + +[lib] +crate-type = ["cdylib"] diff --git a/wasm_for_tests/tx_proposal_phase3/src/lib.rs b/wasm_for_tests/tx_proposal_phase3/src/lib.rs new file mode 100644 index 0000000000..8c9eb3026f --- /dev/null +++ b/wasm_for_tests/tx_proposal_phase3/src/lib.rs @@ -0,0 +1,105 @@ +use std::collections::BTreeMap; + +use dec::Dec; +use namada_tx_prelude::*; +use parameters_storage::get_gas_cost_key; + +pub type ChannelId = &'static str; +pub type BaseToken = &'static str; + +pub type MintTokenLimit = token::Amount; +pub type ThroughtputTokenLimit = token::Amount; +pub type CanBeUsedAsGas = bool; +pub type Gas = token::Amount; +pub type MinimumGasPrice = Option; + +const IBC_TOKENS: [( + ChannelId, + BaseToken, + MintTokenLimit, + ThroughtputTokenLimit, + MinimumGasPrice, +); 1] = [( + "channel-0", + "samoleans", + MintTokenLimit::from_u64(10000000000), + ThroughtputTokenLimit::from_u64(10000000000), + Some(Gas::from_u64(1)), +)]; + +#[transaction] +fn apply_tx(ctx: &mut Ctx, _tx_data: BatchedTx) -> TxResult { + // Read the current gas cost map + let gas_cost_key = get_gas_cost_key(); + let mut minimum_gas_price: BTreeMap = + ctx.read(&gas_cost_key)?.unwrap_or_default(); + + // Read the current MASP token map + let token_map_key = token::storage_key::masp_token_map_key(); + let mut token_map = ctx + .read::(&token_map_key)? + .unwrap_or_default(); + + // Enable IBC deposit/withdraws limits + for ( + channel_id, + base_token, + mint_limit, + throughput_limit, + can_be_used_as_gas, + ) in IBC_TOKENS + { + let ibc_denom = format!("transfer/{channel_id}/{base_token}"); + let token_address = ibc::ibc_token(&ibc_denom).clone(); + + let mint_limit_token_key = ibc::mint_limit_key(&token_address); + ctx.write(&mint_limit_token_key, mint_limit)?; + + let throughput_limit_token_key = + ibc::throughput_limit_key(&token_address); + ctx.write(&throughput_limit_token_key, throughput_limit)?; + + // Check if this ibc token should can also be used to pay for gas + if let Some(gas) = can_be_used_as_gas { + minimum_gas_price.insert(token_address.clone(), gas); + } + + // Add the ibc token to the masp token map + token_map.insert(ibc_denom, token_address.clone()); + + // Write some null MASP reward data + let shielded_token_last_inflation_key = + token::storage_key::masp_last_inflation_key(&token_address); + let shielded_token_last_locked_amount_key = + token::storage_key::masp_last_locked_amount_key(&token_address); + let shielded_token_max_rewards_key = + token::storage_key::masp_max_reward_rate_key(&token_address); + let shielded_token_target_locked_amount_key = + token::storage_key::masp_locked_amount_target_key(&token_address); + let shielded_token_kp_gain_key = + token::storage_key::masp_kp_gain_key(&token_address); + let shielded_token_kd_gain_key = + token::storage_key::masp_kd_gain_key(&token_address); + + ctx.write( + &shielded_token_last_locked_amount_key, + token::Amount::zero(), + )?; + ctx.write(&shielded_token_last_inflation_key, token::Amount::zero())?; + ctx.write(&shielded_token_max_rewards_key, Dec::zero())?; + ctx.write( + &shielded_token_target_locked_amount_key, + token::Amount::zero(), + )?; + ctx.write(&shielded_token_kp_gain_key, Dec::zero())?; + ctx.write(&shielded_token_kd_gain_key, Dec::zero())?; + } + + // Write the gas cost map back to storage + ctx.write(&gas_cost_key, minimum_gas_price)?; + + // Write the token map back to storage + ctx.write(&token_map_key, token_map)?; + + Ok(()) +} diff --git a/wasm_for_tests/tx_proposal_phase4/Cargo.toml b/wasm_for_tests/tx_proposal_phase4/Cargo.toml new file mode 100644 index 0000000000..a4c20c5a58 --- /dev/null +++ b/wasm_for_tests/tx_proposal_phase4/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "tx_proposal_phase4" +description = "Wasm transaction used for testing." +authors.workspace = true +edition.workspace = true +license.workspace = true +version.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +namada_tx_prelude.workspace = true +namada_proof_of_stake.workspace = true +rlsf.workspace = true +getrandom.workspace = true + +[lib] +crate-type = ["cdylib"] diff --git a/wasm_for_tests/tx_proposal_phase4/src/lib.rs b/wasm_for_tests/tx_proposal_phase4/src/lib.rs new file mode 100644 index 0000000000..d3418d83bd --- /dev/null +++ b/wasm_for_tests/tx_proposal_phase4/src/lib.rs @@ -0,0 +1,102 @@ +use std::str::FromStr; + +use dec::Dec; +use namada_tx_prelude::*; +use token::storage_key::balance_key; + +pub type Denomination = u8; +pub type ChannelId = &'static str; +pub type BaseToken = &'static str; + +pub type TokenMaxReward = &'static str; +pub type TokenTargetLockedAmount = u64; +pub type KpGain = &'static str; +pub type KdGain = &'static str; + +const IBC_TOKENS: [( + Denomination, + ChannelId, + BaseToken, + TokenMaxReward, + TokenTargetLockedAmount, + KpGain, + KdGain, +); 1] = [( + 0, + "channel-0", + "samoleans", + "1.0", + 1_000_000_000, + "120000", + "120000", +)]; + +#[transaction] +fn apply_tx(ctx: &mut Ctx, _tx_data: BatchedTx) -> TxResult { + // Read the current MASP token map + let token_map_key = token::storage_key::masp_token_map_key(); + let mut token_map = ctx + .read::(&token_map_key)? + .unwrap_or_default(); + + // Enable shielded set rewards for ibc tokens + for ( + denomination, + channel_id, + base_token, + max_reward, + target_locked_amount, + kp, + kd, + ) in IBC_TOKENS + { + let ibc_denom = format!("transfer/{channel_id}/{base_token}"); + let token_address = ibc::ibc_token(&ibc_denom); + + let shielded_token_last_inflation_key = + token::storage_key::masp_last_inflation_key(&token_address); + let shielded_token_last_locked_amount_key = + token::storage_key::masp_last_locked_amount_key(&token_address); + let shielded_token_max_rewards_key = + token::storage_key::masp_max_reward_rate_key(&token_address); + let shielded_token_target_locked_amount_key = + token::storage_key::masp_locked_amount_target_key(&token_address); + let shielded_token_kp_gain_key = + token::storage_key::masp_kp_gain_key(&token_address); + let shielded_token_kd_gain_key = + token::storage_key::masp_kd_gain_key(&token_address); + + // Add the ibc token to the masp token map + token_map.insert(ibc_denom, token_address.clone()); + + // Read the current balance of the IBC token in MASP and set that as + // initial locked amount + let ibc_balance_key = balance_key( + &token_address, + &Address::Internal(address::InternalAddress::Masp), + ); + let current_ibc_amount = + ctx.read::(&ibc_balance_key)?.unwrap(); + ctx.write(&shielded_token_last_locked_amount_key, current_ibc_amount)?; + + // Initialize the remaining MASP inflation keys + ctx.write(&shielded_token_last_inflation_key, token::Amount::zero())?; + + ctx.write( + &shielded_token_max_rewards_key, + Dec::from_str(max_reward).unwrap(), + )?; + ctx.write( + &shielded_token_target_locked_amount_key, + token::Amount::from_uint(target_locked_amount, denomination) + .unwrap(), + )?; + ctx.write(&shielded_token_kp_gain_key, Dec::from_str(kp).unwrap())?; + ctx.write(&shielded_token_kd_gain_key, Dec::from_str(kd).unwrap())?; + } + + // Write the token map back to storage + ctx.write(&token_map_key, token_map)?; + + Ok(()) +} diff --git a/wasm_for_tests/tx_proposal_phase5/Cargo.toml b/wasm_for_tests/tx_proposal_phase5/Cargo.toml new file mode 100644 index 0000000000..ccbf21e3f4 --- /dev/null +++ b/wasm_for_tests/tx_proposal_phase5/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "tx_proposal_phase5" +description = "Wasm transaction used for testing." +authors.workspace = true +edition.workspace = true +license.workspace = true +version.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +namada_tx_prelude.workspace = true +namada_proof_of_stake.workspace = true +rlsf.workspace = true +getrandom.workspace = true + +[lib] +crate-type = ["cdylib"] diff --git a/wasm_for_tests/tx_proposal_phase5/src/lib.rs b/wasm_for_tests/tx_proposal_phase5/src/lib.rs new file mode 100644 index 0000000000..ff0507892e --- /dev/null +++ b/wasm_for_tests/tx_proposal_phase5/src/lib.rs @@ -0,0 +1,23 @@ +use namada_tx_prelude::*; + +pub const MIN_PROPOSAL_GRACE_EPOCHS: u64 = 8; +pub const MIN_PROPOSAL_VOTING_PERIOD: u64 = 28; + +#[transaction] +fn apply_tx(ctx: &mut Ctx, _tx_data: BatchedTx) -> TxResult { + // 1. Enable NAM transfers + let native_token_transferable_key = + parameters_storage::get_native_token_transferable_key(); + ctx.write(&native_token_transferable_key, true)?; + + // 2. Update governance parameters + let min_proposal_grace_epochs_key = + gov_storage::keys::get_min_proposal_grace_epochs_key(); + ctx.write(&min_proposal_grace_epochs_key, MIN_PROPOSAL_GRACE_EPOCHS)?; + + let min_proposal_voting_period_key = + gov_storage::keys::get_min_proposal_voting_period_key(); + ctx.write(&min_proposal_voting_period_key, MIN_PROPOSAL_VOTING_PERIOD)?; + + Ok(()) +}