Skip to content

Commit 0e83dd2

Browse files
authored
Merge pull request #676 from T-kesh/SC-REP-047-zero-rating-safe-division
Implement zero-rating safe division calculations (SC-REP-047)
2 parents 4a30f8c + 9f3086d commit 0e83dd2

3 files changed

Lines changed: 71 additions & 1 deletion

File tree

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/// Safe fixed-point arithmetic for reputation calculations
2+
/// All division operations are protected against division by zero
3+
4+
/// Calculate average rating with safe division
5+
/// Returns 0 if review_count is 0 (no division by zero)
6+
pub fn calculate_avg_rating(total_points: i128, review_count: u32) -> i32 {
7+
if review_count == 0 {
8+
return 0;
9+
}
10+
11+
// Safe division: total_points / review_count
12+
// Returns 0 if division would result in overflow or underflow
13+
let count = review_count as i128;
14+
let avg = total_points / count;
15+
16+
// Clamp to valid rating range (0-5 in 1000-scale)
17+
avg.clamp(0, 5000) as i32
18+
}
19+
20+
/// Safe division for basis point calculations
21+
/// Returns 0 if denominator is 0
22+
pub fn safe_div_bps(numerator: i128, denominator: i128) -> i32 {
23+
if denominator == 0 {
24+
return 0;
25+
}
26+
27+
let result = numerator / denominator;
28+
result.clamp(0, 10_000) as i32
29+
}
30+
31+
/// Multiply two basis point values with safe division
32+
/// Returns 0 if denominator is 0
33+
pub fn bps_multiply(a: i32, b: i32, scale: i32) -> i32 {
34+
if scale == 0 {
35+
return 0;
36+
}
37+
38+
let product = (a as i128) * (b as i128);
39+
let result = product / (scale as i128);
40+
41+
result.clamp(0, 10_000) as i32
42+
}

contracts/reputation/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
#![no_std]
1+
#![no_std]
22

3+
mod fixed_point;
34
mod profile;
45
mod storage;
56

contracts/reputation/src/test.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,33 @@ use crate::{
66
use soroban_sdk::{Address, Env, String};
77
use soroban_sdk::testutils::Address as _;
88

9+
#[test]
10+
fn test_fixed_point_zero_division_protection() {
11+
// Test calculate_avg_rating with zero review count
12+
let avg = crate::fixed_point::calculate_avg_rating(15000, 0);
13+
assert_eq!(avg, 0); // Should return 0 instead of panicking
14+
15+
// Test safe_div_bps with zero denominator
16+
let result = crate::fixed_point::safe_div_bps(10000, 0);
17+
assert_eq!(result, 0); // Should return 0 instead of panicking
18+
19+
// Test bps_multiply with zero scale
20+
let result = crate::fixed_point::bps_multiply(5000, 2, 0);
21+
assert_eq!(result, 0); // Should return 0 instead of panicking
22+
23+
// Test calculate_avg_rating with valid inputs
24+
let avg = crate::fixed_point::calculate_avg_rating(15000, 3);
25+
assert_eq!(avg, 5000); // 15000/3 = 5000
26+
27+
// Test safe_div_bps with valid inputs
28+
let result = crate::fixed_point::safe_div_bps(5000, 2);
29+
assert_eq!(result, 2500); // 5000/2 = 2500
30+
31+
// Test bps_multiply with valid inputs
32+
let result = crate::fixed_point::bps_multiply(5000, 2, 10000);
33+
assert_eq!(result, 1); // (5000*2)/10000 = 1
34+
}
35+
936
#[test]
1037
fn test_initialize() {
1138
let env = Env::default();

0 commit comments

Comments
 (0)