From 2e557429c4f1806ce1fb6768e6619d47c831d1a5 Mon Sep 17 00:00:00 2001 From: Dusan Maksimovic Date: Tue, 16 Jul 2024 11:59:50 +0200 Subject: [PATCH 1/2] Extend Proposal with Title and Description fields --- contracts/atom_wars/src/contract.rs | 30 +++++++++- contracts/atom_wars/src/msg.rs | 3 +- contracts/atom_wars/src/state.rs | 2 + contracts/atom_wars/src/testing.rs | 88 ++++++++++++++++++++++++++++- contracts/tribute/src/testing.rs | 12 ++++ 5 files changed, 130 insertions(+), 5 deletions(-) diff --git a/contracts/atom_wars/src/contract.rs b/contracts/atom_wars/src/contract.rs index 89ef50e..5c0d504 100644 --- a/contracts/atom_wars/src/contract.rs +++ b/contracts/atom_wars/src/contract.rs @@ -27,6 +27,10 @@ const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); pub const MAX_LOCK_ENTRIES: usize = 100; +pub const MIN_PROP_TITLE_LENGTH: usize = 3; +pub const MAX_PROP_TITLE_LENGTH: usize = 256; +pub const MAX_PROP_DESC_LENGTH: usize = 10000; + #[entry_point] pub fn instantiate( deps: DepsMut, @@ -111,8 +115,10 @@ pub fn execute( ExecuteMsg::UnlockTokens {} => unlock_tokens(deps, env, info), ExecuteMsg::CreateProposal { tranche_id, + title, + description, covenant_params, - } => create_proposal(deps, env, tranche_id, covenant_params), + } => create_proposal(deps, env, tranche_id, title, description, covenant_params), ExecuteMsg::Vote { tranche_id, proposal_id, @@ -363,6 +369,8 @@ fn create_proposal( deps: DepsMut, env: Env, tranche_id: u64, + title: String, + description: String, covenant_params: CovenantParams, ) -> Result { TRANCHE_MAP.load(deps.storage, tranche_id)?; @@ -371,11 +379,15 @@ fn create_proposal( let round_id = compute_current_round_id(&env, &constants)?; let proposal_id = PROP_ID.load(deps.storage)?; - // Create proposal in PropMap + let title = sanitize_input_string(title, MIN_PROP_TITLE_LENGTH, MAX_PROP_TITLE_LENGTH)?; + let description = sanitize_input_string(description, 0, MAX_PROP_DESC_LENGTH)?; + let proposal = Proposal { round_id, tranche_id, proposal_id, + title, + description, covenant_params, power: Uint128::zero(), percentage: Uint128::zero(), @@ -959,3 +971,17 @@ fn get_lock_count(deps: Deps, user_address: Addr) -> usize { .range(deps.storage, None, None, Order::Ascending) .count() } + +fn sanitize_input_string(input: String, min_length: usize, max_length: usize) -> StdResult { + let input = input.trim().to_string(); + let input_length = input.len(); + + if input_length < min_length || input_length > max_length { + return Err(StdError::generic_err(format!( + "Invalid string length, got {}, expected length to be between {} and {}", + input, min_length, max_length + ))); + } + + Ok(input) +} diff --git a/contracts/atom_wars/src/msg.rs b/contracts/atom_wars/src/msg.rs index 26587d4..4e4b161 100644 --- a/contracts/atom_wars/src/msg.rs +++ b/contracts/atom_wars/src/msg.rs @@ -28,6 +28,8 @@ pub enum ExecuteMsg { UnlockTokens {}, CreateProposal { tranche_id: u64, + title: String, + description: String, covenant_params: CovenantParams, }, Vote { @@ -40,5 +42,4 @@ pub enum ExecuteMsg { RemoveFromWhitelist { covenant_params: CovenantParams, }, - // ExecuteProposal { proposal_id: u64 }, } diff --git a/contracts/atom_wars/src/state.rs b/contracts/atom_wars/src/state.rs index 09b4615..a234c8b 100644 --- a/contracts/atom_wars/src/state.rs +++ b/contracts/atom_wars/src/state.rs @@ -32,6 +32,8 @@ pub struct Proposal { pub round_id: u64, pub tranche_id: u64, pub proposal_id: u64, + pub title: String, + pub description: String, pub covenant_params: CovenantParams, pub power: Uint128, pub percentage: Uint128, diff --git a/contracts/atom_wars/src/testing.rs b/contracts/atom_wars/src/testing.rs index 9f39e16..23b9b2f 100644 --- a/contracts/atom_wars/src/testing.rs +++ b/contracts/atom_wars/src/testing.rs @@ -1,4 +1,7 @@ -use crate::contract::{query_whitelist, query_whitelist_admins, MAX_LOCK_ENTRIES}; +use crate::contract::{ + query_whitelist, query_whitelist_admins, MAX_LOCK_ENTRIES, MAX_PROP_DESC_LENGTH, + MAX_PROP_TITLE_LENGTH, MIN_PROP_TITLE_LENGTH, +}; use crate::state::Tranche; use crate::{ contract::{ @@ -219,18 +222,22 @@ fn create_proposal_basic_test() { let covenant_params_1 = get_default_covenant_params(); let msg1 = ExecuteMsg::CreateProposal { tranche_id: 1, + title: "proposal title 1".to_string(), + description: "proposal description 1".to_string(), covenant_params: covenant_params_1.clone(), }; let res = execute(deps.as_mut(), env.clone(), info.clone(), msg1.clone()); assert!(res.is_ok()); - let mut covenant_params_2 = get_default_covenant_params().clone(); + let mut covenant_params_2 = get_default_covenant_params(); covenant_params_2.pool_id = "pool_id_2".to_string(); covenant_params_2.outgoing_channel_id = "outgoing_channel_id_2".to_string(); covenant_params_2.funding_destination_name = "funding_destination_name_2".to_string(); let msg2 = ExecuteMsg::CreateProposal { tranche_id: 1, + title: "proposal title 2".to_string(), + description: "proposal description 2".to_string(), covenant_params: covenant_params_2.clone(), }; let res = execute(deps.as_mut(), env.clone(), info.clone(), msg2.clone()); @@ -252,6 +259,71 @@ fn create_proposal_basic_test() { assert_eq!(covenant_params_2, proposal.covenant_params); } +#[test] +fn proposal_title_and_desc_validation_test() { + let user_address = "addr0000"; + let user_token = Coin::new(1000, STATOM.to_string()); + + let (mut deps, env, info) = ( + mock_dependencies(), + mock_env(), + mock_info(user_address, &[user_token.clone()]), + ); + let msg = get_default_instantiate_msg(); + + let res = instantiate(deps.as_mut(), env.clone(), info.clone(), msg.clone()); + assert!(res.is_ok()); + + let too_long_title = String::from("too long title ".repeat(20).trim()); + let too_long_desc = "a".repeat(10001); + + let test_cases = vec![ + ( + "proposal title too short", + "".to_string(), + "proposal description".to_string(), + format!( + "Invalid string length, got {}, expected length to be between {} and {}", + "", MIN_PROP_TITLE_LENGTH, MAX_PROP_TITLE_LENGTH + ), + ), + ( + "proposal title too long", + too_long_title.clone(), + "proposal description".to_string(), + format!( + "Invalid string length, got {}, expected length to be between {} and {}", + too_long_title, MIN_PROP_TITLE_LENGTH, MAX_PROP_TITLE_LENGTH + ), + ), + ( + "proposal description too long", + "proposal title".to_string(), + too_long_desc.clone(), + format!( + "Invalid string length, got {}, expected length to be between {} and {}", + too_long_desc, 0, MAX_PROP_DESC_LENGTH + ), + ), + ]; + + for test_case in test_cases { + println!("running test case: {}", test_case.0); + + let msg = ExecuteMsg::CreateProposal { + tranche_id: 1, + title: test_case.1.to_string(), + description: test_case.2.to_string(), + covenant_params: get_default_covenant_params(), + }; + + let res = execute(deps.as_mut(), env.clone(), info.clone(), msg.clone()); + assert!(res.is_err()); + + assert!(res.unwrap_err().to_string().contains(test_case.3.as_str())); + } +} + #[test] fn vote_basic_test() { let user_address = "addr0000"; @@ -277,6 +349,8 @@ fn vote_basic_test() { // create a new proposal let msg = ExecuteMsg::CreateProposal { tranche_id: 1, + title: "proposal title 1".to_string(), + description: "proposal description 1".to_string(), covenant_params: get_default_covenant_params(), }; let res = execute(deps.as_mut(), env.clone(), info.clone(), msg.clone()); @@ -337,6 +411,8 @@ fn multi_tranches_test() { // create two proposals for tranche 1 let msg1 = ExecuteMsg::CreateProposal { tranche_id: 1, + title: "proposal title 1".to_string(), + description: "proposal description 1".to_string(), covenant_params: get_default_covenant_params(), }; let res = execute(deps.as_mut(), env.clone(), info.clone(), msg1.clone()); @@ -344,6 +420,8 @@ fn multi_tranches_test() { let msg2 = ExecuteMsg::CreateProposal { tranche_id: 1, + title: "proposal title 2".to_string(), + description: "proposal description 2".to_string(), covenant_params: get_default_covenant_params(), }; let res = execute(deps.as_mut(), env.clone(), info.clone(), msg2.clone()); @@ -352,6 +430,8 @@ fn multi_tranches_test() { // create two proposals for tranche 2 let msg3 = ExecuteMsg::CreateProposal { tranche_id: 2, + title: "proposal title 3".to_string(), + description: "proposal description 3".to_string(), covenant_params: get_default_covenant_params(), }; let res = execute(deps.as_mut(), env.clone(), info.clone(), msg3.clone()); @@ -359,6 +439,8 @@ fn multi_tranches_test() { let msg4 = ExecuteMsg::CreateProposal { tranche_id: 2, + title: "proposal title 4".to_string(), + description: "proposal description 4".to_string(), covenant_params: get_default_covenant_params(), }; let res = execute(deps.as_mut(), env.clone(), info.clone(), msg4.clone()); @@ -468,6 +550,8 @@ fn test_query_round_tranche_proposals_pagination() { for i in 0..num_proposals { let create_proposal_msg = ExecuteMsg::CreateProposal { tranche_id: 1, + title: format!("proposal title {}", i + 1), + description: format!("proposal description {}", i + 1), covenant_params: crate::state::CovenantParams { pool_id: format!("Pool ID {}", i), outgoing_channel_id: format!("Outgoing Channel ID {}", i), diff --git a/contracts/tribute/src/testing.rs b/contracts/tribute/src/testing.rs index 1d4a019..c3307a9 100644 --- a/contracts/tribute/src/testing.rs +++ b/contracts/tribute/src/testing.rs @@ -173,6 +173,8 @@ fn add_tribute_test() { round_id: 10, tranche_id: 0, proposal_id: 5, + title: "proposal title 1".to_string(), + description: "proposal description 1".to_string(), covenant_params: CovenantParams { pool_id: "pool 1".to_string(), outgoing_channel_id: "channel-1".to_string(), @@ -294,6 +296,8 @@ fn claim_tribute_test() { round_id: 10, tranche_id: 0, proposal_id: 5, + title: "proposal title 1".to_string(), + description: "proposal description 1".to_string(), covenant_params: CovenantParams { pool_id: "pool 1".to_string(), outgoing_channel_id: "channel-1".to_string(), @@ -308,6 +312,8 @@ fn claim_tribute_test() { round_id: 10, tranche_id: 0, proposal_id: 5, + title: "proposal title 1".to_string(), + description: "proposal description 1".to_string(), covenant_params: CovenantParams { pool_id: "pool 1".to_string(), outgoing_channel_id: "channel-1".to_string(), @@ -320,6 +326,8 @@ fn claim_tribute_test() { round_id: 10, tranche_id: 0, proposal_id: 6, + title: "proposal title 2".to_string(), + description: "proposal description 2".to_string(), covenant_params: CovenantParams { pool_id: "pool 2".to_string(), outgoing_channel_id: "channel-2".to_string(), @@ -505,6 +513,8 @@ fn refund_tribute_test() { round_id: 10, tranche_id: 0, proposal_id: 5, + title: "proposal title 1".to_string(), + description: "proposal description 1".to_string(), covenant_params: CovenantParams { pool_id: "pool 1".to_string(), outgoing_channel_id: "channel-1".to_string(), @@ -518,6 +528,8 @@ fn refund_tribute_test() { round_id: 10, tranche_id: 0, proposal_id: 6, + title: "proposal title 2".to_string(), + description: "proposal description 2".to_string(), covenant_params: CovenantParams { pool_id: "pool 2".to_string(), outgoing_channel_id: "channel-2".to_string(), From e4186520b049b55e431219c796b02cc904b8ce98 Mon Sep 17 00:00:00 2001 From: Dusan Maksimovic Date: Tue, 16 Jul 2024 12:37:01 +0200 Subject: [PATCH 2/2] CR fix --- contracts/atom_wars/src/contract.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/atom_wars/src/contract.rs b/contracts/atom_wars/src/contract.rs index 5c0d504..82c1519 100644 --- a/contracts/atom_wars/src/contract.rs +++ b/contracts/atom_wars/src/contract.rs @@ -972,6 +972,7 @@ fn get_lock_count(deps: Deps, user_address: Addr) -> usize { .count() } +// Trims the input string and validates its minimum and maximum length fn sanitize_input_string(input: String, min_length: usize, max_length: usize) -> StdResult { let input = input.trim().to_string(); let input_length = input.len();