diff --git a/crates/origin-chain-governance/src/tests_integration.rs b/crates/origin-chain-governance/src/tests_integration.rs index 6156140..4e21609 100644 --- a/crates/origin-chain-governance/src/tests_integration.rs +++ b/crates/origin-chain-governance/src/tests_integration.rs @@ -1144,3 +1144,43 @@ fn scenario_10_lifecycle_walkthrough_canonical() { assert!(matches!(vote_bob, VoteStance::Aye)); }); } + +/// HUECO ABIERTO — medido por Palace el 2026-09-06, confirmado por Charon el +/// 2026-09-23 sobre `63918f3`. `cast_vote` (y `override_council_veto`) suman el +/// `stake_weight` que DECLARA el votante sin contrastarlo con nada: el diseño dice +/// «1 OGC = 1 voto» (doc de `lib.rs:403`) y el código es un contador que suma lo que +/// le digan. Hoy no drena la tesorería porque `treasury_disbursement` exige además +/// `ensure_root`, pero cualquiera aprueba o bloquea una propuesta con `u128::MAX`, +/// y el día que se retire el sudo (L-7) y la gobernanza ejecute (L-8) sí la drena. +/// +/// El `ignore` NO lo esconde: `cargo test -- --ignored` lo enseña en rojo, y +/// QUITARLO es el criterio de cierre de RV15 (peso derivado del saldo + un freeze +/// para que el mismo OGC no vote dos veces pasando por una transferencia). +#[test] +#[ignore = "RV15: cast_vote acepta el peso que declara el votante (hueco medido 2026-09-06)"] +fn un_voto_no_puede_pesar_mas_que_el_saldo_del_votante() { + new_test_ext().execute_with(|| { + let id = submit_default(alice_origin()); + let saldo = Balances::free_balance(bob_account()); + let r = Governance::cast_vote(bob_origin(), id, VoteStance::Aye, saldo + 1); + assert!( + r.is_err(), + "un voto de peso {} con saldo {} fue ACEPTADO", + saldo + 1, + saldo + ); + let p = Proposals::::get(id).expect("propuesta existe"); + assert_eq!(p.yes_weight, 0, "el peso inventado entró al recuento"); + + // Control positivo: sin él, una «cura» que rechazara TODO voto pasaría este + // test. El saldo entero sí vota, y cuenta entero. + assert_ok!(Governance::cast_vote( + bob_origin(), + id, + VoteStance::Aye, + saldo + )); + let p = Proposals::::get(id).expect("propuesta existe"); + assert_eq!(p.yes_weight, saldo, "un voto legítimo no contó entero"); + }); +}