Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions contracts/contracts/stellar-grants/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ pub const MAX_SPLIT_RECIPIENTS: u32 = 10;
pub const MAX_CRITERIA_PER_MILESTONE: u32 = 20;
pub const MAX_INDEX_ENTRIES: u32 = 10_000;
pub const MAX_PUBLIC_REVIEW_COMMENT_LEN: u32 = 500;
pub const MAX_PUBLIC_REVIEWS_PER_MILESTONE: u32 = 50;
pub const MAX_ROLLING_WINDOW_SIZE: u32 = 50;
pub const MAX_PARAM_HISTORY: u32 = 20;
pub const MAX_RUBRIC_WEIGHTS: u32 = 6;
Expand Down
54 changes: 53 additions & 1 deletion contracts/contracts/stellar-grants/src/open_review.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/// on a milestone. Reviews are visible to formal reviewers but do not affect governance votes.
use soroban_sdk::{Address, Env, String, Vec};

use crate::constants::MAX_PUBLIC_REVIEW_COMMENT_LEN;
use crate::constants::{MAX_PUBLIC_REVIEWS_PER_MILESTONE, MAX_PUBLIC_REVIEW_COMMENT_LEN};
use crate::errors::ContractError;
use crate::events::Events;
use crate::storage::Storage;
Expand Down Expand Up @@ -56,6 +56,9 @@ pub fn submit_review(
if let Some(idx) = found_idx {
reviews.set(idx, review.clone());
} else {
if reviews.len() >= MAX_PUBLIC_REVIEWS_PER_MILESTONE {
return Err(ContractError::TooManyPublicReviews);
}
reviews.push_back(review.clone());
}

Expand Down Expand Up @@ -276,4 +279,53 @@ mod tests {
let result = submit_review(&env, &reviewer, 1, 0, PublicReviewSignal::Positive, s);
assert_eq!(result, Err(ContractError::CommentTooLong));
}

#[test]
fn submit_review_rejects_when_cap_reached() {
let env = Env::default();
env.mock_all_auths();

let grant_id = 1u64;
let milestone_idx = 0u32;
let mut first_reviewer: Option<Address> = None;

for _ in 0..MAX_PUBLIC_REVIEWS_PER_MILESTONE {
let reviewer = Address::generate(&env);
if first_reviewer.is_none() {
first_reviewer = Some(reviewer.clone());
}
submit_review(
&env,
&reviewer,
grant_id,
milestone_idx,
PublicReviewSignal::Positive,
String::from_str(&env, "Good"),
)
.unwrap();
}

// 51st reviewer should be rejected
let extra_reviewer = Address::generate(&env);
let result = submit_review(
&env,
&extra_reviewer,
grant_id,
milestone_idx,
PublicReviewSignal::Positive,
String::from_str(&env, "Extra"),
);
assert_eq!(result, Err(ContractError::TooManyPublicReviews));

// Existing reviewer can still update their review
let update_result = submit_review(
&env,
&first_reviewer.unwrap(),
grant_id,
milestone_idx,
PublicReviewSignal::Neutral,
String::from_str(&env, "Updated comment"),
);
assert!(update_result.is_ok());
}
}
Loading