fix(cli, gui): Use 0-1 for address pool (#440)

This commit is contained in:
Mohan 2025-06-25 18:13:44 +02:00 committed by GitHub
parent 11b891f530
commit 1587f63232
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 32 additions and 20 deletions

View file

@ -1623,7 +1623,7 @@ impl FfiWallet {
/// # Errors
///
/// Returns an error if:
/// - Percentages don't sum to 100.0
/// - Percentages don't sum to 1.0
/// - Balance is zero
/// - There are more outputs than piconeros in balance
fn distribute(balance: monero::Amount, percentages: &[f64]) -> Result<Vec<monero::Amount>> {
@ -1633,8 +1633,8 @@ impl FfiWallet {
const TOLERANCE: f64 = 1e-6;
let sum: f64 = percentages.iter().sum();
if (sum - 100.0).abs() > TOLERANCE {
bail!("Percentages must sum to 100 (actual sum: {})", sum);
if (sum - 1.0).abs() > TOLERANCE {
bail!("Percentages must sum to 1 (actual sum: {})", sum);
}
// Handle the case where distributable amount is zero
@ -1652,7 +1652,7 @@ impl FfiWallet {
// Distribute amounts according to ratios, except for the last one
for &percentage in &percentages[..percentages.len() - 1] {
let amount_pico = ((balance.as_pico() as f64) * percentage / 100.0).floor() as u64;
let amount_pico = ((balance.as_pico() as f64) * percentage).floor() as u64;
let amount = Amount::from_pico(amount_pico);
amounts.push(amount);
total += amount;
@ -1917,13 +1917,13 @@ mod tests {
return TestResult::discard();
}
// Ensure percentages are valid (non-negative and sum to approximately 100.0)
if percentages.iter().any(|&p| p < 0.0 || p > 100.0) {
// Ensure percentages are valid (non-negative and sum to approximately 1.0)
if percentages.iter().any(|&p| p < 0.0 || p > 1.0) {
return TestResult::discard();
}
let percentage_sum: f64 = percentages.iter().sum();
if (percentage_sum - 100.0).abs() > 1e-6 {
if (percentage_sum - 1.0).abs() > 1e-6 {
return TestResult::discard();
}
@ -1947,12 +1947,12 @@ mod tests {
return TestResult::discard();
}
if percentages.iter().any(|&p| p < 0.0 || p > 100.0) {
if percentages.iter().any(|&p| p < 0.0 || p > 1.0) {
return TestResult::discard();
}
let percentage_sum: f64 = percentages.iter().sum();
if (percentage_sum - 100.0).abs() > 1e-6 {
if (percentage_sum - 1.0).abs() > 1e-6 {
return TestResult::discard();
}
@ -1973,12 +1973,12 @@ mod tests {
return TestResult::discard();
}
if percentages.iter().any(|&p| p < 0.0 || p > 100.0) {
if percentages.iter().any(|&p| p < 0.0 || p > 1.0) {
return TestResult::discard();
}
let percentage_sum: f64 = percentages.iter().sum();
if (percentage_sum - 100.0).abs() > 1e-6 {
if (percentage_sum - 1.0).abs() > 1e-6 {
return TestResult::discard();
}
@ -1991,7 +1991,7 @@ mod tests {
let mut percentages_respected = true;
for i in 0..percentages.len() - 1 {
let expected_amount =
((balance.as_pico() as f64) * percentages[i] / 100.0).floor() as u64;
((balance.as_pico() as f64) * percentages[i]).floor() as u64;
if amounts[i].as_pico() != expected_amount {
percentages_respected = false;
break;
@ -2013,7 +2013,7 @@ mod tests {
#[test]
fn test_distribute_zero_balance() {
let balance = monero::Amount::from_pico(0);
let percentages = vec![50.0, 50.0];
let percentages = vec![0.5, 0.5];
let amounts = FfiWallet::distribute(balance, &percentages);
assert!(amounts.is_err());
@ -2022,7 +2022,7 @@ mod tests {
#[test]
fn test_distribute_insufficient_balance_for_outputs() {
let balance = monero::Amount::from_pico(2);
let percentages = vec![30.0, 30.0, 40.0]; // 3 outputs but only 2 piconeros
let percentages = vec![0.3, 0.3, 0.4]; // 3 outputs but only 2 piconeros
let amounts = FfiWallet::distribute(balance, &percentages);
assert!(amounts.is_err());
@ -2031,7 +2031,7 @@ mod tests {
#[test]
fn test_distribute_simple_case() {
let balance = monero::Amount::from_pico(1000);
let percentages = vec![50.0, 30.0, 20.0];
let percentages = vec![0.5, 0.3, 0.2];
let amounts = FfiWallet::distribute(balance, &percentages).unwrap();
@ -2051,7 +2051,7 @@ mod tests {
#[test]
fn test_distribute_small_donation() {
let balance = monero::Amount::from_pico(1000);
let percentages = vec![99.9, 0.1];
let percentages = vec![0.999, 0.001];
let amounts = FfiWallet::distribute(balance, &percentages).unwrap();
@ -2068,9 +2068,9 @@ mod tests {
}
#[test]
fn test_distribute_percentages_not_sum_to_100() {
fn test_distribute_percentages_not_sum_to_1() {
let balance = monero::Amount::from_pico(1000);
let percentages = vec![50.0, 30.0]; // Only sums to 80%
let percentages = vec![0.5, 0.3]; // Only sums to 0.8
let amounts = FfiWallet::distribute(balance, &percentages);
assert!(amounts.is_err());

View file

@ -0,0 +1,11 @@
-- Fix percentage values that are stored as 0-100 instead of 0-1
-- This migration converts percentage values for swap_ids where the sum > 1.0
-- by scaling all percentages for that swap_id by dividing by 100
UPDATE monero_addresses
SET percentage = percentage / 100.0
WHERE swap_id IN (
SELECT swap_id
FROM monero_addresses
GROUP BY swap_id
HAVING SUM(percentage) > 1.0
);

View file

@ -307,7 +307,7 @@ impl MoneroAddressPool {
self.0.iter().map(|address| address.address()).collect()
}
/// Returns a vector of all percentages as f64 values.
/// Returns a vector of all percentages as f64 values (0-1 range).
pub fn percentages(&self) -> Vec<f64> {
self.0
.iter()
@ -903,10 +903,11 @@ mod tests {
let address = "53gEuGZUhP9JMEBZoGaFNzhwEgiG7hwQdMCqFxiyiTeFPmkbt1mAoNybEUvYBKHcnrSgxnVWgZsTvRBaHBNXPa8tHiCU51a".parse().unwrap();
// Valid percentages should work
// Valid percentages should work (0-1 range)
assert!(LabeledMoneroAddress::new(address, Decimal::ZERO, "test".to_string()).is_ok());
assert!(LabeledMoneroAddress::new(address, Decimal::ONE, "test".to_string()).is_ok());
assert!(LabeledMoneroAddress::new(address, Decimal::new(5, 1), "test".to_string()).is_ok()); // 0.5
assert!(LabeledMoneroAddress::new(address, Decimal::new(9925, 4), "test".to_string()).is_ok()); // 0.9925
// Invalid percentages should fail
assert!(