diff --git a/crates/tests/src/e2e/ledger_tests.rs b/crates/tests/src/e2e/ledger_tests.rs index dceed2b2ac..af8bb62972 100644 --- a/crates/tests/src/e2e/ledger_tests.rs +++ b/crates/tests/src/e2e/ledger_tests.rs @@ -29,9 +29,11 @@ use namada_core::chain::ChainId; use namada_core::token::NATIVE_MAX_DECIMAL_PLACES; use namada_sdk::address::Address; use namada_sdk::chain::Epoch; +use namada_sdk::dec::Dec; use namada_sdk::time::DateTimeUtc; use namada_sdk::token; use namada_test_utils::TestWasms; +use regex::Regex; use serde::Serialize; use serde_json::json; use setup::constants::*; @@ -503,10 +505,13 @@ fn stop_ledger_at_height() -> Result<()> { fn pos_bonds() -> Result<()> { let pipeline_len = 2; let unbonding_len = 4; + let min_commission_rate = Dec::from_str("0.1").unwrap(); let test = setup::network( |mut genesis, base_dir: &_| { genesis.parameters.pos_params.pipeline_len = pipeline_len; genesis.parameters.pos_params.unbonding_len = unbonding_len; + genesis.parameters.pos_params.min_commission_rate = + min_commission_rate; genesis.parameters.parameters.min_num_of_blocks = 6; genesis.parameters.parameters.epochs_per_year = 31_536_000; let mut genesis = setup::set_validators( @@ -576,7 +581,51 @@ fn pos_bonds() -> Result<()> { let rpc = get_actor_rpc(&test, Who::Validator(0)); wait_for_block_height(&test, &rpc, 2, 30)?; + // 1.1 Query the validator commission rate (should be the min parameter) let validator_0_rpc = get_actor_rpc(&test, Who::Validator(0)); + let tx_args = vec![ + "commission-rate", + "--validator", + "validator-0-validator", + "--node", + &validator_0_rpc, + ]; + let mut client = + run_as!(test, Who::Validator(0), Bin::Client, tx_args, Some(40))?; + + let str1 = client.exp_regex(r".* commission rate: [0-1]\.[0-9]*,.*")?.1; + client.assert_success(); + + let rate1 = Regex::new(r"commission rate: ([0-1]\.[0-9]+),")? + .captures(&str1) + .unwrap() + .get(1) + .unwrap() + .as_str(); + let rate1 = Dec::from_str(rate1).unwrap(); + assert_eq!(rate1, min_commission_rate); + + let tx_args = vec![ + "commission-rate", + "--validator", + "validator-1-validator", + "--node", + &validator_0_rpc, + ]; + let mut client = + run_as!(test, Who::Validator(1), Bin::Client, tx_args, Some(40))?; + + let str2 = client.exp_regex(r".* commission rate: [0-1]\.[0-9]*,.*")?.1; + client.assert_success(); + + let rate2 = Regex::new(r"commission rate: ([0-1]\.[0-9]+),")? + .captures(&str2) + .unwrap() + .get(1) + .unwrap() + .as_str(); + let rate2 = Dec::from_str(rate2).unwrap(); + assert_eq!(rate1, rate2); // 2. Submit a self-bond for the first genesis validator let tx_args = vec![ diff --git a/crates/tests/src/integration/ledger_tests.rs b/crates/tests/src/integration/ledger_tests.rs index 7d6322d448..cf1146df9e 100644 --- a/crates/tests/src/integration/ledger_tests.rs +++ b/crates/tests/src/integration/ledger_tests.rs @@ -1666,6 +1666,131 @@ fn change_validator_metadata() -> Result<()> { assert_matches!(captured.result, Ok(_)); assert!(captured.contains(TX_APPLIED_SUCCESS)); + // 6. Query the metadata to see that the validator website is removed + let captured = + CapturedOutput::of(|| run(&node, Bin::Client, metadata_query_args)); + assert_matches!(captured.result, Ok(_)); + assert!(captured.contains("Email: theokayestvalidator@namada.net")); + assert!(captured.contains( + "Description: We are just an okay validator node trying to get by" + )); + assert!(captured.contains("No website")); + assert!(captured.contains("No discord handle")); + assert!(captured.contains("commission rate:")); + assert!(captured.contains("max change per epoch:")); + + // 6. Try to change the validator commission below the minimum + let commission_change_args = apply_use_device(vec![ + "change-commission-rate", + "--validator", + "validator-0-validator", + "--commission-rate", + "0.01", + "--node", + &validator_one_rpc, + ]); + let captured = + CapturedOutput::of(|| run(&node, Bin::Client, commission_change_args)); + assert_matches!(captured.result, Err(_)); + + // 7. Query to ensure it hasn't changed + let commission_change_args = apply_use_device(vec![ + "commission-rate", + "--validator", + "validator-0-validator", + "--node", + &validator_one_rpc, + ]); + let captured = + CapturedOutput::of(|| run(&node, Bin::Client, commission_change_args)); + assert_matches!(captured.result, Ok(_)); + assert!( + captured.contains("commission rate: 0.05, max change per epoch: 0.01") + ); + + Ok(()) +} + +/// Change validator metadata +#[test] +fn check_validator_commission() -> Result<()> { + // This address doesn't matter for tests. But an argument is required. + let validator_one_rpc = "http://127.0.0.1:26567"; + // 1. start the ledger node + let (node, _services) = setup::setup()?; + + // 2. Query the validator metadata loaded from genesis + let metadata_query_args = vec![ + "validator-metadata", + "--validator", + "validator-0-validator", + "--node", + &validator_one_rpc, + ]; + let captured = CapturedOutput::of(|| { + run(&node, Bin::Client, metadata_query_args.clone()) + }); + assert_matches!(captured.result, Ok(_)); + assert!(captured.contains("No validator name")); + assert!(captured.contains("Email:")); + assert!(captured.contains("No description")); + assert!(captured.contains("No website")); + assert!(captured.contains("No discord handle")); + assert!(captured.contains("commission rate:")); + assert!(captured.contains("max change per epoch:")); + + // 3. Add some metadata to the validator + let metadata_change_args = apply_use_device(vec![ + "change-metadata", + "--validator", + "validator-0-validator", + "--name", + "theokayestvalidator", + "--email", + "theokayestvalidator@namada.net", + "--description", + "We are just an okay validator node trying to get by", + "--website", + "theokayestvalidator.com", + "--node", + &validator_one_rpc, + ]); + + let captured = + CapturedOutput::of(|| run(&node, Bin::Client, metadata_change_args)); + assert_matches!(captured.result, Ok(_)); + assert!(captured.contains(TX_APPLIED_SUCCESS)); + + // 4. Query the metadata after the change + let captured = CapturedOutput::of(|| { + run(&node, Bin::Client, metadata_query_args.clone()) + }); + assert_matches!(captured.result, Ok(_)); + assert!(captured.contains("Validator name: theokayestvalidator")); + assert!(captured.contains("Email: theokayestvalidator@namada.net")); + assert!(captured.contains( + "Description: We are just an okay validator node trying to get by" + )); + assert!(captured.contains("Website: theokayestvalidator.com")); + assert!(captured.contains("No discord handle")); + assert!(captured.contains("commission rate:")); + assert!(captured.contains("max change per epoch:")); + + // 5. Remove the validator website + let metadata_change_args = apply_use_device(vec![ + "change-metadata", + "--validator", + "validator-0-validator", + "--website", + "", + "--node", + &validator_one_rpc, + ]); + let captured = + CapturedOutput::of(|| run(&node, Bin::Client, metadata_change_args)); + assert_matches!(captured.result, Ok(_)); + assert!(captured.contains(TX_APPLIED_SUCCESS)); + // 6. Query the metadata to see that the validator website is removed let captured = CapturedOutput::of(|| run(&node, Bin::Client, metadata_query_args)); diff --git a/wasm/tx_change_validator_commission/src/lib.rs b/wasm/tx_change_validator_commission/src/lib.rs index bc30b89ebd..5dd14e0c24 100644 --- a/wasm/tx_change_validator_commission/src/lib.rs +++ b/wasm/tx_change_validator_commission/src/lib.rs @@ -114,7 +114,11 @@ mod tests { assert_eq!(commission_rates_pre[0], Some(initial_rate)); - apply_tx(ctx(), signed_tx.batch_first_tx())?; + let res = apply_tx(ctx(), signed_tx.batch_first_tx()); + if commission_change.new_rate < pos_params.min_commission_rate { + assert!(res.is_err()); + return Ok(()); + } // Read the data after the tx is executed