use tru::Fx;
pub fn pow_subsidy_shares(pool: Fx, work: &[([u8; 32], Fx)]) -> Vec<([u8; 32], Fx)> {
let mut total = Fx::ZERO;
for (_, w) in work {
if *w > Fx::ZERO {
total = total + *w;
}
}
if total <= Fx::ZERO || pool <= Fx::ZERO {
return work.iter().map(|(n, _)| (*n, Fx::ZERO)).collect();
}
work.iter()
.map(|(n, w)| {
if *w > Fx::ZERO {
(*n, pool * (*w).div(total))
} else {
(*n, Fx::ZERO)
}
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
fn h(b: u8) -> [u8; 32] {
let mut x = [0u8; 32];
x[0] = b;
x
}
#[test]
fn splits_proportional_to_work_only() {
let work = vec![(h(1), Fx::from_int(3)), (h(2), Fx::from_int(1))];
let shares = pow_subsidy_shares(Fx::from_int(4), &work);
assert!((shares[0].1.to_f64() - 3.0).abs() < 1e-6);
assert!((shares[1].1.to_f64() - 1.0).abs() < 1e-6);
}
#[test]
fn zero_work_earns_zero_regardless_of_stake_or_karma() {
let work = vec![(h(1), Fx::from_int(5)), (h(2), Fx::ZERO)];
let shares = pow_subsidy_shares(Fx::from_int(10), &work);
assert!((shares[0].1.to_f64() - 10.0).abs() < 1e-6);
assert_eq!(shares[1].1, Fx::ZERO);
}
#[test]
fn no_positive_work_leaves_pool_unclaimed() {
let work = vec![(h(1), Fx::ZERO), (h(2), Fx::ZERO)];
let shares = pow_subsidy_shares(Fx::from_int(10), &work);
assert!(shares.iter().all(|(_, s)| *s == Fx::ZERO));
}
#[test]
fn conserves_the_pool_across_active_workers() {
let work = vec![
(h(1), Fx::from_int(2)),
(h(2), Fx::from_int(5)),
(h(3), Fx::from_int(3)),
];
let pool = Fx::from_int(1000);
let shares = pow_subsidy_shares(pool, &work);
let sum: f64 = shares.iter().map(|(_, s)| s.to_f64()).sum();
assert!((sum - 1000.0).abs() < 1e-3);
}
#[test]
fn negative_work_is_treated_as_ineligible() {
let work = vec![(h(1), Fx::ZERO - Fx::from_int(1)), (h(2), Fx::from_int(1))];
let shares = pow_subsidy_shares(Fx::from_int(10), &work);
assert_eq!(shares[0].1, Fx::ZERO);
assert!((shares[1].1.to_f64() - 10.0).abs() < 1e-6);
}
#[test]
fn empty_work_list_returns_empty() {
let shares = pow_subsidy_shares(Fx::from_int(10), &[]);
assert!(shares.is_empty());
}
}