diff --git a/.github/workflows/scripts/e2e.json b/.github/workflows/scripts/e2e.json index 5a4f302dbf..db8696f942 100644 --- a/.github/workflows/scripts/e2e.json +++ b/.github/workflows/scripts/e2e.json @@ -2,6 +2,7 @@ "e2e::eth_bridge_tests::everything": 4, "e2e::ibc_tests::ibc_transfers": 414, "e2e::ibc_tests::pgf_over_ibc": 415, + "e2e::ibc_tests::fee_payment_with_ibc_token": 840, "e2e::ibc_tests::ibc_token_inflation": 840, "e2e::ibc_tests::ibc_rate_limit": 485, "e2e::ibc_tests::ibc_upgrade_client": 280, diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index 6972135863..0f1f17581d 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs @@ -29,6 +29,7 @@ pub enum TestWasms { TxProposalCode, TxProposalMaspRewards, TxProposalIbcTokenInflation, + TxProposalTokenGas, TxReadStorageKey, TxWriteStorageKey, VpAlwaysFalse, @@ -58,6 +59,7 @@ impl TestWasms { TestWasms::TxProposalIbcTokenInflation => { "tx_proposal_ibc_token_inflation.wasm" } + TestWasms::TxProposalTokenGas => "tx_proposal_token_gas.wasm", TestWasms::TxReadStorageKey => "tx_read_storage_key.wasm", TestWasms::TxWriteStorageKey => "tx_write.wasm", TestWasms::VpAlwaysFalse => "vp_always_false.wasm", diff --git a/crates/tests/src/e2e/ibc_tests.rs b/crates/tests/src/e2e/ibc_tests.rs index bc05a4777c..285a5ca507 100644 --- a/crates/tests/src/e2e/ibc_tests.rs +++ b/crates/tests/src/e2e/ibc_tests.rs @@ -59,31 +59,26 @@ const IBC_REFUND_TARGET_ALIAS: &str = "ibc-refund-target"; const IBC_CLINET_ID: &str = "07-tendermint-0"; const UPGRADED_CHAIN_ID: &str = "upgraded-chain"; -// IBC transfer tests: -// 1. Transparent transfers -// - Namada -> Gaia -> Namada -// - Gaia -> Namada -> Gaia -// 2. Invalid transfers -// 3. Shielding/Unshielding transfers -// - Gaia -> Namada -> (shielded transfer) -> Namada -> Gaia -// 4. Shielding transfer the received token back to a shielded account on -// Namada -// 5. Refunding when transfer failure -// - Ack with an error (invalid receiver) -// - Timeout -// - When unshielding transfer failure, -// - Mint the IBC token for the refund -// - Unescrow the token for the refund -// 6. Malformed shielded actions -// - Missing memo -// - Wrong memo -// 7. Transparent transfer in Namada paying fees with the IBC token +/// IBC transfer tests: +/// 1. Transparent transfers +/// - Namada -> Gaia -> Namada +/// - Gaia -> Namada -> Gaia +/// 2. Invalid transfers +/// 3. Shielding/Unshielding transfers +/// - Gaia -> Namada -> (shielded transfer) -> Namada -> Gaia +/// 4. Shielding transfer the received token back to a shielded account on +/// Namada +/// 5. Refunding when transfer failure +/// - Ack with an error (invalid receiver) +/// - Timeout +/// - When unshielding transfer failure, +/// - Mint the IBC token for the refund +/// - Unescrow the token for the refund +/// 6. Malformed shielded actions +/// - Missing memo +/// - Wrong memo #[test] fn ibc_transfers() -> Result<()> { - const IBC_TOKEN_DENOM: u8 = 6; - let port_id_namada = "transfer".parse().unwrap(); - let port_id_gaia = "transfer".parse().unwrap(); - let update_genesis = |mut genesis: templates::All, base_dir: &_| { genesis.parameters.parameters.epochs_per_year = @@ -94,26 +89,14 @@ fn ibc_transfers() -> Result<()> { .parameters .ibc_params .default_per_epoch_throughput_limit = Amount::max_signed(); - // Whitelist the ibc token for gas payment - let whitelisted_token = - format!("{port_id_namada}/channel-0/{GAIA_COIN}"); - genesis.tokens.token.insert( - whitelisted_token.clone().into(), - templates::TokenConfig { - denom: IBC_TOKEN_DENOM.into(), - masp_params: None, - }, - ); - genesis.parameters.parameters.minimum_gas_price.insert( - whitelisted_token.into(), - DenominatedAmount::new(1.into(), IBC_TOKEN_DENOM.into()), - ); setup::set_validators(1, genesis, base_dir, |_| 0, vec![]) }; let (ledger, gaia, test, test_gaia) = run_namada_gaia(update_genesis)?; let _bg_ledger = ledger.background(); let _bg_gaia = gaia.background(); + let port_id_namada = "transfer".parse().unwrap(); + let port_id_gaia = "transfer".parse().unwrap(); setup_hermes(&test, &test_gaia)?; let (channel_id_namada, channel_id_gaia) = create_channel_with_hermes(&test, &test_gaia)?; @@ -456,22 +439,6 @@ fn ibc_transfers() -> Result<()> { check_balance(&test, AA_VIEWING_KEY, &ibc_denom_on_namada, 40)?; check_gaia_balance(&test_gaia, GAIA_USER, GAIA_COIN, 810)?; - // 7. Transparent transfer in Namada paying gas with samoleans - transfer_on_chain( - &test, - "transparent-transfer", - ALBERT, - BERTHA, - NAM, - 50, - ALBERT_KEY, - &["--gas-token", &ibc_denom_on_namada, "--gas-price", "100"], - )?; - check_balance(&test, ALBERT, NAM, 1_999_950)?; - check_balance(&test, BERTHA, NAM, 2_000_050)?; - check_balance(&test, ALBERT_KEY, &ibc_denom_on_namada, 475)?; - check_balance(&test, "validator-0", &ibc_denom_on_namada, 25)?; - Ok(()) } @@ -562,91 +529,110 @@ fn pgf_over_ibc() -> Result<()> { Ok(()) } -//FIXME: remove -// //FIXME: add this to the e2e.json file -// //FIXME: try to merge this test with the previous one? -// // Test fee payment with an ibc token -// #[test] -// fn fee_payment_with_ibc_token() -> Result<()> { -// const IBC_TOKEN_DENOM: u8 = 6; -// let port_id_gaia = "transfer".parse().unwrap(); -// let port_id_namada = "transfer"; - -// let update_genesis = -// |mut genesis: templates::All, base_dir: &_| { -// genesis.parameters.ibc_params.default_mint_limit = -// Amount::max_signed(); -// genesis -// .parameters -// .ibc_params -// .default_per_epoch_throughput_limit = Amount::max_signed(); -// let whitelisted_token = -// format!("{port_id_namada}/channel-0/{GAIA_COIN}"); -// genesis.tokens.token.insert( -// whitelisted_token.clone().into(), -// templates::TokenConfig { -// denom: IBC_TOKEN_DENOM.into(), -// masp_params: None, -// }, -// ); -// genesis.parameters.parameters.minimum_gas_price.insert( -// whitelisted_token.into(), -// DenominatedAmount::new(1.into(), IBC_TOKEN_DENOM.into()), -// ); - -// setup::set_validators(1, genesis, base_dir, |_| 0, vec![]) -// }; -// let (ledger, gaia, test, test_gaia) = run_namada_gaia(update_genesis)?; -// let _bg_ledger = ledger.background(); -// let _bg_gaia = gaia.background(); - -// setup_hermes(&test, &test_gaia)?; -// let (channel_id_namada, channel_id_gaia) = -// create_channel_with_hermes(&test, &test_gaia)?; -// let ibc_denom_on_namada = -// format!("{port_id_namada}/{channel_id_namada}/{GAIA_COIN}"); - -// // Start relaying -// let hermes = run_hermes(&test)?; -// let _bg_hermes = hermes.background(); - -// // Transfer 500 samoleans from Gaia to Namada -// let namada_receiver = find_address(&test, ALBERT_KEY)?.to_string(); -// transfer_from_gaia( -// &test_gaia, -// GAIA_USER, -// &namada_receiver, -// GAIA_COIN, -// 500, -// &port_id_gaia, -// &channel_id_gaia, -// None, -// None, -// )?; -// wait_for_packet_relay(&port_id_gaia, &channel_id_gaia, &test)?; - -// // Check the token on Namada -// check_balance(&test, ALBERT_KEY, &ibc_denom_on_namada, 500)?; -// check_gaia_balance(&test_gaia, GAIA_USER, GAIA_COIN, 500)?; - -// // Transparent transfer in Namada paying gas with samoleans -// transfer_on_chain( -// &test, -// "transparent-transfer", -// ALBERT, -// BERTHA, -// NAM, -// 50, -// ALBERT_KEY, -// &["--gas-token", &ibc_denom_on_namada, "--gas-price", "100"], -// )?; -// check_balance(&test, ALBERT, NAM, 1_999_950)?; -// check_balance(&test, BERTHA, NAM, 2_000_050)?; -// check_balance(&test, ALBERT_KEY, &ibc_denom_on_namada, 475)?; -// check_balance(&test, "validator-0", &ibc_denom_on_namada, 25)?; - -// Ok(()) -// } +// Test fee payment with an ibc token +// +// 1. Submit governance proposal to allow fee payment with the IBC token +// 2. Transfer some IBC tokens from gaia +// 3. Transparent transfer in Namada with ibc token gas payment +#[test] +fn fee_payment_with_ibc_token() -> Result<()> { + const PIPELINE_LEN: u64 = 2; + let update_genesis = + |mut genesis: templates::All, base_dir: &_| { + genesis.parameters.ibc_params.default_mint_limit = + Amount::max_signed(); + genesis.parameters.gov_params.min_proposal_grace_epochs = 3; + genesis + .parameters + .ibc_params + .default_per_epoch_throughput_limit = Amount::max_signed(); + setup::set_validators(1, genesis, base_dir, |_| 0, vec![]) + }; + let (ledger, gaia, test, test_gaia) = run_namada_gaia(update_genesis)?; + let _bg_ledger = ledger.background(); + let _bg_gaia = gaia.background(); + + // Proposal on Namada + // Delegate some token + delegate_token(&test)?; + let rpc = get_actor_rpc(&test, Who::Validator(0)); + let mut epoch = get_epoch(&test, &rpc).unwrap(); + let delegated = epoch + PIPELINE_LEN; + while epoch < delegated { + sleep(10); + #[allow(clippy::disallowed_methods)] + let new_epoch = get_epoch(&test, &rpc).unwrap_or_default(); + epoch = new_epoch; + } + // ib gas token proposal on Namada + let start_epoch = propose_gas_token(&test)?; + let mut epoch = get_epoch(&test, &rpc).unwrap(); + // Vote + while epoch < start_epoch { + sleep(10); + #[allow(clippy::disallowed_methods)] + let new_epoch = get_epoch(&test, &rpc).unwrap_or_default(); + epoch = new_epoch; + } + submit_votes(&test)?; + + // Create an IBC channel while waiting the grace epoch + setup_hermes(&test, &test_gaia)?; + let (channel_id_namada, channel_id_gaia) = + create_channel_with_hermes(&test, &test_gaia)?; + let port_id_gaia = "transfer".parse().unwrap(); + let port_id_namada = "transfer"; + let ibc_denom_on_namada = + format!("{port_id_namada}/{channel_id_namada}/{GAIA_COIN}"); + + // Start relaying + let hermes = run_hermes(&test)?; + let _bg_hermes = hermes.background(); + + // wait for the grace + let grace_epoch = start_epoch + 6u64; + while epoch < grace_epoch { + sleep(5); + epoch = get_epoch(&test, &rpc).unwrap(); + } + + // Transfer 200 samoleans from Gaia to Namada + let namada_receiver = find_address(&test, ALBERT_KEY)?.to_string(); + transfer_from_gaia( + &test_gaia, + GAIA_USER, + &namada_receiver, + GAIA_COIN, + 200, + &port_id_gaia, + &channel_id_gaia, + None, + None, + )?; + wait_for_packet_relay(&port_id_gaia, &channel_id_gaia, &test)?; + + // Check the token on Namada + check_balance(&test, ALBERT_KEY, &ibc_denom_on_namada, 200)?; + check_gaia_balance(&test_gaia, GAIA_USER, GAIA_COIN, 800)?; + + // Transparent transfer in Namada paying gas with samoleans + transfer_on_chain( + &test, + "transparent-transfer", + ALBERT, + BERTHA, + NAM, + 50, + ALBERT_KEY, + &["--gas-token", &ibc_denom_on_namada, "--gas-price", "1"], + )?; + check_balance(&test, ALBERT, NAM, 1_999_950)?; + check_balance(&test, BERTHA, NAM, 2_000_050)?; + check_balance(&test, ALBERT_KEY, &ibc_denom_on_namada, 475)?; + check_balance(&test, "validator-0", &ibc_denom_on_namada, 25)?; + + Ok(()) +} /// IBC token inflation test /// - Propose the inflation of an IBC token received from Gaia @@ -1492,6 +1478,50 @@ fn propose_upgrade_client( Ok(()) } +fn propose_gas_token(test: &Test) -> Result { + let albert = find_address(test, ALBERT)?; + let rpc = get_actor_rpc(test, Who::Validator(0)); + let epoch = get_epoch(test, &rpc)?; + let start_epoch = (epoch.0 + 3) / 3 * 3; + let proposal_json = serde_json::json!({ + "proposal": { + "content": { + "title": "IBC token gas", + "authors": "test@test.com", + "discussions-to": "www.github.com/anoma/aip/1", + "created": "2022-03-10T08:54:37Z", + "license": "MIT", + "abstract": "IBC token gas", + "motivation": "IBC token gas", + "details": "IBC token gas", + "requires": "2" + }, + "author": albert, + "voting_start_epoch": start_epoch, + "voting_end_epoch": start_epoch + 3_u64, + "activation_epoch": start_epoch + 6_u64, + }, + "data": TestWasms::TxProposalTokenGas.read_bytes() + }); + + let proposal_json_path = test.test_dir.path().join("proposal.json"); + write_json_file(proposal_json_path.as_path(), proposal_json); + + let submit_proposal_args = apply_use_device(vec![ + "init-proposal", + "--data-path", + proposal_json_path.to_str().unwrap(), + "--gas-limit", + "10000000", + "--node", + &rpc, + ]); + let mut client = run!(test, Bin::Client, submit_proposal_args, Some(100))?; + client.exp_string(TX_APPLIED_SUCCESS)?; + client.assert_success(); + Ok(start_epoch.into()) +} + fn wait_for_pass(test: &Test) -> Result<()> { let args = ["query", "gov", "proposal", "1"]; for _ in 0..10 { diff --git a/wasm_for_tests/Cargo.lock b/wasm_for_tests/Cargo.lock index 90e2f85c51..795c146a9e 100644 --- a/wasm_for_tests/Cargo.lock +++ b/wasm_for_tests/Cargo.lock @@ -3784,6 +3784,15 @@ dependencies = [ "rlsf", ] +[[package]] +name = "tx_proposal_token_gas" +version = "0.41.0" +dependencies = [ + "getrandom", + "namada_tx_prelude", + "rlsf", +] + [[package]] name = "tx_read_storage_key" version = "0.41.0" diff --git a/wasm_for_tests/Cargo.toml b/wasm_for_tests/Cargo.toml index e37543c2ef..13a9d70662 100644 --- a/wasm_for_tests/Cargo.toml +++ b/wasm_for_tests/Cargo.toml @@ -13,6 +13,7 @@ members = [ "tx_proposal_code", "tx_proposal_ibc_token_inflation", "tx_proposal_masp_reward", + "tx_proposal_token_gas", "tx_read_storage_key", "tx_write", "vp_always_false", diff --git a/wasm_for_tests/Makefile b/wasm_for_tests/Makefile index 4fd8f83485..5e92278698 100644 --- a/wasm_for_tests/Makefile +++ b/wasm_for_tests/Makefile @@ -16,6 +16,7 @@ wasms += tx_no_op_event wasms += tx_proposal_code wasms += tx_proposal_ibc_token_inflation wasms += tx_proposal_masp_reward +wasms += tx_proposal_token_gas wasms += tx_read_storage_key wasms += tx_write wasms += vp_always_false diff --git a/wasm_for_tests/tx_proposal_token_gas/Cargo.toml b/wasm_for_tests/tx_proposal_token_gas/Cargo.toml new file mode 100644 index 0000000000..3d027b6319 --- /dev/null +++ b/wasm_for_tests/tx_proposal_token_gas/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "tx_proposal_token_gas" +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 +rlsf.workspace = true +getrandom.workspace = true + +[lib] +crate-type = ["cdylib"] diff --git a/wasm_for_tests/tx_proposal_token_gas/src/lib.rs b/wasm_for_tests/tx_proposal_token_gas/src/lib.rs new file mode 100644 index 0000000000..bbf087da34 --- /dev/null +++ b/wasm_for_tests/tx_proposal_token_gas/src/lib.rs @@ -0,0 +1,18 @@ +use std::collections::BTreeMap; + +use namada_tx_prelude::{ + parameters_storage::get_gas_cost_key, token::Amount, *, +}; + +#[transaction] +fn apply_tx(ctx: &mut Ctx, _tx_data: BatchedTx) -> TxResult { + let ibc_token = ibc::ibc_token("transfer/channel-0/samoleans"); + + let gas_cost_key = get_gas_cost_key(); + let mut minimum_gas_price: BTreeMap = + ctx.read(&gas_cost_key)?.unwrap_or_default(); + minimum_gas_price.insert(ibc_token, 1.into()); + ctx.write(&gas_cost_key, minimum_gas_price)?; + + Ok(()) +}