fn le_bytes_to_biguint(bytes: &[u8]) -> num_bigint::BigUint {
let mut v = bytes.to_vec();
while v.last() == Some(&0) {
v.pop();
}
v.reverse();
num_bigint::BigUint::from_bytes_be(&v)
}
fn run_addmod(a: &str, b: &str, m: &str) -> num_bigint::BigUint {
let src = format!(
r#"// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract C {{
function f() external pure returns (uint256) {{
return addmod({a}, {b}, {m});
}}
}}"#
);
let artifacts = compile_contracts(&src, false, 2).expect("compile failed");
let result = execute_bytecode(&artifacts[0].bytecode);
assert!(result.success, "addmod execution failed: {:?}", result.exception);
le_bytes_to_biguint(&result.return_data)
}
#[test]
fn addmod_large_modulus_above_2_255_uses_unsigned_reduction() {
let m: num_bigint::BigUint = (num_bigint::BigUint::from(1u32) << 255) + 7u32;
let a = m.clone() + 100u32;
let got = run_addmod(&a.to_str_radix(10), "0", &m.to_str_radix(10));
assert_eq!(
got,
num_bigint::BigUint::from(100u32),
"addmod(m+100, 0, m) with m >= 2^255 must be 100 (unsigned reduction)"
);
}
#[test]
fn addmod_small_inputs_still_correct() {
let got = run_addmod("3", "5", "7");
assert_eq!(
got,
num_bigint::BigUint::from(1u32),
"(3+5) % 7 = 1"
);
}
fn run_mulmod(a: &str, b: &str, m: &str) -> num_bigint::BigUint {
let src = format!(
r#"// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract C {{
function f() external pure returns (uint256) {{
return mulmod({a}, {b}, {m});
}}
}}"#
);
let artifacts = compile_contracts(&src, false, 2).expect("compile failed");
let result = execute_bytecode(&artifacts[0].bytecode);
assert!(result.success, "mulmod execution failed: {:?}", result.exception);
le_bytes_to_biguint(&result.return_data)
}
#[test]
fn mulmod_large_modulus_above_2_255_uses_unsigned_reduction() {
let m: num_bigint::BigUint = (num_bigint::BigUint::from(1u32) << 255) + 7u32;
let got = run_mulmod("3", "5", &m.to_str_radix(10));
assert_eq!(
got,
num_bigint::BigUint::from(15u32),
"mulmod(3,5,2^255+7) must be 15 with unsigned reduction"
);
}