tawasuyu/hifas
Motor en Rust para decidir, con demostración y no a ojo, cuándo un conjunto de réplicas local-first puede operar sin coordinación y cuándo no — y, cuando la respuesta es "sí, dentro de límites", proveer el mecanismo que lo garantiza.
T9c: comparación honesta y corrección del registro (cierra T9)
1//! Respaldo de `COMPARISON.md` (T9c): el detector de T8 y el certificador de
2//! escrow de T9 sobre los mismos escenarios. Los veredictos de la tabla del `.md`
3//! son exactamente los que asertan estos tests — nada escrito a mano.
4//!
5//! Cuenta común: saldo inicial 100, invariante `saldo ≥ 0` (slack 100).
6
7use sheafsync::escrow::{Budget, SpendResult};
8use sheafsync::invariant::AccountConfig;
9use sheafsync::oracle::OracleVerdict;
10
11/// Veredicto del DETECTOR de T8 (funde estados y comprueba el merge).
12fn detector(withdrawals: Vec<u64>, edges: Vec<(usize, usize)>) -> OracleVerdict {
13 AccountConfig::new("cmp", 100, withdrawals, edges).haz_coordination()
14}
15
16/// ¿El ESCROW de T9 permite todos los gastos localmente (sin coordinar) con el
17/// reparto dado? `true` = seguro sin coordinación; `false` = alguien pide rebalanceo.
18fn escrow_todo_local(withdrawals: &[u64], split: Vec<i64>) -> bool {
19 let mut budget = Budget::new(split);
20 withdrawals
21 .iter()
22 .enumerate()
23 .all(|(i, &w)| matches!(budget.try_spend(i, w as i64), SpendResult::Local))
24}
25
26#[test]
27fn escenarios_de_comparison_md() {
28 // 1) Conectado, retiros que caben (10/10).
29 assert_eq!(detector(vec![10, 10], vec![(0, 1)]), OracleVerdict::CoordinationFree);
30 assert!(escrow_todo_local(&[10, 10], vec![50, 50]));
31
32 // 2) Conectado, sobregiro (60/60): ambos detectan el problema.
33 assert_eq!(detector(vec![60, 60], vec![(0, 1)]), OracleVerdict::NeedsCoordination);
34 assert!(!escrow_todo_local(&[60, 60], vec![50, 50]));
35
36 // 3) PARTICIONADO, sobregiro (60/60): el detector da FALSO-VERDE...
37 assert_eq!(detector(vec![60, 60], vec![]), OracleVerdict::CoordinationFree);
38 // ...pero el escrow lo bloquea correctamente (60 > presupuesto de 50).
39 assert!(!escrow_todo_local(&[60, 60], vec![50, 50]));
40
41 // 4) Particionado, retiros que caben (10/10): ambos ok.
42 assert_eq!(detector(vec![10, 10], vec![]), OracleVerdict::CoordinationFree);
43 assert!(escrow_todo_local(&[10, 10], vec![50, 50]));
44
45 // 5) Reparto malo [100,0] (R-T9a): retiro 10 en la réplica seca pide
46 // rebalanceo aunque el slack global sobre — es rendimiento, no corrección.
47 assert!(!escrow_todo_local(&[0, 10], vec![100, 0]));
48 // Con un reparto equilibrado [50,50] el mismo gasto es local.
49 assert!(escrow_todo_local(&[0, 10], vec![50, 50]));
50}