feat(governance): RV16 — el override del veto se decide al cerrar la ventana, con 4/5 de quien participa
Cierra L-11 (override real del veto). David aprobó el 4/5 el 2026-10-01: «exigir una mayoría calificada de 4/5 en el consejo para anular un veto de emergencia es el cerrojo adecuado antes de retirar la llave Sudo». Qué cambia - La ventana de override tiene dos lados: revertir (override_council_veto) y sostener (uphold_council_veto, nuevo). Quien votó Nay puede sostener el veto; antes no tenía voz. - Nada se decide al cruzar un umbral. finalize_override (nuevo) decide UNA vez, al cerrar la ventana: se revierte si override >= ceil(4/5 * max(quórum, participación)). - El quórum se fija al vetar: max(Aye + Nay del voto original, MinOverrideQuorum). Abstain no cuenta. Así el bond que el veto devuelve no basta para revertirlo y un upgrade no lo mueve. - MinOverrideQuorum en el runtime = 100M OGC (el 10% de PILAR_4 §6.6); integrity_test exige que supere MIN_PROPOSAL_STAKE. - El periodo de voto ordinario dura al menos 14 días (MIN = DEFAULT) y el veto solo cabe dentro de él. Un voto de peso 0 en la ventana se rechaza. - spec_version 16. Pesos de uphold_council_veto y finalize_override medidos (frame-omni-bencher 0.15.0, 50 steps x 20 repeats); los demás re-medidos en el mismo banco. La revisión adversarial (nivel 1 de la directiva) tumbó la primera versión - Bloqueante: la regla de quórum no era monótona y votar «sostener» podía REVERTIR el veto. Ahora sostener nunca ayuda a revertir (test con la frontera exacta 2250/2251). - El mínimo de 48 h regalaba la vía de emergencia a cualquiera; ahora 14 días. - Las abstenciones inflaban el quórum; el quórum podía cambiar con un upgrade; la spec no subía. - 18 contrafactuales (mutaciones V1..V18), todos en rojo sin su pieza. Divergencia que queda abierta (D1 del roadmap V2): PILAR_4 §6.6 ratificó «override por 2/3» y el código implementa 4/5, que es lo que David confirmó. La constitución del DAO lo fija. Tests: gobernanza 78 (94 con runtime-benchmarks), runtime 44. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a694cbd9f0
commit
3771a15165
8 files changed
+1081
-173
No files matched your search
@@ -94,7 +94,13 @@ mod benchmarks {
|
|||||||
fund::<T>(&caller);
|
fund::<T>(&caller);
|
||||||
|
|
||||||
#[extrinsic_call]
|
#[extrinsic_call]
|
||||||
submit_proposal(RawOrigin::Signed(caller), [1u8; 32], [2u8; 32], 1_000u64, MIN_PROPOSAL_STAKE);
|
submit_proposal(
|
||||||
|
RawOrigin::Signed(caller),
|
||||||
|
[1u8; 32],
|
||||||
|
[2u8; 32],
|
||||||
|
MIN_VOTING_PERIOD_BLOCKS,
|
||||||
|
MIN_PROPOSAL_STAKE,
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(Proposals::<T>::iter().count(), 1);
|
assert_eq!(Proposals::<T>::iter().count(), 1);
|
||||||
}
|
}
|
||||||
@@ -230,7 +236,7 @@ mod benchmarks {
|
|||||||
seed_proposal::<T>(id, ProposalStatus::Rejected, 0u64);
|
seed_proposal::<T>(id, ProposalStatus::Rejected, 0u64);
|
||||||
VetoRejected::<T>::insert(id, ());
|
VetoRejected::<T>::insert(id, ());
|
||||||
// RV15: ventana abierta, peso respaldado por saldo y la lista de respaldo casi
|
// RV15: ventana abierta, peso respaldado por saldo y la lista de respaldo casi
|
||||||
// llena (peor caso). Y rama cara: el override ALCANZA el umbral y aprueba.
|
// llena (peor caso). RV16: el voto ya no decide nada; eso es finalize_override.
|
||||||
OverrideDeadline::<T>::insert(id, 1_000_000u64);
|
OverrideDeadline::<T>::insert(id, 1_000_000u64);
|
||||||
fund::<T>(&caller);
|
fund::<T>(&caller);
|
||||||
seed_backing::<T>(
|
seed_backing::<T>(
|
||||||
@@ -243,10 +249,6 @@ mod benchmarks {
|
|||||||
override_council_veto(RawOrigin::Signed(caller.clone()), id, MIN_PROPOSAL_STAKE);
|
override_council_veto(RawOrigin::Signed(caller.clone()), id, MIN_PROPOSAL_STAKE);
|
||||||
|
|
||||||
assert!(OverrideVotes::<T>::contains_key(id, &caller));
|
assert!(OverrideVotes::<T>::contains_key(id, &caller));
|
||||||
assert!(matches!(
|
|
||||||
Proposals::<T>::get(id).unwrap().status,
|
|
||||||
ProposalStatus::Approved
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[benchmark]
|
#[benchmark]
|
||||||
@@ -332,5 +334,48 @@ mod benchmarks {
|
|||||||
assert_eq!(VoteLocks::<T>::get(&votante).len() as u32, max - vencidos);
|
assert_eq!(VoteLocks::<T>::get(&votante).len() as u32, max - vencidos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[benchmark]
|
||||||
|
fn uphold_council_veto() {
|
||||||
|
// Mismo peor caso que override_council_veto, por el otro lado.
|
||||||
|
let caller: T::AccountId = whitelisted_caller();
|
||||||
|
let id = ProposalId([7u8; 32]);
|
||||||
|
seed_proposal::<T>(id, ProposalStatus::Rejected, 0u64);
|
||||||
|
VetoRejected::<T>::insert(id, ());
|
||||||
|
OverrideDeadline::<T>::insert(id, 1_000_000u64);
|
||||||
|
fund::<T>(&caller);
|
||||||
|
seed_backing::<T>(
|
||||||
|
&caller,
|
||||||
|
T::MaxActiveVotes::get().saturating_sub(1),
|
||||||
|
1_000_000u64,
|
||||||
|
);
|
||||||
|
|
||||||
|
#[extrinsic_call]
|
||||||
|
uphold_council_veto(RawOrigin::Signed(caller.clone()), id, MIN_PROPOSAL_STAKE);
|
||||||
|
|
||||||
|
assert!(UpholdVotes::<T>::contains_key(id, &caller));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[benchmark]
|
||||||
|
fn finalize_override() {
|
||||||
|
// Rama cara: el override REVIERTE el veto (escribe la propuesta y el evento).
|
||||||
|
let caller: T::AccountId = whitelisted_caller();
|
||||||
|
let id = ProposalId([7u8; 32]);
|
||||||
|
seed_proposal::<T>(id, ProposalStatus::Rejected, 0u64);
|
||||||
|
VetoRejected::<T>::insert(id, ());
|
||||||
|
OverrideDeadline::<T>::insert(id, 1u64);
|
||||||
|
OverrideQuorum::<T>::insert(id, T::MinOverrideQuorum::get());
|
||||||
|
OverrideStakeTotal::<T>::insert(id, T::MinOverrideQuorum::get().max(1));
|
||||||
|
frame_system::Pallet::<T>::set_block_number(2u32.into());
|
||||||
|
|
||||||
|
#[extrinsic_call]
|
||||||
|
finalize_override(RawOrigin::Signed(caller), id);
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
Proposals::<T>::get(id).unwrap().status,
|
||||||
|
ProposalStatus::Approved
|
||||||
|
));
|
||||||
|
assert!(!OverrideDeadline::<T>::contains_key(id));
|
||||||
|
}
|
||||||
|
|
||||||
impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test);
|
impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test);
|
||||||
}
|
}
|
||||||
@@ -23,7 +23,10 @@
|
|||||||
//! - 5.2: `pallet_collective` integration (Technical Council)
|
//! - 5.2: `pallet_collective` integration (Technical Council)
|
||||||
//! - 5.3: OIP stake minimum 1,000 OGC canonical
|
//! - 5.3: OIP stake minimum 1,000 OGC canonical
|
||||||
//! - 5.4: Stake-weighted voting (1 OGC = 1 vote, 14-day period)
|
//! - 5.4: Stake-weighted voting (1 OGC = 1 vote, 14-day period)
|
||||||
//! - 5.5: Technical Council veto + override (2/3 voters)
|
//! - 5.5: Technical Council veto (2/3 del council) + override comunitario. PILAR_4 §6.6
|
||||||
|
//! ratificó «overridable por 2/3 voters»; el código implementa 4/5 de la participación
|
||||||
|
//! en la ventana, con quórum (RV16), y David confirmó el 4/5 el 2026-10-01. Lo que
|
||||||
|
//! el código hace y lo que dice la doctrina difieren hasta que la constitución lo fije.
|
||||||
//! - 5.6: AI governance agent interfaces (5 agents advise-only)
|
//! - 5.6: AI governance agent interfaces (5 agents advise-only)
|
||||||
//! - 5.7: Emergency referendum (48h, 5/7 Technical Council)
|
//! - 5.7: Emergency referendum (48h, 5/7 Technical Council)
|
||||||
//! - 5.8: Treasury disbursement extrinsics
|
//! - 5.8: Treasury disbursement extrinsics
|
||||||
@@ -208,6 +211,17 @@ pub mod pallet {
|
|||||||
/// de quien votara en ella (demostrado por la revisión adversaria de RV15).
|
/// de quien votara en ella (demostrado por la revisión adversaria de RV15).
|
||||||
pub const MAX_VOTING_PERIOD_BLOCKS: u64 = 2 * DEFAULT_VOTING_PERIOD_BLOCKS;
|
pub const MAX_VOTING_PERIOD_BLOCKS: u64 = 2 * DEFAULT_VOTING_PERIOD_BLOCKS;
|
||||||
|
|
||||||
|
/// RV16: periodo de voto mínimo de una propuesta ORDINARIA: 14 días, el que
|
||||||
|
/// ratifica PILAR_4 §6.6 («14-day period»).
|
||||||
|
///
|
||||||
|
/// Antes podía ser 0: el proponente enviaba, votaba en el mismo bloque y
|
||||||
|
/// finalizaba en el siguiente, aprobando con su único voto. La primera versión de
|
||||||
|
/// RV16 lo subió a 48 h y la revisión adversaria mostró por qué no basta: era la
|
||||||
|
/// duración de la vía de emergencia sin sus 5/7 endosos del council, y como el
|
||||||
|
/// veto solo cabe dentro del periodo, el proponente elegía cuánto tiempo tenía el
|
||||||
|
/// council para reaccionar. Las 48 h quedan solo para `submit_emergency_proposal`.
|
||||||
|
pub const MIN_VOTING_PERIOD_BLOCKS: u64 = DEFAULT_VOTING_PERIOD_BLOCKS;
|
||||||
|
|
||||||
/// Emergency voting period canonical canonical 48 hours per CHN-4 5.7.
|
/// Emergency voting period canonical canonical 48 hours per CHN-4 5.7.
|
||||||
/// 48 hours × 600 blocks/hour (@ 6s slot Aura) = 28,800 blocks.
|
/// 48 hours × 600 blocks/hour (@ 6s slot Aura) = 28,800 blocks.
|
||||||
pub const EMERGENCY_VOTING_PERIOD_BLOCKS: u64 = 48 * 600;
|
pub const EMERGENCY_VOTING_PERIOD_BLOCKS: u64 = 48 * 600;
|
||||||
@@ -309,6 +323,14 @@ pub mod pallet {
|
|||||||
#[pallet::constant]
|
#[pallet::constant]
|
||||||
type MaxActiveVotes: Get<u32>;
|
type MaxActiveVotes: Get<u32>;
|
||||||
|
|
||||||
|
/// RV16: participación mínima (en OGC) para que un override pueda revertir
|
||||||
|
/// un veto. El quórum efectivo, fijado en el veto, es el MAYOR entre esto y la
|
||||||
|
/// participación del voto original (Aye + Nay): revertir al council exige al
|
||||||
|
/// menos tanta comunidad como la que votó. Es un parámetro de la constitución,
|
||||||
|
/// y `integrity_test` exige que supere el bond de una propuesta.
|
||||||
|
#[pallet::constant]
|
||||||
|
type MinOverrideQuorum: Get<u128>;
|
||||||
|
|
||||||
/// FIX-6: PalletId del que se deriva la cuenta-tesorería (vault). Cuenta
|
/// FIX-6: PalletId del que se deriva la cuenta-tesorería (vault). Cuenta
|
||||||
/// determinística sin private key — nadie la controla por wallet, solo la
|
/// determinística sin private key — nadie la controla por wallet, solo la
|
||||||
/// governance vía treasury_disbursement (Root). Honor Inmutable #16: la
|
/// governance vía treasury_disbursement (Root). Honor Inmutable #16: la
|
||||||
@@ -330,6 +352,19 @@ pub mod pallet {
|
|||||||
#[pallet::storage_version(STORAGE_VERSION)]
|
#[pallet::storage_version(STORAGE_VERSION)]
|
||||||
pub struct Pallet<T>(_);
|
pub struct Pallet<T>(_);
|
||||||
|
|
||||||
|
#[pallet::hooks]
|
||||||
|
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
|
||||||
|
/// RV16: el quórum de override tiene que superar el bond de una propuesta. Si
|
||||||
|
/// no, el proponente revertiría solo el veto con el bond que el propio veto le
|
||||||
|
/// devuelve (hallazgo C3), y con 0 bastaría una unidad base.
|
||||||
|
fn integrity_test() {
|
||||||
|
assert!(
|
||||||
|
T::MinOverrideQuorum::get() > MIN_PROPOSAL_STAKE,
|
||||||
|
"MinOverrideQuorum debe superar MIN_PROPOSAL_STAKE (hallazgo C3)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// RV15: por qué este pallet congela OGC.
|
/// RV15: por qué este pallet congela OGC.
|
||||||
#[pallet::composite_enum]
|
#[pallet::composite_enum]
|
||||||
pub enum FreezeReason {
|
pub enum FreezeReason {
|
||||||
@@ -339,6 +374,25 @@ pub mod pallet {
|
|||||||
GovernanceVote,
|
GovernanceVote,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// RV16: votos que SOSTIENEN el veto del council durante su ventana de
|
||||||
|
/// override (la otra mitad del recuento; antes no existía y quien votó Nay no
|
||||||
|
/// podía defender el veto).
|
||||||
|
#[pallet::storage]
|
||||||
|
pub type UpholdVotes<T: Config> = StorageDoubleMap<
|
||||||
|
_,
|
||||||
|
Blake2_128Concat,
|
||||||
|
ProposalId,
|
||||||
|
Blake2_128Concat,
|
||||||
|
T::AccountId,
|
||||||
|
u128,
|
||||||
|
OptionQuery,
|
||||||
|
>;
|
||||||
|
|
||||||
|
/// RV16: OGC total que sostiene el veto, por propuesta.
|
||||||
|
#[pallet::storage]
|
||||||
|
pub type UpholdStakeTotal<T: Config> =
|
||||||
|
StorageMap<_, Blake2_128Concat, ProposalId, u128, ValueQuery>;
|
||||||
|
|
||||||
/// RV15: los votos vivos que respalda cada cuenta (ver [`VoteBacking`]). El
|
/// RV15: los votos vivos que respalda cada cuenta (ver [`VoteBacking`]). El
|
||||||
/// freeze `GovernanceVote` de la cuenta es el máximo de sus `amount`.
|
/// freeze `GovernanceVote` de la cuenta es el máximo de sus `amount`.
|
||||||
#[pallet::storage]
|
#[pallet::storage]
|
||||||
@@ -357,6 +411,16 @@ pub mod pallet {
|
|||||||
pub type OverrideDeadline<T: Config> =
|
pub type OverrideDeadline<T: Config> =
|
||||||
StorageMap<_, Blake2_128Concat, ProposalId, u64, OptionQuery>;
|
StorageMap<_, Blake2_128Concat, ProposalId, u64, OptionQuery>;
|
||||||
|
|
||||||
|
/// RV16: quórum del override de cada propuesta vetada, FIJADO en el veto:
|
||||||
|
/// max(Aye + Nay del voto original, `MinOverrideQuorum`). Se guarda y no se
|
||||||
|
/// recalcula al decidir: un runtime upgrade que cambie el parámetro no mueve la
|
||||||
|
/// meta de una ventana ya abierta (hallazgo de la revisión de RV16). La
|
||||||
|
/// abstención no cuenta: si contara, el mismo OGC abstenido en muchas propuestas
|
||||||
|
/// a la vez inflaría el quórum de todas sin aparecer en ninguna ventana.
|
||||||
|
#[pallet::storage]
|
||||||
|
pub type OverrideQuorum<T: Config> =
|
||||||
|
StorageMap<_, Blake2_128Concat, ProposalId, u128, OptionQuery>;
|
||||||
|
|
||||||
/// CHN-4 canonical: `Proposals<StorageMap<ProposalId, Proposal>>` canonical.
|
/// CHN-4 canonical: `Proposals<StorageMap<ProposalId, Proposal>>` canonical.
|
||||||
#[pallet::storage]
|
#[pallet::storage]
|
||||||
#[pallet::getter(fn proposal_of)]
|
#[pallet::getter(fn proposal_of)]
|
||||||
@@ -595,15 +659,16 @@ pub mod pallet {
|
|||||||
},
|
},
|
||||||
/// Council veto canonical overridden por community supermajority (Phase 5.5 v0.0.35).
|
/// Council veto canonical overridden por community supermajority (Phase 5.5 v0.0.35).
|
||||||
///
|
///
|
||||||
/// Emitted canonical cuando override votes reach 4/5 total stake canonical canonical.
|
/// RV16: lo emite `finalize_override` cuando, cerrada la ventana, revertir
|
||||||
/// Proposal status canonical flipped from Rejected back to Voting OR Approved
|
/// reunió 4/5 del mayor entre el quórum y la participación. La propuesta pasa
|
||||||
/// (depending canonical en tally pre-veto).
|
/// a `Approved`. (Antes se emitía al cruzar el umbral en mitad de la ventana.)
|
||||||
CouncilVetoOverridden {
|
CouncilVetoOverridden {
|
||||||
/// ProposalId canonical overridden.
|
/// Propuesta cuyo veto se revierte.
|
||||||
id: ProposalId,
|
id: ProposalId,
|
||||||
/// Total override stake canonical at moment of override.
|
/// OGC que votó revertir.
|
||||||
override_stake: u128,
|
override_stake: u128,
|
||||||
/// Total community stake canonical baseline (proposal.stake at submit).
|
/// RV16: participación total en la ventana (revertir + sostener). Antes era
|
||||||
|
/// la base del umbral viejo (stake + yes + nay + abstain de la propuesta).
|
||||||
total_community_stake: u128,
|
total_community_stake: u128,
|
||||||
},
|
},
|
||||||
/// Emergency proposal submitted canonical (Phase 5.7 v0.0.35).
|
/// Emergency proposal submitted canonical (Phase 5.7 v0.0.35).
|
||||||
@@ -654,6 +719,30 @@ pub mod pallet {
|
|||||||
/// OGC que sigue congelado por votos todavía vivos.
|
/// OGC que sigue congelado por votos todavía vivos.
|
||||||
still_frozen: u128,
|
still_frozen: u128,
|
||||||
},
|
},
|
||||||
|
/// RV16: un voto en la ventana de override de un veto (para el indexer del
|
||||||
|
/// portal: antes un voto de override no emitía nada hasta el desenlace).
|
||||||
|
OverrideVoteCast {
|
||||||
|
/// Propuesta vetada.
|
||||||
|
id: ProposalId,
|
||||||
|
/// Quien vota.
|
||||||
|
voter: T::AccountId,
|
||||||
|
/// `true` = sostiene el veto; `false` = vota revertirlo.
|
||||||
|
uphold: bool,
|
||||||
|
/// OGC que respalda el voto (congelado hasta el cierre de la ventana).
|
||||||
|
weight: u128,
|
||||||
|
},
|
||||||
|
/// RV16: se cerró la ventana y el veto se SOSTIENE (el override no alcanzó
|
||||||
|
/// 4/5 de la participación o no hubo quórum).
|
||||||
|
CouncilVetoUpheld {
|
||||||
|
/// Propuesta vetada.
|
||||||
|
id: ProposalId,
|
||||||
|
/// OGC que votó revertir el veto.
|
||||||
|
override_stake: u128,
|
||||||
|
/// OGC que votó sostenerlo.
|
||||||
|
uphold_stake: u128,
|
||||||
|
/// Quórum que se exigía.
|
||||||
|
quorum: u128,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pallet errors canonical.
|
/// Pallet errors canonical.
|
||||||
@@ -729,6 +818,15 @@ pub mod pallet {
|
|||||||
VotingPeriodTooLong,
|
VotingPeriodTooLong,
|
||||||
/// RV15: la cuenta ya respalda `MaxActiveVotes` votos vivos.
|
/// RV15: la cuenta ya respalda `MaxActiveVotes` votos vivos.
|
||||||
TooManyActiveVotes,
|
TooManyActiveVotes,
|
||||||
|
/// RV16: el periodo de voto pedido es menor que `MIN_VOTING_PERIOD_BLOCKS`.
|
||||||
|
VotingPeriodTooShort,
|
||||||
|
/// RV16: la ventana de override sigue abierta; el resultado se decide al
|
||||||
|
/// cerrarse, no al cruzar un umbral a mitad de camino.
|
||||||
|
OverrideWindowOpen,
|
||||||
|
/// RV16: esta propuesta no tiene un override pendiente de decidir.
|
||||||
|
OverrideNotPending,
|
||||||
|
/// RV16: un voto de la ventana de override tiene que pesar más de 0.
|
||||||
|
ZeroWeightWindowVote,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Minimum stake canonical proposal submission per CHN-4 5.3.
|
/// Minimum stake canonical proposal submission per CHN-4 5.3.
|
||||||
@@ -763,6 +861,10 @@ pub mod pallet {
|
|||||||
voting_period_blocks <= MAX_VOTING_PERIOD_BLOCKS,
|
voting_period_blocks <= MAX_VOTING_PERIOD_BLOCKS,
|
||||||
Error::<T>::VotingPeriodTooLong
|
Error::<T>::VotingPeriodTooLong
|
||||||
);
|
);
|
||||||
|
ensure!(
|
||||||
|
voting_period_blocks >= MIN_VOTING_PERIOD_BLOCKS,
|
||||||
|
Error::<T>::VotingPeriodTooShort
|
||||||
|
);
|
||||||
|
|
||||||
let proposer_acct = AccountId::new(account_bytes::<T>(&who));
|
let proposer_acct = AccountId::new(account_bytes::<T>(&who));
|
||||||
let id = crate::derive_proposal_id(&proposer_acct, &title_hash, &body_hash);
|
let id = crate::derive_proposal_id(&proposer_acct, &title_hash, &body_hash);
|
||||||
@@ -1044,6 +1146,14 @@ pub mod pallet {
|
|||||||
Error::<T>::ProposalAlreadyFinalized
|
Error::<T>::ProposalAlreadyFinalized
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// RV16: el veto solo cabe DURANTE el periodo de voto. Antes se podía vetar
|
||||||
|
// entre end_block y el finalize, y el resultado dependía de quién llamara
|
||||||
|
// primero (carrera entre el council y cualquiera que finalizara).
|
||||||
|
ensure!(
|
||||||
|
current_block_u64::<T>() <= proposal.end_block,
|
||||||
|
Error::<T>::VotingPeriodClosed
|
||||||
|
);
|
||||||
|
|
||||||
// Step 4: council size canonical > 0
|
// Step 4: council size canonical > 0
|
||||||
let council_size = CouncilSize::<T>::get();
|
let council_size = CouncilSize::<T>::get();
|
||||||
ensure!(council_size > 0, Error::<T>::InsufficientCouncilSize);
|
ensure!(council_size > 0, Error::<T>::InsufficientCouncilSize);
|
||||||
@@ -1070,6 +1180,10 @@ pub mod pallet {
|
|||||||
|
|
||||||
let required = council_veto_threshold(council_size);
|
let required = council_veto_threshold(council_size);
|
||||||
if live_count >= required {
|
if live_count >= required {
|
||||||
|
// RV16: el quórum del override se fija AQUÍ, con la participación del
|
||||||
|
// voto original (Aye + Nay; la abstención no cuenta).
|
||||||
|
let turnout = proposal.yes_weight.saturating_add(proposal.nay_weight);
|
||||||
|
OverrideQuorum::<T>::insert(proposal_id, turnout.max(T::MinOverrideQuorum::get()));
|
||||||
// Veto threshold reached canonical → flip status canonical
|
// Veto threshold reached canonical → flip status canonical
|
||||||
proposal.status = ProposalStatus::Rejected;
|
proposal.status = ProposalStatus::Rejected;
|
||||||
Proposals::<T>::insert(proposal_id, proposal);
|
Proposals::<T>::insert(proposal_id, proposal);
|
||||||
@@ -1142,21 +1256,21 @@ pub mod pallet {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// CHN-4 5.5 v0.0.35 canonical: `override_council_veto` Phase 5.5 dispatchable.
|
/// CHN-4 5.5 + RV16: votar REVERTIR un veto del council durante su ventana.
|
||||||
///
|
///
|
||||||
/// Community supermajority canonical override de council veto. 4/5 total stake
|
/// Doctrina: el council veta con 2/3 y la comunidad revierte con 4/5
|
||||||
/// canonical required canonical per CHN-4 5.5. Proposal status canonical flipped
|
/// (`docs/ORIGIN_DAO_DONDE_VIVE_V1.md`). RV16 hace real ese 4/5 (hallazgo C3 de
|
||||||
/// desde Rejected canonical back to Approved canonical (community canonical >
|
/// SUPER_AUDIT_V1, demostrado otra vez por la revisión de RV15):
|
||||||
/// council canonical veto on supermajority).
|
/// - el voto NO voltea nada al cruzar un umbral; el resultado se decide al
|
||||||
|
/// cerrarse la ventana, en [`Pallet::finalize_override`];
|
||||||
|
/// - el 4/5 es de la PARTICIPACIÓN en la ventana (revertir + sostener), no de
|
||||||
|
/// los recuentos de la propia propuesta: antes contaba el bond del proponente,
|
||||||
|
/// que el veto le devuelve y que luego podía votar;
|
||||||
|
/// - quien quiere el veto puede defenderlo con [`Pallet::uphold_council_veto`];
|
||||||
|
/// - hace falta quórum.
|
||||||
///
|
///
|
||||||
/// 7-step canonical flow:
|
/// El peso sale del saldo y queda congelado hasta el cierre de la ventana (RV15).
|
||||||
/// 1. ensure_signed canonical
|
/// Una cuenta vota una vez en la ventana, en un solo lado.
|
||||||
/// 2. Get proposal (must canonical be Rejected previamente vía veto)
|
|
||||||
/// 3. Validate proposal.stake > 0 (baseline canonical)
|
|
||||||
/// 4. !AdvisorFlags canonical (humans-only canonical canonical)
|
|
||||||
/// 5. !OverrideVotes contains canonical (double-vote prevention)
|
|
||||||
/// 6. Insert vote + increment total stake
|
|
||||||
/// 7. Check threshold canonical 4/5: if >= → flip to Approved + emit CouncilVetoOverridden
|
|
||||||
#[pallet::call_index(8)]
|
#[pallet::call_index(8)]
|
||||||
#[pallet::weight(T::WeightInfo::override_council_veto())]
|
#[pallet::weight(T::WeightInfo::override_council_veto())]
|
||||||
pub fn override_council_veto(
|
pub fn override_council_veto(
|
||||||
@@ -1165,73 +1279,7 @@ pub mod pallet {
|
|||||||
stake_weight: u128,
|
stake_weight: u128,
|
||||||
) -> DispatchResult {
|
) -> DispatchResult {
|
||||||
let who = ensure_signed(origin)?;
|
let who = ensure_signed(origin)?;
|
||||||
|
Self::cast_override_window_vote(who, proposal_id, stake_weight, false)
|
||||||
let mut proposal = Proposals::<T>::get(proposal_id)
|
|
||||||
.ok_or(Error::<T>::ProposalNotFound)?;
|
|
||||||
|
|
||||||
ensure!(
|
|
||||||
matches!(proposal.status, ProposalStatus::Rejected),
|
|
||||||
Error::<T>::ProposalNotRejected
|
|
||||||
);
|
|
||||||
|
|
||||||
// Audit 2026-07-04: el override SOLO revierte un VETO del council, no
|
|
||||||
// un rechazo por voto popular. Sin esta marca, una propuesta que la
|
|
||||||
// comunidad votó EN CONTRA (Rejected vía finalize_proposal) podía
|
|
||||||
// resucitarse a Approved, invirtiendo el voto. Solo las vetadas aplican.
|
|
||||||
ensure!(
|
|
||||||
VetoRejected::<T>::contains_key(proposal_id),
|
|
||||||
Error::<T>::ProposalNotVetoRejected
|
|
||||||
);
|
|
||||||
|
|
||||||
ensure!(
|
|
||||||
!AdvisorFlags::<T>::contains_key(&who),
|
|
||||||
Error::<T>::AdvisorMayNotVote
|
|
||||||
);
|
|
||||||
|
|
||||||
ensure!(
|
|
||||||
!OverrideVotes::<T>::contains_key(proposal_id, &who),
|
|
||||||
Error::<T>::AlreadyOverrideVoted
|
|
||||||
);
|
|
||||||
|
|
||||||
// RV15: el override tiene ventana, y su peso sale del saldo y queda
|
|
||||||
// congelado hasta que la ventana cierre.
|
|
||||||
let deadline =
|
|
||||||
OverrideDeadline::<T>::get(proposal_id).ok_or(Error::<T>::OverrideWindowClosed)?;
|
|
||||||
ensure!(
|
|
||||||
current_block_u64::<T>() <= deadline,
|
|
||||||
Error::<T>::OverrideWindowClosed
|
|
||||||
);
|
|
||||||
Self::back_vote_with_funds(
|
|
||||||
&who,
|
|
||||||
proposal_id,
|
|
||||||
BackingKind::Override,
|
|
||||||
stake_weight,
|
|
||||||
deadline,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
OverrideVotes::<T>::insert(proposal_id, &who, stake_weight);
|
|
||||||
let new_total = OverrideStakeTotal::<T>::get(proposal_id)
|
|
||||||
.saturating_add(stake_weight);
|
|
||||||
OverrideStakeTotal::<T>::insert(proposal_id, new_total);
|
|
||||||
|
|
||||||
let baseline = proposal.stake.saturating_add(proposal.yes_weight)
|
|
||||||
.saturating_add(proposal.nay_weight)
|
|
||||||
.saturating_add(proposal.abstain_weight);
|
|
||||||
|
|
||||||
let required = override_supermajority_threshold(baseline);
|
|
||||||
|
|
||||||
if new_total >= required {
|
|
||||||
proposal.status = ProposalStatus::Approved;
|
|
||||||
Proposals::<T>::insert(proposal_id, proposal);
|
|
||||||
|
|
||||||
Self::deposit_event(Event::CouncilVetoOverridden {
|
|
||||||
id: proposal_id,
|
|
||||||
override_stake: new_total,
|
|
||||||
total_community_stake: baseline,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// CHN-4 5.7 v0.0.35 canonical: `submit_emergency_proposal` Phase 5.7 dispatchable.
|
/// CHN-4 5.7 v0.0.35 canonical: `submit_emergency_proposal` Phase 5.7 dispatchable.
|
||||||
@@ -1473,6 +1521,89 @@ pub mod pallet {
|
|||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// RV16: votar SOSTENER un veto del council durante su ventana de override.
|
||||||
|
/// Es la otra mitad del recuento: sin ella, quien votó Nay no podía defender
|
||||||
|
/// el veto y cualquier minoría con OGC suficiente lo revertía sola.
|
||||||
|
#[pallet::call_index(14)]
|
||||||
|
#[pallet::weight(T::WeightInfo::uphold_council_veto())]
|
||||||
|
pub fn uphold_council_veto(
|
||||||
|
origin: OriginFor<T>,
|
||||||
|
proposal_id: ProposalId,
|
||||||
|
stake_weight: u128,
|
||||||
|
) -> DispatchResult {
|
||||||
|
let who = ensure_signed(origin)?;
|
||||||
|
Self::cast_override_window_vote(who, proposal_id, stake_weight, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// RV16: decide un override cuando su ventana ya cerró. Lo puede llamar
|
||||||
|
/// cualquiera.
|
||||||
|
///
|
||||||
|
/// El veto se revierte (la propuesta pasa a `Approved`) solo si:
|
||||||
|
/// 1. la participación (revertir + sostener) alcanza el quórum, que es el
|
||||||
|
/// MAYOR entre `MinOverrideQuorum` y la participación del voto original
|
||||||
|
/// (Aye + Nay + Abstain); y
|
||||||
|
/// 2. revertir reúne al menos 4/5 de esa participación.
|
||||||
|
///
|
||||||
|
/// En cualquier otro caso el veto se sostiene. Se decide una sola vez.
|
||||||
|
#[pallet::call_index(15)]
|
||||||
|
#[pallet::weight(T::WeightInfo::finalize_override())]
|
||||||
|
pub fn finalize_override(origin: OriginFor<T>, proposal_id: ProposalId) -> DispatchResult {
|
||||||
|
ensure_signed(origin)?;
|
||||||
|
let mut proposal =
|
||||||
|
Proposals::<T>::get(proposal_id).ok_or(Error::<T>::ProposalNotFound)?;
|
||||||
|
ensure!(
|
||||||
|
matches!(proposal.status, ProposalStatus::Rejected),
|
||||||
|
Error::<T>::ProposalNotRejected
|
||||||
|
);
|
||||||
|
ensure!(
|
||||||
|
VetoRejected::<T>::contains_key(proposal_id),
|
||||||
|
Error::<T>::ProposalNotVetoRejected
|
||||||
|
);
|
||||||
|
let deadline =
|
||||||
|
OverrideDeadline::<T>::get(proposal_id).ok_or(Error::<T>::OverrideNotPending)?;
|
||||||
|
ensure!(
|
||||||
|
current_block_u64::<T>() > deadline,
|
||||||
|
Error::<T>::OverrideWindowOpen
|
||||||
|
);
|
||||||
|
|
||||||
|
let override_stake = OverrideStakeTotal::<T>::get(proposal_id);
|
||||||
|
let uphold_stake = UpholdStakeTotal::<T>::get(proposal_id);
|
||||||
|
let participation = override_stake.saturating_add(uphold_stake);
|
||||||
|
let quorum =
|
||||||
|
OverrideQuorum::<T>::get(proposal_id).ok_or(Error::<T>::OverrideNotPending)?;
|
||||||
|
|
||||||
|
// Se decide una sola vez: sin ventana, no hay override pendiente.
|
||||||
|
OverrideDeadline::<T>::remove(proposal_id);
|
||||||
|
OverrideQuorum::<T>::remove(proposal_id);
|
||||||
|
|
||||||
|
// Regla MONÓTONA: revertir necesita 4/5 del MAYOR entre el quórum y la
|
||||||
|
// participación. La primera versión exigía `participation >= quorum` y
|
||||||
|
// aparte el 4/5 de la participación, y la revisión adversaria demostró que
|
||||||
|
// así SOSTENER podía revertir el veto: un voto de sostener completaba el
|
||||||
|
// quórum que el lado revertir no alcanzaba solo, y no votar era la manera
|
||||||
|
// de defender el veto. Con max(quorum, participación), cada OGC que sostiene
|
||||||
|
// solo puede subir el listón de revertir, nunca bajarlo.
|
||||||
|
let overridden = override_stake > 0
|
||||||
|
&& override_stake >= override_supermajority_threshold(quorum.max(participation));
|
||||||
|
if overridden {
|
||||||
|
proposal.status = ProposalStatus::Approved;
|
||||||
|
Proposals::<T>::insert(proposal_id, proposal);
|
||||||
|
Self::deposit_event(Event::CouncilVetoOverridden {
|
||||||
|
id: proposal_id,
|
||||||
|
override_stake,
|
||||||
|
total_community_stake: participation,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Self::deposit_event(Event::CouncilVetoUpheld {
|
||||||
|
id: proposal_id,
|
||||||
|
override_stake,
|
||||||
|
uphold_stake,
|
||||||
|
quorum,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// El mayor OGC respaldado por una lista de votos (0 si está vacía).
|
/// El mayor OGC respaldado por una lista de votos (0 si está vacía).
|
||||||
@@ -1485,6 +1616,74 @@ pub mod pallet {
|
|||||||
<frame_system::Pallet<T>>::block_number().saturated_into::<u64>()
|
<frame_system::Pallet<T>>::block_number().saturated_into::<u64>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// RV16 — un voto en la ventana de override de un veto, de cualquiera de los dos
|
||||||
|
/// lados. Comparte TODAS las comprobaciones: que la propuesta esté vetada (no
|
||||||
|
/// rechazada por voto popular), que la ventana esté abierta, que la cuenta no sea
|
||||||
|
/// advisor, que no haya votado ya en la ventana (en ningún lado), y que el peso
|
||||||
|
/// salga de su saldo y quede congelado hasta el cierre.
|
||||||
|
impl<T: Config> Pallet<T> {
|
||||||
|
fn cast_override_window_vote(
|
||||||
|
who: T::AccountId,
|
||||||
|
proposal_id: ProposalId,
|
||||||
|
stake_weight: u128,
|
||||||
|
uphold: bool,
|
||||||
|
) -> DispatchResult {
|
||||||
|
// Un voto de peso 0 en la ventana no decide nada y dejaría una entrada
|
||||||
|
// permanente (y a la cuenta sin poder votar después en esa ventana).
|
||||||
|
ensure!(stake_weight > 0, Error::<T>::ZeroWeightWindowVote);
|
||||||
|
let proposal = Proposals::<T>::get(proposal_id).ok_or(Error::<T>::ProposalNotFound)?;
|
||||||
|
ensure!(
|
||||||
|
matches!(proposal.status, ProposalStatus::Rejected),
|
||||||
|
Error::<T>::ProposalNotRejected
|
||||||
|
);
|
||||||
|
// Audit 2026-07-04: el override SOLO revierte un VETO del council, no
|
||||||
|
// un rechazo por voto popular.
|
||||||
|
ensure!(
|
||||||
|
VetoRejected::<T>::contains_key(proposal_id),
|
||||||
|
Error::<T>::ProposalNotVetoRejected
|
||||||
|
);
|
||||||
|
ensure!(
|
||||||
|
!AdvisorFlags::<T>::contains_key(&who),
|
||||||
|
Error::<T>::AdvisorMayNotVote
|
||||||
|
);
|
||||||
|
ensure!(
|
||||||
|
!OverrideVotes::<T>::contains_key(proposal_id, &who)
|
||||||
|
&& !UpholdVotes::<T>::contains_key(proposal_id, &who),
|
||||||
|
Error::<T>::AlreadyOverrideVoted
|
||||||
|
);
|
||||||
|
let deadline =
|
||||||
|
OverrideDeadline::<T>::get(proposal_id).ok_or(Error::<T>::OverrideWindowClosed)?;
|
||||||
|
ensure!(
|
||||||
|
current_block_u64::<T>() <= deadline,
|
||||||
|
Error::<T>::OverrideWindowClosed
|
||||||
|
);
|
||||||
|
Self::back_vote_with_funds(
|
||||||
|
&who,
|
||||||
|
proposal_id,
|
||||||
|
BackingKind::Override,
|
||||||
|
stake_weight,
|
||||||
|
deadline,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
if uphold {
|
||||||
|
UpholdVotes::<T>::insert(proposal_id, &who, stake_weight);
|
||||||
|
UpholdStakeTotal::<T>::mutate(proposal_id, |t| *t = t.saturating_add(stake_weight));
|
||||||
|
} else {
|
||||||
|
OverrideVotes::<T>::insert(proposal_id, &who, stake_weight);
|
||||||
|
OverrideStakeTotal::<T>::mutate(proposal_id, |t| {
|
||||||
|
*t = t.saturating_add(stake_weight)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Self::deposit_event(Event::OverrideVoteCast {
|
||||||
|
id: proposal_id,
|
||||||
|
voter: who,
|
||||||
|
uphold,
|
||||||
|
weight: stake_weight,
|
||||||
|
});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// RV15 — el OGC que respalda un voto.
|
/// RV15 — el OGC que respalda un voto.
|
||||||
impl<T: Config> Pallet<T> {
|
impl<T: Config> Pallet<T> {
|
||||||
/// Exige que `weight` no supere el OGC LIBRE de `who` y lo deja congelado
|
/// Exige que `weight` no supere el OGC LIBRE de `who` y lo deja congelado
|
||||||
|
|||||||
@@ -48,6 +48,11 @@ impl origin_chain_governance::Config for Test {
|
|||||||
type RuntimeFreezeReason = RuntimeFreezeReason;
|
type RuntimeFreezeReason = RuntimeFreezeReason;
|
||||||
// Bajo a propósito: el límite de votos vivos tiene que poder probarse.
|
// Bajo a propósito: el límite de votos vivos tiene que poder probarse.
|
||||||
type MaxActiveVotes = frame_support::traits::ConstU32<4>;
|
type MaxActiveVotes = frame_support::traits::ConstU32<4>;
|
||||||
|
// RV16: quórum mínimo de override del mock = DOS bonds (2.000 OGC). Por encima del
|
||||||
|
// bond a propósito, como en el runtime (100M OGC frente a 1.000): con el quórum
|
||||||
|
// igual al bond, el proponente revertía solo con el bond devuelto (revisión de RV16).
|
||||||
|
type MinOverrideQuorum =
|
||||||
|
frame_support::traits::ConstU128<{ 2 * crate::pallet::MIN_PROPOSAL_STAKE }>;
|
||||||
type TreasuryPalletId = TestTreasuryPalletId;
|
type TreasuryPalletId = TestTreasuryPalletId;
|
||||||
type WeightInfo = ();
|
type WeightInfo = ();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,13 @@ use origin_chain_types::{AccountId, VoteStance};
|
|||||||
|
|
||||||
const TITLE_HASH: [u8; 32] = [10u8; 32];
|
const TITLE_HASH: [u8; 32] = [10u8; 32];
|
||||||
const BODY_HASH: [u8; 32] = [20u8; 32];
|
const BODY_HASH: [u8; 32] = [20u8; 32];
|
||||||
const VOTING_PERIOD: u64 = 100;
|
/// RV16: el periodo de los tests es el mínimo legal (48 h). Antes era 100 bloques,
|
||||||
|
/// que hoy rechaza `submit_proposal`.
|
||||||
|
const VOTING_PERIOD: u64 = crate::pallet::MIN_VOTING_PERIOD_BLOCKS;
|
||||||
|
/// Un bloque claramente posterior al fin del periodo de `submit_default` (inicio
|
||||||
|
/// en el bloque 1). Sustituye a los `run_to_block(200)` que suponían un periodo
|
||||||
|
/// de 100 bloques.
|
||||||
|
const TRAS_EL_PERIODO: u64 = 1 + VOTING_PERIOD + 100;
|
||||||
|
|
||||||
fn submit_default(origin: frame_system::pallet_prelude::OriginFor<Test>) -> ProposalId {
|
fn submit_default(origin: frame_system::pallet_prelude::OriginFor<Test>) -> ProposalId {
|
||||||
let alice_bytes = alice_account_bytes();
|
let alice_bytes = alice_account_bytes();
|
||||||
@@ -187,7 +193,7 @@ fn scenario_7_finalize_approved_yes_majority_canonical() {
|
|||||||
assert_ok!(Governance::cast_vote(charlie_origin(), id, VoteStance::Nay, 500));
|
assert_ok!(Governance::cast_vote(charlie_origin(), id, VoteStance::Nay, 500));
|
||||||
|
|
||||||
// Advance past voting period
|
// Advance past voting period
|
||||||
run_to_block(200);
|
run_to_block(TRAS_EL_PERIODO);
|
||||||
|
|
||||||
assert_ok!(Governance::finalize_proposal(alice_origin(), id));
|
assert_ok!(Governance::finalize_proposal(alice_origin(), id));
|
||||||
|
|
||||||
@@ -214,7 +220,7 @@ fn scenario_8_finalize_rejected_nay_majority_canonical() {
|
|||||||
assert_ok!(Governance::cast_vote(bob_origin(), id, VoteStance::Aye, 300));
|
assert_ok!(Governance::cast_vote(bob_origin(), id, VoteStance::Aye, 300));
|
||||||
assert_ok!(Governance::cast_vote(charlie_origin(), id, VoteStance::Nay, 700));
|
assert_ok!(Governance::cast_vote(charlie_origin(), id, VoteStance::Nay, 700));
|
||||||
|
|
||||||
run_to_block(200);
|
run_to_block(TRAS_EL_PERIODO);
|
||||||
|
|
||||||
assert_ok!(Governance::finalize_proposal(alice_origin(), id));
|
assert_ok!(Governance::finalize_proposal(alice_origin(), id));
|
||||||
|
|
||||||
@@ -239,7 +245,7 @@ fn scenario_8b_finalize_tie_rejected_canonical() {
|
|||||||
assert_ok!(Governance::cast_vote(bob_origin(), id, VoteStance::Aye, 500));
|
assert_ok!(Governance::cast_vote(bob_origin(), id, VoteStance::Aye, 500));
|
||||||
assert_ok!(Governance::cast_vote(charlie_origin(), id, VoteStance::Nay, 500));
|
assert_ok!(Governance::cast_vote(charlie_origin(), id, VoteStance::Nay, 500));
|
||||||
|
|
||||||
run_to_block(200);
|
run_to_block(TRAS_EL_PERIODO);
|
||||||
|
|
||||||
assert_ok!(Governance::finalize_proposal(alice_origin(), id));
|
assert_ok!(Governance::finalize_proposal(alice_origin(), id));
|
||||||
|
|
||||||
@@ -262,7 +268,7 @@ fn scenario_9_vote_closed_period_rejected_canonical() {
|
|||||||
let id = submit_default(alice_origin());
|
let id = submit_default(alice_origin());
|
||||||
|
|
||||||
// Advance past voting period
|
// Advance past voting period
|
||||||
run_to_block(200);
|
run_to_block(TRAS_EL_PERIODO);
|
||||||
|
|
||||||
// Vote after end_block → VotingPeriodClosed
|
// Vote after end_block → VotingPeriodClosed
|
||||||
assert_noop!(
|
assert_noop!(
|
||||||
@@ -650,21 +656,29 @@ fn override_scenario_2_successful_override_canonical() {
|
|||||||
let proposal = Proposals::<Test>::get(id).unwrap();
|
let proposal = Proposals::<Test>::get(id).unwrap();
|
||||||
assert!(matches!(proposal.status, ProposalStatus::Rejected));
|
assert!(matches!(proposal.status, ProposalStatus::Rejected));
|
||||||
|
|
||||||
// Community supermajority override canonical
|
// RV16: Bob vota revertir con el quórum mínimo (sin votos previos, el quórum
|
||||||
// baseline = stake + yes + nay + abstain = MIN_PROPOSAL_STAKE (no votes cast)
|
// es MinOverrideQuorum = 2 × MIN_PROPOSAL_STAKE en el mock) y nadie sostiene.
|
||||||
// required = ceil(baseline × 4 / 5)
|
|
||||||
let baseline = MIN_PROPOSAL_STAKE;
|
|
||||||
let required = (baseline * 4 + 4) / 5;
|
|
||||||
|
|
||||||
// Bob casts override con stake = required (enough)
|
|
||||||
assert_ok!(Governance::override_council_veto(
|
assert_ok!(Governance::override_council_veto(
|
||||||
bob_origin(),
|
bob_origin(),
|
||||||
id,
|
id,
|
||||||
required,
|
2 * MIN_PROPOSAL_STAKE,
|
||||||
));
|
));
|
||||||
|
// El voto no voltea nada: el resultado se decide al cerrar la ventana.
|
||||||
|
assert!(matches!(
|
||||||
|
Proposals::<Test>::get(id).unwrap().status,
|
||||||
|
ProposalStatus::Rejected
|
||||||
|
));
|
||||||
|
let cierre = crate::OverrideDeadline::<Test>::get(id).expect("ventana");
|
||||||
|
run_to_block(cierre + 1);
|
||||||
|
assert_ok!(Governance::finalize_override(charlie_origin(), id));
|
||||||
|
|
||||||
let proposal_after = Proposals::<Test>::get(id).unwrap();
|
let proposal_after = Proposals::<Test>::get(id).unwrap();
|
||||||
assert!(matches!(proposal_after.status, ProposalStatus::Approved));
|
assert!(matches!(proposal_after.status, ProposalStatus::Approved));
|
||||||
|
System::assert_last_event(RuntimeEvent::Governance(Event::CouncilVetoOverridden {
|
||||||
|
id,
|
||||||
|
override_stake: 2 * MIN_PROPOSAL_STAKE,
|
||||||
|
total_community_stake: 2 * MIN_PROPOSAL_STAKE,
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -881,7 +895,7 @@ fn treasury_scenario_3_insufficient_balance_rejected_canonical() {
|
|||||||
// Setup approved proposal
|
// Setup approved proposal
|
||||||
let id = submit_default(alice_origin());
|
let id = submit_default(alice_origin());
|
||||||
assert_ok!(Governance::cast_vote(bob_origin(), id, VoteStance::Aye, 1000));
|
assert_ok!(Governance::cast_vote(bob_origin(), id, VoteStance::Aye, 1000));
|
||||||
run_to_block(200);
|
run_to_block(TRAS_EL_PERIODO);
|
||||||
assert_ok!(Governance::finalize_proposal(alice_origin(), id));
|
assert_ok!(Governance::finalize_proposal(alice_origin(), id));
|
||||||
|
|
||||||
// Try disburse 200 (exceeds 100 balance)
|
// Try disburse 200 (exceeds 100 balance)
|
||||||
@@ -908,7 +922,7 @@ fn treasury_scenario_4_successful_disbursement_canonical() {
|
|||||||
// Setup approved proposal
|
// Setup approved proposal
|
||||||
let id = submit_default(alice_origin());
|
let id = submit_default(alice_origin());
|
||||||
assert_ok!(Governance::cast_vote(bob_origin(), id, VoteStance::Aye, 1000));
|
assert_ok!(Governance::cast_vote(bob_origin(), id, VoteStance::Aye, 1000));
|
||||||
run_to_block(200);
|
run_to_block(TRAS_EL_PERIODO);
|
||||||
assert_ok!(Governance::finalize_proposal(alice_origin(), id));
|
assert_ok!(Governance::finalize_proposal(alice_origin(), id));
|
||||||
|
|
||||||
// Disburse 300_000 canonical
|
// Disburse 300_000 canonical
|
||||||
@@ -951,7 +965,7 @@ fn override_no_puede_revertir_un_rechazo_por_voto_popular() {
|
|||||||
// La comunidad la rechaza en las urnas (Nay > Aye).
|
// La comunidad la rechaza en las urnas (Nay > Aye).
|
||||||
assert_ok!(Governance::cast_vote(bob_origin(), id, VoteStance::Aye, 100));
|
assert_ok!(Governance::cast_vote(bob_origin(), id, VoteStance::Aye, 100));
|
||||||
assert_ok!(Governance::cast_vote(charlie_origin(), id, VoteStance::Nay, 900));
|
assert_ok!(Governance::cast_vote(charlie_origin(), id, VoteStance::Nay, 900));
|
||||||
run_to_block(200);
|
run_to_block(TRAS_EL_PERIODO);
|
||||||
assert_ok!(Governance::finalize_proposal(alice_origin(), id));
|
assert_ok!(Governance::finalize_proposal(alice_origin(), id));
|
||||||
let p = Proposals::<Test>::get(id).unwrap();
|
let p = Proposals::<Test>::get(id).unwrap();
|
||||||
assert!(matches!(p.status, ProposalStatus::Rejected), "rechazada por voto");
|
assert!(matches!(p.status, ProposalStatus::Rejected), "rechazada por voto");
|
||||||
@@ -977,7 +991,7 @@ fn treasury_disbursement_es_single_use_anti_replay() {
|
|||||||
assert_ok!(Governance::treasury_deposit(alice_origin(), 1_000_000));
|
assert_ok!(Governance::treasury_deposit(alice_origin(), 1_000_000));
|
||||||
let id = submit_default(alice_origin());
|
let id = submit_default(alice_origin());
|
||||||
assert_ok!(Governance::cast_vote(bob_origin(), id, VoteStance::Aye, 1000));
|
assert_ok!(Governance::cast_vote(bob_origin(), id, VoteStance::Aye, 1000));
|
||||||
run_to_block(200);
|
run_to_block(TRAS_EL_PERIODO);
|
||||||
assert_ok!(Governance::finalize_proposal(alice_origin(), id));
|
assert_ok!(Governance::finalize_proposal(alice_origin(), id));
|
||||||
|
|
||||||
// Primer desembolso: OK.
|
// Primer desembolso: OK.
|
||||||
@@ -1054,7 +1068,7 @@ fn bond_se_retiene_al_someter_y_se_devuelve_al_finalizar() {
|
|||||||
assert_eq!(bal(&alice_account()), before - MIN_PROPOSAL_STAKE, "bond retenido");
|
assert_eq!(bal(&alice_account()), before - MIN_PROPOSAL_STAKE, "bond retenido");
|
||||||
assert_eq!(bal(&bond_acct), MIN_PROPOSAL_STAKE, "og/props tiene el bond");
|
assert_eq!(bal(&bond_acct), MIN_PROPOSAL_STAKE, "og/props tiene el bond");
|
||||||
// Finaliza (sin votos → Rejected) → bond devuelto íntegro.
|
// Finaliza (sin votos → Rejected) → bond devuelto íntegro.
|
||||||
run_to_block(200);
|
run_to_block(TRAS_EL_PERIODO);
|
||||||
assert_ok!(Governance::finalize_proposal(alice_origin(), id));
|
assert_ok!(Governance::finalize_proposal(alice_origin(), id));
|
||||||
assert_eq!(bal(&alice_account()), before, "bond devuelto al finalizar");
|
assert_eq!(bal(&alice_account()), before, "bond devuelto al finalizar");
|
||||||
assert_eq!(bal(&bond_acct), 0, "cuenta-bond vacía");
|
assert_eq!(bal(&bond_acct), 0, "cuenta-bond vacía");
|
||||||
@@ -1090,7 +1104,13 @@ fn submit_rechazado_si_no_alcanza_para_el_bond() {
|
|||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
assert_noop!(
|
assert_noop!(
|
||||||
Governance::submit_proposal(pobre, [7u8; 32], [8u8; 32], 100, MIN_PROPOSAL_STAKE),
|
Governance::submit_proposal(
|
||||||
|
pobre,
|
||||||
|
[7u8; 32],
|
||||||
|
[8u8; 32],
|
||||||
|
VOTING_PERIOD,
|
||||||
|
MIN_PROPOSAL_STAKE
|
||||||
|
),
|
||||||
Error::<Test>::StakeInsufficientBalance
|
Error::<Test>::StakeInsufficientBalance
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -1133,7 +1153,7 @@ fn scenario_10_lifecycle_walkthrough_canonical() {
|
|||||||
assert_eq!(proposal.nay_weight, 1000);
|
assert_eq!(proposal.nay_weight, 1000);
|
||||||
|
|
||||||
// Step 3: advance + finalize canonical
|
// Step 3: advance + finalize canonical
|
||||||
run_to_block(200);
|
run_to_block(TRAS_EL_PERIODO);
|
||||||
assert_ok!(Governance::finalize_proposal(alice_origin(), id));
|
assert_ok!(Governance::finalize_proposal(alice_origin(), id));
|
||||||
|
|
||||||
// Verify final status canonical
|
// Verify final status canonical
|
||||||
@@ -1252,7 +1272,7 @@ fn el_ogc_que_respalda_un_voto_no_sale_de_la_cuenta_mientras_el_voto_vive() {
|
|||||||
fn el_freeze_real_es_el_mayor_de_los_votos_vivos_no_el_ultimo() {
|
fn el_freeze_real_es_el_mayor_de_los_votos_vivos_no_el_ultimo() {
|
||||||
new_test_ext().execute_with(|| {
|
new_test_ext().execute_with(|| {
|
||||||
let corta = submit_default(alice_origin());
|
let corta = submit_default(alice_origin());
|
||||||
let larga = propuesta_con_periodo(11, 3 * VOTING_PERIOD);
|
let larga = propuesta_con_periodo(11, 2 * VOTING_PERIOD);
|
||||||
let saldo = Balances::free_balance(bob_account());
|
let saldo = Balances::free_balance(bob_account());
|
||||||
assert_ok!(Governance::cast_vote(
|
assert_ok!(Governance::cast_vote(
|
||||||
bob_origin(),
|
bob_origin(),
|
||||||
@@ -1292,7 +1312,7 @@ fn el_freeze_real_es_el_mayor_de_los_votos_vivos_no_el_ultimo() {
|
|||||||
fn al_vencer_el_voto_grande_solo_queda_congelado_el_pequeno_que_sigue_vivo() {
|
fn al_vencer_el_voto_grande_solo_queda_congelado_el_pequeno_que_sigue_vivo() {
|
||||||
new_test_ext().execute_with(|| {
|
new_test_ext().execute_with(|| {
|
||||||
let corta = submit_default(alice_origin());
|
let corta = submit_default(alice_origin());
|
||||||
let larga = propuesta_con_periodo(11, 3 * VOTING_PERIOD);
|
let larga = propuesta_con_periodo(11, 2 * VOTING_PERIOD);
|
||||||
assert_ok!(Governance::cast_vote(
|
assert_ok!(Governance::cast_vote(
|
||||||
bob_origin(),
|
bob_origin(),
|
||||||
larga,
|
larga,
|
||||||
@@ -1462,7 +1482,7 @@ fn un_voto_vencido_no_revive_con_el_siguiente() {
|
|||||||
fn el_mismo_ogc_entero_vota_en_dos_propuestas() {
|
fn el_mismo_ogc_entero_vota_en_dos_propuestas() {
|
||||||
new_test_ext().execute_with(|| {
|
new_test_ext().execute_with(|| {
|
||||||
let p1 = submit_default(alice_origin());
|
let p1 = submit_default(alice_origin());
|
||||||
let p2 = propuesta_con_periodo(11, 3 * VOTING_PERIOD);
|
let p2 = propuesta_con_periodo(11, 2 * VOTING_PERIOD);
|
||||||
let saldo = Balances::free_balance(bob_account());
|
let saldo = Balances::free_balance(bob_account());
|
||||||
assert_ok!(Governance::cast_vote(
|
assert_ok!(Governance::cast_vote(
|
||||||
bob_origin(),
|
bob_origin(),
|
||||||
@@ -1615,7 +1635,7 @@ fn el_limite_es_de_votos_vivos_y_los_vencidos_no_ocupan_sitio() {
|
|||||||
let corta = submit_default(alice_origin());
|
let corta = submit_default(alice_origin());
|
||||||
let mut largas = Vec::new();
|
let mut largas = Vec::new();
|
||||||
for semilla in 11..15u8 {
|
for semilla in 11..15u8 {
|
||||||
largas.push(propuesta_con_periodo(semilla, 3 * VOTING_PERIOD));
|
largas.push(propuesta_con_periodo(semilla, 2 * VOTING_PERIOD));
|
||||||
}
|
}
|
||||||
assert_ok!(Governance::cast_vote(
|
assert_ok!(Governance::cast_vote(
|
||||||
bob_origin(),
|
bob_origin(),
|
||||||
@@ -1646,3 +1666,529 @@ fn el_limite_es_de_votos_vivos_y_los_vencidos_no_ocupan_sitio() {
|
|||||||
assert_eq!(crate::VoteLocks::<Test>::get(bob_account()).len(), 4);
|
assert_eq!(crate::VoteLocks::<Test>::get(bob_account()).len(), 4);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════
|
||||||
|
// RV16 — el override de un veto del council es un 4/5 REAL de la comunidad.
|
||||||
|
// Doctrina: council veta con 2/3, la comunidad revierte con 4/5. Hallazgo C3
|
||||||
|
// de SUPER_AUDIT_V1, demostrado otra vez por la revisión adversaria de RV15.
|
||||||
|
// ═══════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
/// 1 OGC en unidades base (MIN_PROPOSAL_STAKE son 1.000 OGC). Los importes de los
|
||||||
|
/// tests de RV16 van en OGC reales: el quórum del mock es 1.000 OGC, y con unidades
|
||||||
|
/// base sueltas ningún override lo alcanzaría (me pasó al escribirlos).
|
||||||
|
const OGC: u128 = MIN_PROPOSAL_STAKE / 1_000;
|
||||||
|
|
||||||
|
/// Council de 1 (Alice) y propuesta de Alice vetada en el acto (bloque 1).
|
||||||
|
fn propuesta_vetada() -> (ProposalId, u64) {
|
||||||
|
use crate::pallet::CouncilRole;
|
||||||
|
use frame_support::dispatch::RawOrigin;
|
||||||
|
assert_ok!(Governance::add_council_member(
|
||||||
|
RawOrigin::Root.into(),
|
||||||
|
alice_account(),
|
||||||
|
CouncilRole::Chair,
|
||||||
|
));
|
||||||
|
let id = submit_default(alice_origin());
|
||||||
|
assert_ok!(Governance::veto_proposal(alice_origin(), id));
|
||||||
|
let cierre = crate::OverrideDeadline::<Test>::get(id).expect("el veto abre la ventana");
|
||||||
|
(id, cierre)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn estado(id: ProposalId) -> ProposalStatus {
|
||||||
|
Proposals::<Test>::get(id).expect("propuesta").status
|
||||||
|
}
|
||||||
|
|
||||||
|
/// C3: el bond que el veto devuelve no basta para revertirlo. El quórum (aquí 2.000
|
||||||
|
/// OGC; 100M en el runtime) está por encima del bond (1.000) a propósito, y lo exige
|
||||||
|
/// `integrity_test`. La primera versión de este test usaba 800 OGC y pasaba porque
|
||||||
|
/// 800 no llegaba a un quórum IGUAL al bond: con el bond entero, revertía (revisión
|
||||||
|
/// de RV16). Ahora el bond entero no basta, y el doble sí, si nadie sostiene.
|
||||||
|
#[test]
|
||||||
|
fn el_bond_devuelto_no_basta_para_revertir_el_veto() {
|
||||||
|
for (revertir, revierte) in [(MIN_PROPOSAL_STAKE, false), (2 * MIN_PROPOSAL_STAKE, true)] {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
let (id, cierre) = propuesta_vetada();
|
||||||
|
assert_ok!(Governance::override_council_veto(
|
||||||
|
alice_origin(),
|
||||||
|
id,
|
||||||
|
revertir
|
||||||
|
));
|
||||||
|
run_to_block(cierre + 1);
|
||||||
|
assert_ok!(Governance::finalize_override(bob_origin(), id));
|
||||||
|
let aprobada = matches!(estado(id), ProposalStatus::Approved);
|
||||||
|
assert_eq!(aprobada, revierte, "el proponente revierte con {revertir}");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// La regla es MONÓTONA: sostener el veto nunca ayuda a revertirlo. La primera
|
||||||
|
/// versión de RV16 contaba sostener para el quórum y la revisión demostró que, con
|
||||||
|
/// Aye 9.000 / Nay 1.000, Charlie revertía el veto votando SOSTENER con 1.000, y lo
|
||||||
|
/// defendía no votando. Ahora: revertir 9.000 contra quórum 10.000 pasa sin nadie
|
||||||
|
/// enfrente, y cada OGC que sostiene solo sube el listón (2.250 aún no, 2.251 sí).
|
||||||
|
#[test]
|
||||||
|
fn sostener_el_veto_nunca_ayuda_a_revertirlo() {
|
||||||
|
let casos = [
|
||||||
|
(0u128, true),
|
||||||
|
(1_000, true),
|
||||||
|
(2_000, true),
|
||||||
|
(2_250, true),
|
||||||
|
(2_251, false),
|
||||||
|
];
|
||||||
|
let mut anterior = true;
|
||||||
|
for (sostener, esperado) in casos {
|
||||||
|
let aprobada = new_test_ext().execute_with(|| {
|
||||||
|
use crate::pallet::CouncilRole;
|
||||||
|
use frame_support::dispatch::RawOrigin;
|
||||||
|
assert_ok!(Governance::add_council_member(
|
||||||
|
RawOrigin::Root.into(),
|
||||||
|
alice_account(),
|
||||||
|
CouncilRole::Chair,
|
||||||
|
));
|
||||||
|
let id = submit_default(alice_origin());
|
||||||
|
assert_ok!(Governance::cast_vote(
|
||||||
|
bob_origin(),
|
||||||
|
id,
|
||||||
|
VoteStance::Aye,
|
||||||
|
9_000 * OGC
|
||||||
|
));
|
||||||
|
assert_ok!(Governance::cast_vote(
|
||||||
|
charlie_origin(),
|
||||||
|
id,
|
||||||
|
VoteStance::Nay,
|
||||||
|
1_000 * OGC
|
||||||
|
));
|
||||||
|
assert_ok!(Governance::veto_proposal(alice_origin(), id));
|
||||||
|
let cierre = crate::OverrideDeadline::<Test>::get(id).expect("ventana");
|
||||||
|
assert_ok!(Governance::override_council_veto(
|
||||||
|
bob_origin(),
|
||||||
|
id,
|
||||||
|
9_000 * OGC
|
||||||
|
));
|
||||||
|
if sostener > 0 {
|
||||||
|
assert_ok!(Governance::uphold_council_veto(
|
||||||
|
charlie_origin(),
|
||||||
|
id,
|
||||||
|
sostener * OGC
|
||||||
|
));
|
||||||
|
}
|
||||||
|
run_to_block(cierre + 1);
|
||||||
|
assert_ok!(Governance::finalize_override(alice_origin(), id));
|
||||||
|
matches!(estado(id), ProposalStatus::Approved)
|
||||||
|
});
|
||||||
|
assert_eq!(aprobada, esperado, "sostener {sostener} OGC");
|
||||||
|
assert!(
|
||||||
|
anterior || !aprobada,
|
||||||
|
"sostener {sostener} OGC ayudó a revertir"
|
||||||
|
);
|
||||||
|
anterior = aprobada;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Quien quiere el veto puede DEFENDERLO, y el 4/5 se mide sobre la participación:
|
||||||
|
/// 4/5 exacto revierte; un poco menos lo sostiene.
|
||||||
|
#[test]
|
||||||
|
fn quien_voto_nay_puede_sostener_el_veto_y_el_umbral_es_4_de_5_exacto() {
|
||||||
|
for (sostener, revierte) in [(2_000 * OGC, true), (2_000 * OGC + 1, false)] {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
let (id, cierre) = propuesta_vetada();
|
||||||
|
// 8.000 OGC revertir (Bob) contra `sostener` (Charlie): 8.000/10.000 = 4/5 exacto.
|
||||||
|
assert_ok!(Governance::override_council_veto(
|
||||||
|
bob_origin(),
|
||||||
|
id,
|
||||||
|
8_000 * OGC
|
||||||
|
));
|
||||||
|
assert_ok!(Governance::uphold_council_veto(
|
||||||
|
charlie_origin(),
|
||||||
|
id,
|
||||||
|
sostener
|
||||||
|
));
|
||||||
|
run_to_block(cierre + 1);
|
||||||
|
assert_ok!(Governance::finalize_override(alice_origin(), id));
|
||||||
|
let aprobada = matches!(estado(id), ProposalStatus::Approved);
|
||||||
|
assert_eq!(
|
||||||
|
aprobada, revierte,
|
||||||
|
"revertir 8.000 contra sostener {sostener}"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Un voto no voltea nada a mitad de ventana: el resultado se decide al cerrarla.
|
||||||
|
/// Antes, cruzar el umbral aprobaba en el acto, antes de que nadie pudiera sostener.
|
||||||
|
#[test]
|
||||||
|
fn el_override_se_decide_al_cerrar_la_ventana_no_al_cruzar_un_umbral() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
let (id, cierre) = propuesta_vetada();
|
||||||
|
let saldo = Balances::free_balance(bob_account());
|
||||||
|
assert_ok!(Governance::override_council_veto(bob_origin(), id, saldo));
|
||||||
|
assert!(
|
||||||
|
matches!(estado(id), ProposalStatus::Rejected),
|
||||||
|
"100% y sigue vetada"
|
||||||
|
);
|
||||||
|
|
||||||
|
run_to_block(cierre);
|
||||||
|
assert_noop!(
|
||||||
|
Governance::finalize_override(alice_origin(), id),
|
||||||
|
Error::<Test>::OverrideWindowOpen
|
||||||
|
);
|
||||||
|
run_to_block(cierre + 1);
|
||||||
|
assert_ok!(Governance::finalize_override(alice_origin(), id));
|
||||||
|
assert!(matches!(estado(id), ProposalStatus::Approved));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// El quórum, fijado en el veto, es el MAYOR entre MinOverrideQuorum y la
|
||||||
|
/// participación del voto original contando Aye + Nay (la abstención NO cuenta: el
|
||||||
|
/// mismo OGC abstenido en muchas propuestas inflaría el quórum de todas sin aparecer
|
||||||
|
/// en ninguna ventana). Con Aye 1.000 + Nay 2.000 + Abstain 2.000, el quórum es
|
||||||
|
/// 3.000, y revertir solo, sin nadie que sostenga, necesita 4/5 de él: 2.400.
|
||||||
|
#[test]
|
||||||
|
fn el_quorum_es_aye_mas_nay_del_voto_original_y_se_fija_en_el_veto() {
|
||||||
|
for (revertir, revierte) in [(2_400 * OGC - 1, false), (2_400 * OGC, true)] {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
use crate::pallet::CouncilRole;
|
||||||
|
use frame_support::dispatch::RawOrigin;
|
||||||
|
assert_ok!(Governance::add_council_member(
|
||||||
|
RawOrigin::Root.into(),
|
||||||
|
alice_account(),
|
||||||
|
CouncilRole::Chair,
|
||||||
|
));
|
||||||
|
let id = submit_default(alice_origin());
|
||||||
|
assert_ok!(Governance::cast_vote(
|
||||||
|
alice_origin(),
|
||||||
|
id,
|
||||||
|
VoteStance::Aye,
|
||||||
|
1_000 * OGC
|
||||||
|
));
|
||||||
|
assert_ok!(Governance::cast_vote(
|
||||||
|
bob_origin(),
|
||||||
|
id,
|
||||||
|
VoteStance::Nay,
|
||||||
|
2_000 * OGC
|
||||||
|
));
|
||||||
|
assert_ok!(Governance::cast_vote(
|
||||||
|
charlie_origin(),
|
||||||
|
id,
|
||||||
|
VoteStance::Abstain,
|
||||||
|
2_000 * OGC
|
||||||
|
));
|
||||||
|
assert_ok!(Governance::veto_proposal(alice_origin(), id));
|
||||||
|
assert_eq!(crate::OverrideQuorum::<Test>::get(id), Some(3_000 * OGC));
|
||||||
|
let cierre = crate::OverrideDeadline::<Test>::get(id).expect("ventana");
|
||||||
|
assert_ok!(Governance::override_council_veto(
|
||||||
|
bob_origin(),
|
||||||
|
id,
|
||||||
|
revertir
|
||||||
|
));
|
||||||
|
run_to_block(cierre + 1);
|
||||||
|
assert_ok!(Governance::finalize_override(alice_origin(), id));
|
||||||
|
let aprobada = matches!(estado(id), ProposalStatus::Approved);
|
||||||
|
assert_eq!(
|
||||||
|
aprobada, revierte,
|
||||||
|
"revertir {revertir} con quórum 3.000 OGC"
|
||||||
|
);
|
||||||
|
if !revierte {
|
||||||
|
System::assert_last_event(RuntimeEvent::Governance(Event::CouncilVetoUpheld {
|
||||||
|
id,
|
||||||
|
override_stake: revertir,
|
||||||
|
uphold_stake: 0,
|
||||||
|
quorum: 3_000 * OGC,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// El quórum que decide es el GUARDADO en el veto, no uno recalculado al final: un
|
||||||
|
/// upgrade que cambie el parámetro no mueve la meta de una ventana abierta.
|
||||||
|
#[test]
|
||||||
|
fn finalize_override_usa_el_quorum_fijado_en_el_veto() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
let (id, cierre) = propuesta_vetada();
|
||||||
|
assert_eq!(
|
||||||
|
crate::OverrideQuorum::<Test>::get(id),
|
||||||
|
Some(2 * MIN_PROPOSAL_STAKE)
|
||||||
|
);
|
||||||
|
assert_ok!(Governance::override_council_veto(
|
||||||
|
bob_origin(),
|
||||||
|
id,
|
||||||
|
MIN_PROPOSAL_STAKE
|
||||||
|
));
|
||||||
|
// Simula un quórum guardado más bajo: si finalize recalculara, no lo vería.
|
||||||
|
crate::OverrideQuorum::<Test>::insert(id, MIN_PROPOSAL_STAKE);
|
||||||
|
run_to_block(cierre + 1);
|
||||||
|
assert_ok!(Governance::finalize_override(alice_origin(), id));
|
||||||
|
assert!(matches!(estado(id), ProposalStatus::Approved));
|
||||||
|
assert!(crate::OverrideQuorum::<Test>::get(id).is_none());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Los votos del mismo lado se SUMAN, en los dos lados.
|
||||||
|
#[test]
|
||||||
|
fn los_votos_del_mismo_lado_se_suman() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
let (id, cierre) = propuesta_vetada();
|
||||||
|
assert_ok!(Governance::override_council_veto(
|
||||||
|
bob_origin(),
|
||||||
|
id,
|
||||||
|
4_000 * OGC
|
||||||
|
));
|
||||||
|
assert_ok!(Governance::override_council_veto(
|
||||||
|
alice_origin(),
|
||||||
|
id,
|
||||||
|
4_000 * OGC
|
||||||
|
));
|
||||||
|
assert_ok!(Governance::uphold_council_veto(
|
||||||
|
charlie_origin(),
|
||||||
|
id,
|
||||||
|
2_000 * OGC
|
||||||
|
));
|
||||||
|
assert_eq!(crate::OverrideStakeTotal::<Test>::get(id), 8_000 * OGC);
|
||||||
|
run_to_block(cierre + 1);
|
||||||
|
assert_ok!(Governance::finalize_override(alice_origin(), id));
|
||||||
|
assert!(
|
||||||
|
matches!(estado(id), ProposalStatus::Approved),
|
||||||
|
"8.000 de 10.000"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
let (id, cierre) = propuesta_vetada();
|
||||||
|
assert_ok!(Governance::override_council_veto(
|
||||||
|
bob_origin(),
|
||||||
|
id,
|
||||||
|
8_000 * OGC
|
||||||
|
));
|
||||||
|
assert_ok!(Governance::uphold_council_veto(
|
||||||
|
charlie_origin(),
|
||||||
|
id,
|
||||||
|
1_000 * OGC
|
||||||
|
));
|
||||||
|
assert_ok!(Governance::uphold_council_veto(
|
||||||
|
alice_origin(),
|
||||||
|
id,
|
||||||
|
1_000 * OGC + 1
|
||||||
|
));
|
||||||
|
assert_eq!(crate::UpholdStakeTotal::<Test>::get(id), 2_000 * OGC + 1);
|
||||||
|
run_to_block(cierre + 1);
|
||||||
|
assert_ok!(Governance::finalize_override(alice_origin(), id));
|
||||||
|
assert!(
|
||||||
|
matches!(estado(id), ProposalStatus::Rejected),
|
||||||
|
"8.000 de 10.000+1"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Quien vota revertir no vuelve a votar en la ventana, ni en el mismo lado ni en el
|
||||||
|
/// otro: sin esto, el mismo OGC revertiría hasta MaxActiveVotes veces.
|
||||||
|
#[test]
|
||||||
|
fn quien_revierte_no_vuelve_a_votar_en_la_ventana() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
let (id, _) = propuesta_vetada();
|
||||||
|
let saldo = Balances::free_balance(bob_account());
|
||||||
|
assert_ok!(Governance::override_council_veto(bob_origin(), id, saldo));
|
||||||
|
System::assert_last_event(RuntimeEvent::Governance(Event::OverrideVoteCast {
|
||||||
|
id,
|
||||||
|
voter: bob_account(),
|
||||||
|
uphold: false,
|
||||||
|
weight: saldo,
|
||||||
|
}));
|
||||||
|
assert_noop!(
|
||||||
|
Governance::override_council_veto(bob_origin(), id, saldo),
|
||||||
|
Error::<Test>::AlreadyOverrideVoted
|
||||||
|
);
|
||||||
|
assert_noop!(
|
||||||
|
Governance::uphold_council_veto(bob_origin(), id, 1),
|
||||||
|
Error::<Test>::AlreadyOverrideVoted
|
||||||
|
);
|
||||||
|
assert_eq!(crate::OverrideStakeTotal::<Test>::get(id), saldo);
|
||||||
|
assert_eq!(crate::VoteLocks::<Test>::get(bob_account()).len(), 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// SOSTENER pasa por las mismas puertas que revertir: advisor, voto popular,
|
||||||
|
/// propuesta aún en votación, sin ventana, ventana cerrada y peso 0.
|
||||||
|
#[test]
|
||||||
|
fn sostener_pasa_por_las_mismas_puertas_que_revertir() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
use frame_support::dispatch::RawOrigin;
|
||||||
|
let (id, cierre) = propuesta_vetada();
|
||||||
|
assert_noop!(
|
||||||
|
Governance::uphold_council_veto(charlie_origin(), id, 0),
|
||||||
|
Error::<Test>::ZeroWeightWindowVote
|
||||||
|
);
|
||||||
|
assert_noop!(
|
||||||
|
Governance::override_council_veto(charlie_origin(), id, 0),
|
||||||
|
Error::<Test>::ZeroWeightWindowVote
|
||||||
|
);
|
||||||
|
assert_ok!(Governance::mark_as_advisor(
|
||||||
|
RawOrigin::Root.into(),
|
||||||
|
charlie_account()
|
||||||
|
));
|
||||||
|
assert_noop!(
|
||||||
|
Governance::uphold_council_veto(charlie_origin(), id, 1),
|
||||||
|
Error::<Test>::AdvisorMayNotVote
|
||||||
|
);
|
||||||
|
// Propuesta todavía en votación.
|
||||||
|
let viva = propuesta_con_periodo(12, VOTING_PERIOD);
|
||||||
|
assert_noop!(
|
||||||
|
Governance::uphold_council_veto(bob_origin(), viva, 1),
|
||||||
|
Error::<Test>::ProposalNotRejected
|
||||||
|
);
|
||||||
|
// Rechazada por voto popular: no es un veto.
|
||||||
|
run_to_block(fin_de(viva) + 1);
|
||||||
|
assert_ok!(Governance::finalize_proposal(bob_origin(), viva));
|
||||||
|
assert_noop!(
|
||||||
|
Governance::uphold_council_veto(bob_origin(), viva, 1),
|
||||||
|
Error::<Test>::ProposalNotVetoRejected
|
||||||
|
);
|
||||||
|
// Ventana cerrada.
|
||||||
|
run_to_block(cierre + 1);
|
||||||
|
assert_noop!(
|
||||||
|
Governance::uphold_council_veto(bob_origin(), id, 1),
|
||||||
|
Error::<Test>::OverrideWindowClosed
|
||||||
|
);
|
||||||
|
});
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
// Veto sin ventana registrada: falla cerrado también al sostener.
|
||||||
|
let id = submit_default(alice_origin());
|
||||||
|
let mut p = Proposals::<Test>::get(id).expect("propuesta");
|
||||||
|
p.status = ProposalStatus::Rejected;
|
||||||
|
Proposals::<Test>::insert(id, p);
|
||||||
|
crate::VetoRejected::<Test>::insert(id, ());
|
||||||
|
assert_noop!(
|
||||||
|
Governance::uphold_council_veto(bob_origin(), id, 1),
|
||||||
|
Error::<Test>::OverrideWindowClosed
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Una cuenta vota una vez en la ventana, en un solo lado; el peso de sostener
|
||||||
|
/// también sale del saldo y queda congelado hasta el cierre.
|
||||||
|
#[test]
|
||||||
|
fn una_cuenta_vota_una_vez_en_la_ventana_y_sostener_tambien_congela() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
let (id, cierre) = propuesta_vetada();
|
||||||
|
let saldo = Balances::free_balance(charlie_account());
|
||||||
|
assert_noop!(
|
||||||
|
Governance::uphold_council_veto(charlie_origin(), id, saldo + 1),
|
||||||
|
Error::<Test>::VoteWeightExceedsBalance
|
||||||
|
);
|
||||||
|
assert_ok!(Governance::uphold_council_veto(charlie_origin(), id, 500));
|
||||||
|
assert_eq!(congelado_por_votos(&charlie_account()), 500);
|
||||||
|
assert_eq!(
|
||||||
|
crate::VoteLocks::<Test>::get(charlie_account())[0].until,
|
||||||
|
cierre
|
||||||
|
);
|
||||||
|
System::assert_last_event(RuntimeEvent::Governance(Event::OverrideVoteCast {
|
||||||
|
id,
|
||||||
|
voter: charlie_account(),
|
||||||
|
uphold: true,
|
||||||
|
weight: 500,
|
||||||
|
}));
|
||||||
|
assert_noop!(
|
||||||
|
Governance::override_council_veto(charlie_origin(), id, 1),
|
||||||
|
Error::<Test>::AlreadyOverrideVoted
|
||||||
|
);
|
||||||
|
assert_noop!(
|
||||||
|
Governance::uphold_council_veto(charlie_origin(), id, 1),
|
||||||
|
Error::<Test>::AlreadyOverrideVoted
|
||||||
|
);
|
||||||
|
// Control positivo: otra cuenta sí vota.
|
||||||
|
assert_ok!(Governance::override_council_veto(bob_origin(), id, 1));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// El override se decide una sola vez, y solo sobre un veto.
|
||||||
|
#[test]
|
||||||
|
fn el_override_se_decide_una_sola_vez_y_solo_sobre_un_veto() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
let (id, cierre) = propuesta_vetada();
|
||||||
|
run_to_block(cierre + 1);
|
||||||
|
assert_ok!(Governance::finalize_override(bob_origin(), id));
|
||||||
|
// Decidido: no queda ventana ni quórum guardados (estado limpio, no basura).
|
||||||
|
assert!(crate::OverrideDeadline::<Test>::get(id).is_none());
|
||||||
|
assert!(crate::OverrideQuorum::<Test>::get(id).is_none());
|
||||||
|
assert_noop!(
|
||||||
|
Governance::finalize_override(bob_origin(), id),
|
||||||
|
Error::<Test>::OverrideNotPending
|
||||||
|
);
|
||||||
|
// Una propuesta rechazada por VOTO POPULAR no tiene override que decidir.
|
||||||
|
let otra = propuesta_con_periodo(12, VOTING_PERIOD);
|
||||||
|
assert_ok!(Governance::cast_vote(
|
||||||
|
charlie_origin(),
|
||||||
|
otra,
|
||||||
|
VoteStance::Nay,
|
||||||
|
10
|
||||||
|
));
|
||||||
|
run_to_block(fin_de(otra) + 1);
|
||||||
|
assert_ok!(Governance::finalize_proposal(bob_origin(), otra));
|
||||||
|
assert_noop!(
|
||||||
|
Governance::finalize_override(bob_origin(), otra),
|
||||||
|
Error::<Test>::ProposalNotVetoRejected
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// El veto solo cabe DURANTE el periodo de voto: antes podía llegar entre el fin
|
||||||
|
/// del periodo y el finalize, y ganaba quien llamara primero.
|
||||||
|
#[test]
|
||||||
|
fn el_veto_solo_cabe_durante_el_periodo_de_voto() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
use crate::pallet::CouncilRole;
|
||||||
|
use frame_support::dispatch::RawOrigin;
|
||||||
|
assert_ok!(Governance::add_council_member(
|
||||||
|
RawOrigin::Root.into(),
|
||||||
|
alice_account(),
|
||||||
|
CouncilRole::Chair,
|
||||||
|
));
|
||||||
|
let tarde = submit_default(alice_origin());
|
||||||
|
let a_tiempo = propuesta_con_periodo(12, VOTING_PERIOD);
|
||||||
|
run_to_block(fin_de(tarde) + 1);
|
||||||
|
assert_noop!(
|
||||||
|
Governance::veto_proposal(alice_origin(), tarde),
|
||||||
|
Error::<Test>::VotingPeriodClosed
|
||||||
|
);
|
||||||
|
// Control positivo: en el último bloque de su periodo, sí.
|
||||||
|
run_to_block(fin_de(a_tiempo));
|
||||||
|
assert_ok!(Governance::veto_proposal(alice_origin(), a_tiempo));
|
||||||
|
assert!(matches!(estado(a_tiempo), ProposalStatus::Rejected));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Una propuesta ORDINARIA dura al menos 14 días (PILAR_4 §6.6). La primera versión
|
||||||
|
/// de RV16 ponía 48 h, que es la vía de emergencia sin sus 5/7 endosos: cualquiera la
|
||||||
|
/// obtenía y elegía cuánto tiempo tenía el council para vetar (revisión de RV16).
|
||||||
|
#[test]
|
||||||
|
fn una_propuesta_ordinaria_dura_al_menos_14_dias() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
use crate::pallet::{
|
||||||
|
DEFAULT_VOTING_PERIOD_BLOCKS, EMERGENCY_VOTING_PERIOD_BLOCKS, MIN_VOTING_PERIOD_BLOCKS,
|
||||||
|
};
|
||||||
|
assert_eq!(MIN_VOTING_PERIOD_BLOCKS, DEFAULT_VOTING_PERIOD_BLOCKS);
|
||||||
|
assert_eq!(MIN_VOTING_PERIOD_BLOCKS, 14 * 24 * 600);
|
||||||
|
for corto in [
|
||||||
|
0u64,
|
||||||
|
EMERGENCY_VOTING_PERIOD_BLOCKS,
|
||||||
|
MIN_VOTING_PERIOD_BLOCKS - 1,
|
||||||
|
] {
|
||||||
|
assert_noop!(
|
||||||
|
Governance::submit_proposal(
|
||||||
|
alice_origin(),
|
||||||
|
TITLE_HASH,
|
||||||
|
BODY_HASH,
|
||||||
|
corto,
|
||||||
|
MIN_PROPOSAL_STAKE,
|
||||||
|
),
|
||||||
|
Error::<Test>::VotingPeriodTooShort
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Control positivo: el mínimo exacto se acepta.
|
||||||
|
assert_ok!(Governance::submit_proposal(
|
||||||
|
alice_origin(),
|
||||||
|
TITLE_HASH,
|
||||||
|
BODY_HASH,
|
||||||
|
MIN_VOTING_PERIOD_BLOCKS,
|
||||||
|
MIN_PROPOSAL_STAKE,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
//! procedimiento completo y el hardware al que hay que creerle).
|
//! procedimiento completo y el hardware al que hay que creerle).
|
||||||
//!
|
//!
|
||||||
//! GENERADO POR SUBSTRATE BENCHMARK CLI VERSION 51.0.0
|
//! GENERADO POR SUBSTRATE BENCHMARK CLI VERSION 51.0.0
|
||||||
//! FECHA: 2026-09-24, STEPS: `50`, REPEAT: `20`
|
//! FECHA: 2026-10-07, STEPS: `50`, REPEAT: `20`
|
||||||
//! HOSTNAME: `MacBookProM3Max.local`, CPU: `<UNKNOWN>`
|
//! HOSTNAME: `MacBookProM3Max.local`, CPU: `<UNKNOWN>`
|
||||||
//! WASM-EXECUTION: `Compiled`, DB CACHE: 1024
|
//! WASM-EXECUTION: `Compiled`, DB CACHE: 1024
|
||||||
|
|
||||||
@@ -80,6 +80,10 @@ pub trait WeightInfo {
|
|||||||
fn endorse_emergency_proposal() -> Weight;
|
fn endorse_emergency_proposal() -> Weight;
|
||||||
/// Peso de `unlock_vote_funds` — MEDIDO sobre el WASM, no estimado a mano.
|
/// Peso de `unlock_vote_funds` — MEDIDO sobre el WASM, no estimado a mano.
|
||||||
fn unlock_vote_funds() -> Weight;
|
fn unlock_vote_funds() -> Weight;
|
||||||
|
/// Peso de `uphold_council_veto` — MEDIDO sobre el WASM, no estimado a mano.
|
||||||
|
fn uphold_council_veto() -> Weight;
|
||||||
|
/// Peso de `finalize_override` — MEDIDO sobre el WASM, no estimado a mano.
|
||||||
|
fn finalize_override() -> Weight;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Weights parametrizados por el `DbWeight` real del runtime.
|
/// Weights parametrizados por el `DbWeight` real del runtime.
|
||||||
@@ -105,8 +109,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
|||||||
// Proof size en bytes:
|
// Proof size en bytes:
|
||||||
// Medido: `107`
|
// Medido: `107`
|
||||||
// Estimado: `3722`
|
// Estimado: `3722`
|
||||||
// Tiempo minimo de ejecucion: 54_000_000 picosegundos.
|
// Tiempo minimo de ejecucion: 44_000_000 picosegundos.
|
||||||
Weight::from_parts(55_000_000, 0)
|
Weight::from_parts(45_000_000, 0)
|
||||||
.saturating_add(Weight::from_parts(0, 3722))
|
.saturating_add(Weight::from_parts(0, 3722))
|
||||||
.saturating_add(T::DbWeight::get().reads(7_u64))
|
.saturating_add(T::DbWeight::get().reads(7_u64))
|
||||||
.saturating_add(T::DbWeight::get().writes(5_u64))
|
.saturating_add(T::DbWeight::get().writes(5_u64))
|
||||||
@@ -135,8 +139,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
|||||||
// Proof size en bytes:
|
// Proof size en bytes:
|
||||||
// Medido: `2320`
|
// Medido: `2320`
|
||||||
// Estimado: `5338`
|
// Estimado: `5338`
|
||||||
// Tiempo minimo de ejecucion: 46_000_000 picosegundos.
|
// Tiempo minimo de ejecucion: 38_000_000 picosegundos.
|
||||||
Weight::from_parts(48_000_000, 0)
|
Weight::from_parts(42_000_000, 0)
|
||||||
.saturating_add(Weight::from_parts(0, 5338))
|
.saturating_add(Weight::from_parts(0, 5338))
|
||||||
.saturating_add(T::DbWeight::get().reads(10_u64))
|
.saturating_add(T::DbWeight::get().reads(10_u64))
|
||||||
.saturating_add(T::DbWeight::get().writes(6_u64))
|
.saturating_add(T::DbWeight::get().writes(6_u64))
|
||||||
@@ -161,8 +165,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
|||||||
// Proof size en bytes:
|
// Proof size en bytes:
|
||||||
// Medido: `614`
|
// Medido: `614`
|
||||||
// Estimado: `6196`
|
// Estimado: `6196`
|
||||||
// Tiempo minimo de ejecucion: 61_000_000 picosegundos.
|
// Tiempo minimo de ejecucion: 52_000_000 picosegundos.
|
||||||
Weight::from_parts(62_000_000, 0)
|
Weight::from_parts(59_000_000, 0)
|
||||||
.saturating_add(Weight::from_parts(0, 6196))
|
.saturating_add(Weight::from_parts(0, 6196))
|
||||||
.saturating_add(T::DbWeight::get().reads(9_u64))
|
.saturating_add(T::DbWeight::get().reads(9_u64))
|
||||||
.saturating_add(T::DbWeight::get().writes(6_u64))
|
.saturating_add(T::DbWeight::get().writes(6_u64))
|
||||||
@@ -183,8 +187,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
|||||||
// Proof size en bytes:
|
// Proof size en bytes:
|
||||||
// Medido: `25`
|
// Medido: `25`
|
||||||
// Estimado: `3554`
|
// Estimado: `3554`
|
||||||
// Tiempo minimo de ejecucion: 12_000_000 picosegundos.
|
// Tiempo minimo de ejecucion: 10_000_000 picosegundos.
|
||||||
Weight::from_parts(12_000_000, 0)
|
Weight::from_parts(10_000_000, 0)
|
||||||
.saturating_add(Weight::from_parts(0, 3554))
|
.saturating_add(Weight::from_parts(0, 3554))
|
||||||
.saturating_add(T::DbWeight::get().reads(6_u64))
|
.saturating_add(T::DbWeight::get().reads(6_u64))
|
||||||
.saturating_add(T::DbWeight::get().writes(4_u64))
|
.saturating_add(T::DbWeight::get().writes(4_u64))
|
||||||
@@ -205,8 +209,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
|||||||
// Proof size en bytes:
|
// Proof size en bytes:
|
||||||
// Medido: `153`
|
// Medido: `153`
|
||||||
// Estimado: `3554`
|
// Estimado: `3554`
|
||||||
// Tiempo minimo de ejecucion: 13_000_000 picosegundos.
|
// Tiempo minimo de ejecucion: 11_000_000 picosegundos.
|
||||||
Weight::from_parts(14_000_000, 0)
|
Weight::from_parts(12_000_000, 0)
|
||||||
.saturating_add(Weight::from_parts(0, 3554))
|
.saturating_add(Weight::from_parts(0, 3554))
|
||||||
.saturating_add(T::DbWeight::get().reads(6_u64))
|
.saturating_add(T::DbWeight::get().reads(6_u64))
|
||||||
.saturating_add(T::DbWeight::get().writes(4_u64))
|
.saturating_add(T::DbWeight::get().writes(4_u64))
|
||||||
@@ -215,12 +219,12 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
|||||||
/// Proof: `Governance::Council` (`max_values`: None, `max_size`: Some(89), added: 2564, mode: `MaxEncodedLen`)
|
/// Proof: `Governance::Council` (`max_values`: None, `max_size`: Some(89), added: 2564, mode: `MaxEncodedLen`)
|
||||||
/// Storage: `Governance::Proposals` (r:1 w:1)
|
/// Storage: `Governance::Proposals` (r:1 w:1)
|
||||||
/// Proof: `Governance::Proposals` (`max_values`: None, `max_size`: Some(257), added: 2732, mode: `MaxEncodedLen`)
|
/// Proof: `Governance::Proposals` (`max_values`: None, `max_size`: Some(257), added: 2732, mode: `MaxEncodedLen`)
|
||||||
|
/// Storage: `System::Number` (r:1 w:0)
|
||||||
|
/// Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
|
||||||
/// Storage: `Governance::CouncilSize` (r:1 w:0)
|
/// Storage: `Governance::CouncilSize` (r:1 w:0)
|
||||||
/// Proof: `Governance::CouncilSize` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
|
/// Proof: `Governance::CouncilSize` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
|
||||||
/// Storage: `Governance::VetoVotes` (r:6 w:1)
|
/// Storage: `Governance::VetoVotes` (r:6 w:1)
|
||||||
/// Proof: `Governance::VetoVotes` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `MaxEncodedLen`)
|
/// Proof: `Governance::VetoVotes` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `MaxEncodedLen`)
|
||||||
/// Storage: `System::Number` (r:1 w:0)
|
|
||||||
/// Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
|
|
||||||
/// Storage: `Governance::ProposalBond` (r:1 w:1)
|
/// Storage: `Governance::ProposalBond` (r:1 w:1)
|
||||||
/// Proof: `Governance::ProposalBond` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `MaxEncodedLen`)
|
/// Proof: `Governance::ProposalBond` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `MaxEncodedLen`)
|
||||||
/// Storage: `Balances::TotalIssuance` (r:1 w:0)
|
/// Storage: `Balances::TotalIssuance` (r:1 w:0)
|
||||||
@@ -235,6 +239,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
|||||||
/// Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
|
/// Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
|
||||||
/// Storage: `Governance::OverrideDeadline` (r:0 w:1)
|
/// Storage: `Governance::OverrideDeadline` (r:0 w:1)
|
||||||
/// Proof: `Governance::OverrideDeadline` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`)
|
/// Proof: `Governance::OverrideDeadline` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`)
|
||||||
|
/// Storage: `Governance::OverrideQuorum` (r:0 w:1)
|
||||||
|
/// Proof: `Governance::OverrideQuorum` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`)
|
||||||
/// Storage: `Governance::VetoRejected` (r:0 w:1)
|
/// Storage: `Governance::VetoRejected` (r:0 w:1)
|
||||||
/// Proof: `Governance::VetoRejected` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`)
|
/// Proof: `Governance::VetoRejected` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`)
|
||||||
/// Storage: `Governance::VetoCount` (r:0 w:1)
|
/// Storage: `Governance::VetoCount` (r:0 w:1)
|
||||||
@@ -243,11 +249,11 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
|||||||
// Proof size en bytes:
|
// Proof size en bytes:
|
||||||
// Medido: `1440`
|
// Medido: `1440`
|
||||||
// Estimado: `16416`
|
// Estimado: `16416`
|
||||||
// Tiempo minimo de ejecucion: 109_000_000 picosegundos.
|
// Tiempo minimo de ejecucion: 91_000_000 picosegundos.
|
||||||
Weight::from_parts(112_000_000, 0)
|
Weight::from_parts(95_000_000, 0)
|
||||||
.saturating_add(Weight::from_parts(0, 16416))
|
.saturating_add(Weight::from_parts(0, 16416))
|
||||||
.saturating_add(T::DbWeight::get().reads(21_u64))
|
.saturating_add(T::DbWeight::get().reads(21_u64))
|
||||||
.saturating_add(T::DbWeight::get().writes(10_u64))
|
.saturating_add(T::DbWeight::get().writes(11_u64))
|
||||||
}
|
}
|
||||||
/// Storage: `Governance::AdvisorFlags` (r:1 w:1)
|
/// Storage: `Governance::AdvisorFlags` (r:1 w:1)
|
||||||
/// Proof: `Governance::AdvisorFlags` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`)
|
/// Proof: `Governance::AdvisorFlags` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`)
|
||||||
@@ -263,8 +269,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
|||||||
// Proof size en bytes:
|
// Proof size en bytes:
|
||||||
// Medido: `25`
|
// Medido: `25`
|
||||||
// Estimado: `3513`
|
// Estimado: `3513`
|
||||||
// Tiempo minimo de ejecucion: 9_000_000 picosegundos.
|
// Tiempo minimo de ejecucion: 8_000_000 picosegundos.
|
||||||
Weight::from_parts(10_000_000, 0)
|
Weight::from_parts(9_000_000, 0)
|
||||||
.saturating_add(Weight::from_parts(0, 3513))
|
.saturating_add(Weight::from_parts(0, 3513))
|
||||||
.saturating_add(T::DbWeight::get().reads(5_u64))
|
.saturating_add(T::DbWeight::get().reads(5_u64))
|
||||||
.saturating_add(T::DbWeight::get().writes(3_u64))
|
.saturating_add(T::DbWeight::get().writes(3_u64))
|
||||||
@@ -283,13 +289,13 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
|||||||
// Proof size en bytes:
|
// Proof size en bytes:
|
||||||
// Medido: `98`
|
// Medido: `98`
|
||||||
// Estimado: `3513`
|
// Estimado: `3513`
|
||||||
// Tiempo minimo de ejecucion: 11_000_000 picosegundos.
|
// Tiempo minimo de ejecucion: 10_000_000 picosegundos.
|
||||||
Weight::from_parts(11_000_000, 0)
|
Weight::from_parts(10_000_000, 0)
|
||||||
.saturating_add(Weight::from_parts(0, 3513))
|
.saturating_add(Weight::from_parts(0, 3513))
|
||||||
.saturating_add(T::DbWeight::get().reads(5_u64))
|
.saturating_add(T::DbWeight::get().reads(5_u64))
|
||||||
.saturating_add(T::DbWeight::get().writes(3_u64))
|
.saturating_add(T::DbWeight::get().writes(3_u64))
|
||||||
}
|
}
|
||||||
/// Storage: `Governance::Proposals` (r:1 w:1)
|
/// Storage: `Governance::Proposals` (r:1 w:0)
|
||||||
/// Proof: `Governance::Proposals` (`max_values`: None, `max_size`: Some(257), added: 2732, mode: `MaxEncodedLen`)
|
/// Proof: `Governance::Proposals` (`max_values`: None, `max_size`: Some(257), added: 2732, mode: `MaxEncodedLen`)
|
||||||
/// Storage: `Governance::VetoRejected` (r:1 w:0)
|
/// Storage: `Governance::VetoRejected` (r:1 w:0)
|
||||||
/// Proof: `Governance::VetoRejected` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`)
|
/// Proof: `Governance::VetoRejected` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`)
|
||||||
@@ -297,6 +303,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
|||||||
/// Proof: `Governance::AdvisorFlags` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`)
|
/// Proof: `Governance::AdvisorFlags` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`)
|
||||||
/// Storage: `Governance::OverrideVotes` (r:1 w:1)
|
/// Storage: `Governance::OverrideVotes` (r:1 w:1)
|
||||||
/// Proof: `Governance::OverrideVotes` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`)
|
/// Proof: `Governance::OverrideVotes` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`)
|
||||||
|
/// Storage: `Governance::UpholdVotes` (r:1 w:0)
|
||||||
|
/// Proof: `Governance::UpholdVotes` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`)
|
||||||
/// Storage: `Governance::OverrideDeadline` (r:1 w:0)
|
/// Storage: `Governance::OverrideDeadline` (r:1 w:0)
|
||||||
/// Proof: `Governance::OverrideDeadline` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`)
|
/// Proof: `Governance::OverrideDeadline` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`)
|
||||||
/// Storage: `System::Number` (r:1 w:0)
|
/// Storage: `System::Number` (r:1 w:0)
|
||||||
@@ -319,11 +327,11 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
|||||||
// Proof size en bytes:
|
// Proof size en bytes:
|
||||||
// Medido: `2468`
|
// Medido: `2468`
|
||||||
// Estimado: `5338`
|
// Estimado: `5338`
|
||||||
// Tiempo minimo de ejecucion: 56_000_000 picosegundos.
|
// Tiempo minimo de ejecucion: 47_000_000 picosegundos.
|
||||||
Weight::from_parts(58_000_000, 0)
|
Weight::from_parts(49_000_000, 0)
|
||||||
.saturating_add(Weight::from_parts(0, 5338))
|
.saturating_add(Weight::from_parts(0, 5338))
|
||||||
.saturating_add(T::DbWeight::get().reads(13_u64))
|
.saturating_add(T::DbWeight::get().reads(14_u64))
|
||||||
.saturating_add(T::DbWeight::get().writes(7_u64))
|
.saturating_add(T::DbWeight::get().writes(6_u64))
|
||||||
}
|
}
|
||||||
/// Storage: `Governance::Council` (r:1 w:0)
|
/// Storage: `Governance::Council` (r:1 w:0)
|
||||||
/// Proof: `Governance::Council` (`max_values`: None, `max_size`: Some(89), added: 2564, mode: `MaxEncodedLen`)
|
/// Proof: `Governance::Council` (`max_values`: None, `max_size`: Some(89), added: 2564, mode: `MaxEncodedLen`)
|
||||||
@@ -351,8 +359,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
|||||||
// Proof size en bytes:
|
// Proof size en bytes:
|
||||||
// Medido: `353`
|
// Medido: `353`
|
||||||
// Estimado: `6132`
|
// Estimado: `6132`
|
||||||
// Tiempo minimo de ejecucion: 68_000_000 picosegundos.
|
// Tiempo minimo de ejecucion: 58_000_000 picosegundos.
|
||||||
Weight::from_parts(69_000_000, 0)
|
Weight::from_parts(60_000_000, 0)
|
||||||
.saturating_add(Weight::from_parts(0, 6132))
|
.saturating_add(Weight::from_parts(0, 6132))
|
||||||
.saturating_add(T::DbWeight::get().reads(11_u64))
|
.saturating_add(T::DbWeight::get().reads(11_u64))
|
||||||
.saturating_add(T::DbWeight::get().writes(6_u64))
|
.saturating_add(T::DbWeight::get().writes(6_u64))
|
||||||
@@ -377,8 +385,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
|||||||
// Proof size en bytes:
|
// Proof size en bytes:
|
||||||
// Medido: `494`
|
// Medido: `494`
|
||||||
// Estimado: `6196`
|
// Estimado: `6196`
|
||||||
// Tiempo minimo de ejecucion: 56_000_000 picosegundos.
|
// Tiempo minimo de ejecucion: 48_000_000 picosegundos.
|
||||||
Weight::from_parts(58_000_000, 0)
|
Weight::from_parts(49_000_000, 0)
|
||||||
.saturating_add(Weight::from_parts(0, 6196))
|
.saturating_add(Weight::from_parts(0, 6196))
|
||||||
.saturating_add(T::DbWeight::get().reads(9_u64))
|
.saturating_add(T::DbWeight::get().reads(9_u64))
|
||||||
.saturating_add(T::DbWeight::get().writes(5_u64))
|
.saturating_add(T::DbWeight::get().writes(5_u64))
|
||||||
@@ -399,8 +407,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
|||||||
// Proof size en bytes:
|
// Proof size en bytes:
|
||||||
// Medido: `101`
|
// Medido: `101`
|
||||||
// Estimado: `3593`
|
// Estimado: `3593`
|
||||||
// Tiempo minimo de ejecucion: 43_000_000 picosegundos.
|
// Tiempo minimo de ejecucion: 37_000_000 picosegundos.
|
||||||
Weight::from_parts(43_000_000, 0)
|
Weight::from_parts(38_000_000, 0)
|
||||||
.saturating_add(Weight::from_parts(0, 3593))
|
.saturating_add(Weight::from_parts(0, 3593))
|
||||||
.saturating_add(T::DbWeight::get().reads(6_u64))
|
.saturating_add(T::DbWeight::get().reads(6_u64))
|
||||||
.saturating_add(T::DbWeight::get().writes(3_u64))
|
.saturating_add(T::DbWeight::get().writes(3_u64))
|
||||||
@@ -423,8 +431,8 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
|||||||
// Proof size en bytes:
|
// Proof size en bytes:
|
||||||
// Medido: `153`
|
// Medido: `153`
|
||||||
// Estimado: `6132`
|
// Estimado: `6132`
|
||||||
// Tiempo minimo de ejecucion: 22_000_000 picosegundos.
|
// Tiempo minimo de ejecucion: 19_000_000 picosegundos.
|
||||||
Weight::from_parts(23_000_000, 0)
|
Weight::from_parts(20_000_000, 0)
|
||||||
.saturating_add(Weight::from_parts(0, 6132))
|
.saturating_add(Weight::from_parts(0, 6132))
|
||||||
.saturating_add(T::DbWeight::get().reads(8_u64))
|
.saturating_add(T::DbWeight::get().reads(8_u64))
|
||||||
.saturating_add(T::DbWeight::get().writes(3_u64))
|
.saturating_add(T::DbWeight::get().writes(3_u64))
|
||||||
@@ -449,100 +457,180 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> {
|
|||||||
// Proof size en bytes:
|
// Proof size en bytes:
|
||||||
// Medido: `2197`
|
// Medido: `2197`
|
||||||
// Estimado: `5338`
|
// Estimado: `5338`
|
||||||
// Tiempo minimo de ejecucion: 34_000_000 picosegundos.
|
// Tiempo minimo de ejecucion: 29_000_000 picosegundos.
|
||||||
Weight::from_parts(40_000_000, 0)
|
Weight::from_parts(30_000_000, 0)
|
||||||
.saturating_add(Weight::from_parts(0, 5338))
|
.saturating_add(Weight::from_parts(0, 5338))
|
||||||
.saturating_add(T::DbWeight::get().reads(8_u64))
|
.saturating_add(T::DbWeight::get().reads(8_u64))
|
||||||
.saturating_add(T::DbWeight::get().writes(5_u64))
|
.saturating_add(T::DbWeight::get().writes(5_u64))
|
||||||
}
|
}
|
||||||
|
/// Storage: `Governance::Proposals` (r:1 w:0)
|
||||||
|
/// Proof: `Governance::Proposals` (`max_values`: None, `max_size`: Some(257), added: 2732, mode: `MaxEncodedLen`)
|
||||||
|
/// Storage: `Governance::VetoRejected` (r:1 w:0)
|
||||||
|
/// Proof: `Governance::VetoRejected` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`)
|
||||||
|
/// Storage: `Governance::AdvisorFlags` (r:1 w:0)
|
||||||
|
/// Proof: `Governance::AdvisorFlags` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`)
|
||||||
|
/// Storage: `Governance::OverrideVotes` (r:1 w:0)
|
||||||
|
/// Proof: `Governance::OverrideVotes` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`)
|
||||||
|
/// Storage: `Governance::UpholdVotes` (r:1 w:1)
|
||||||
|
/// Proof: `Governance::UpholdVotes` (`max_values`: None, `max_size`: Some(112), added: 2587, mode: `MaxEncodedLen`)
|
||||||
|
/// Storage: `Governance::OverrideDeadline` (r:1 w:0)
|
||||||
|
/// Proof: `Governance::OverrideDeadline` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`)
|
||||||
|
/// Storage: `System::Number` (r:1 w:0)
|
||||||
|
/// Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
|
||||||
|
/// Storage: `Governance::VoteLocks` (r:1 w:1)
|
||||||
|
/// Proof: `Governance::VoteLocks` (`max_values`: None, `max_size`: Some(1873), added: 4348, mode: `MaxEncodedLen`)
|
||||||
|
/// Storage: `Balances::Freezes` (r:1 w:1)
|
||||||
|
/// Proof: `Balances::Freezes` (`max_values`: None, `max_size`: Some(67), added: 2542, mode: `MaxEncodedLen`)
|
||||||
|
/// Storage: `Balances::Locks` (r:1 w:0)
|
||||||
|
/// Proof: `Balances::Locks` (`max_values`: None, `max_size`: Some(1299), added: 3774, mode: `MaxEncodedLen`)
|
||||||
|
/// Storage: `System::ExecutionPhase` (r:1 w:0)
|
||||||
|
/// Proof: `System::ExecutionPhase` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`)
|
||||||
|
/// Storage: `System::EventCount` (r:1 w:1)
|
||||||
|
/// Proof: `System::EventCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
|
||||||
|
/// Storage: `System::Events` (r:1 w:1)
|
||||||
|
/// Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
|
||||||
|
/// Storage: `Governance::UpholdStakeTotal` (r:1 w:1)
|
||||||
|
/// Proof: `Governance::UpholdStakeTotal` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`)
|
||||||
|
fn uphold_council_veto() -> Weight {
|
||||||
|
// Proof size en bytes:
|
||||||
|
// Medido: `2468`
|
||||||
|
// Estimado: `5338`
|
||||||
|
// Tiempo minimo de ejecucion: 47_000_000 picosegundos.
|
||||||
|
Weight::from_parts(50_000_000, 0)
|
||||||
|
.saturating_add(Weight::from_parts(0, 5338))
|
||||||
|
.saturating_add(T::DbWeight::get().reads(14_u64))
|
||||||
|
.saturating_add(T::DbWeight::get().writes(6_u64))
|
||||||
|
}
|
||||||
|
/// Storage: `Governance::Proposals` (r:1 w:1)
|
||||||
|
/// Proof: `Governance::Proposals` (`max_values`: None, `max_size`: Some(257), added: 2732, mode: `MaxEncodedLen`)
|
||||||
|
/// Storage: `Governance::VetoRejected` (r:1 w:0)
|
||||||
|
/// Proof: `Governance::VetoRejected` (`max_values`: None, `max_size`: Some(48), added: 2523, mode: `MaxEncodedLen`)
|
||||||
|
/// Storage: `Governance::OverrideDeadline` (r:1 w:1)
|
||||||
|
/// Proof: `Governance::OverrideDeadline` (`max_values`: None, `max_size`: Some(56), added: 2531, mode: `MaxEncodedLen`)
|
||||||
|
/// Storage: `System::Number` (r:1 w:0)
|
||||||
|
/// Proof: `System::Number` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
|
||||||
|
/// Storage: `Governance::OverrideStakeTotal` (r:1 w:0)
|
||||||
|
/// Proof: `Governance::OverrideStakeTotal` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`)
|
||||||
|
/// Storage: `Governance::UpholdStakeTotal` (r:1 w:0)
|
||||||
|
/// Proof: `Governance::UpholdStakeTotal` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`)
|
||||||
|
/// Storage: `Governance::OverrideQuorum` (r:1 w:1)
|
||||||
|
/// Proof: `Governance::OverrideQuorum` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`)
|
||||||
|
/// Storage: `System::ExecutionPhase` (r:1 w:0)
|
||||||
|
/// Proof: `System::ExecutionPhase` (`max_values`: Some(1), `max_size`: Some(5), added: 500, mode: `MaxEncodedLen`)
|
||||||
|
/// Storage: `System::EventCount` (r:1 w:1)
|
||||||
|
/// Proof: `System::EventCount` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
|
||||||
|
/// Storage: `System::Events` (r:1 w:1)
|
||||||
|
/// Proof: `System::Events` (`max_values`: Some(1), `max_size`: None, mode: `Measured`)
|
||||||
|
fn finalize_override() -> Weight {
|
||||||
|
// Proof size en bytes:
|
||||||
|
// Medido: `636`
|
||||||
|
// Estimado: `3722`
|
||||||
|
// Tiempo minimo de ejecucion: 25_000_000 picosegundos.
|
||||||
|
Weight::from_parts(26_000_000, 0)
|
||||||
|
.saturating_add(Weight::from_parts(0, 3722))
|
||||||
|
.saturating_add(T::DbWeight::get().reads(10_u64))
|
||||||
|
.saturating_add(T::DbWeight::get().writes(5_u64))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Impl para mocks y tests — `RocksDbWeight` directo, sin necesitar `T`.
|
/// Impl para mocks y tests — `RocksDbWeight` directo, sin necesitar `T`.
|
||||||
impl WeightInfo for () {
|
impl WeightInfo for () {
|
||||||
fn submit_proposal() -> Weight {
|
fn submit_proposal() -> Weight {
|
||||||
Weight::from_parts(55_000_000, 0)
|
Weight::from_parts(45_000_000, 0)
|
||||||
.saturating_add(Weight::from_parts(0, 3722))
|
.saturating_add(Weight::from_parts(0, 3722))
|
||||||
.saturating_add(RocksDbWeight::get().reads(7_u64))
|
.saturating_add(RocksDbWeight::get().reads(7_u64))
|
||||||
.saturating_add(RocksDbWeight::get().writes(5_u64))
|
.saturating_add(RocksDbWeight::get().writes(5_u64))
|
||||||
}
|
}
|
||||||
fn cast_vote() -> Weight {
|
fn cast_vote() -> Weight {
|
||||||
Weight::from_parts(48_000_000, 0)
|
Weight::from_parts(42_000_000, 0)
|
||||||
.saturating_add(Weight::from_parts(0, 5338))
|
.saturating_add(Weight::from_parts(0, 5338))
|
||||||
.saturating_add(RocksDbWeight::get().reads(10_u64))
|
.saturating_add(RocksDbWeight::get().reads(10_u64))
|
||||||
.saturating_add(RocksDbWeight::get().writes(6_u64))
|
.saturating_add(RocksDbWeight::get().writes(6_u64))
|
||||||
}
|
}
|
||||||
fn finalize_proposal() -> Weight {
|
fn finalize_proposal() -> Weight {
|
||||||
Weight::from_parts(62_000_000, 0)
|
Weight::from_parts(59_000_000, 0)
|
||||||
.saturating_add(Weight::from_parts(0, 6196))
|
.saturating_add(Weight::from_parts(0, 6196))
|
||||||
.saturating_add(RocksDbWeight::get().reads(9_u64))
|
.saturating_add(RocksDbWeight::get().reads(9_u64))
|
||||||
.saturating_add(RocksDbWeight::get().writes(6_u64))
|
.saturating_add(RocksDbWeight::get().writes(6_u64))
|
||||||
}
|
}
|
||||||
fn add_council_member() -> Weight {
|
fn add_council_member() -> Weight {
|
||||||
Weight::from_parts(12_000_000, 0)
|
Weight::from_parts(10_000_000, 0)
|
||||||
.saturating_add(Weight::from_parts(0, 3554))
|
.saturating_add(Weight::from_parts(0, 3554))
|
||||||
.saturating_add(RocksDbWeight::get().reads(6_u64))
|
.saturating_add(RocksDbWeight::get().reads(6_u64))
|
||||||
.saturating_add(RocksDbWeight::get().writes(4_u64))
|
.saturating_add(RocksDbWeight::get().writes(4_u64))
|
||||||
}
|
}
|
||||||
fn remove_council_member() -> Weight {
|
fn remove_council_member() -> Weight {
|
||||||
Weight::from_parts(14_000_000, 0)
|
Weight::from_parts(12_000_000, 0)
|
||||||
.saturating_add(Weight::from_parts(0, 3554))
|
.saturating_add(Weight::from_parts(0, 3554))
|
||||||
.saturating_add(RocksDbWeight::get().reads(6_u64))
|
.saturating_add(RocksDbWeight::get().reads(6_u64))
|
||||||
.saturating_add(RocksDbWeight::get().writes(4_u64))
|
.saturating_add(RocksDbWeight::get().writes(4_u64))
|
||||||
}
|
}
|
||||||
fn veto_proposal() -> Weight {
|
fn veto_proposal() -> Weight {
|
||||||
Weight::from_parts(112_000_000, 0)
|
Weight::from_parts(95_000_000, 0)
|
||||||
.saturating_add(Weight::from_parts(0, 16416))
|
.saturating_add(Weight::from_parts(0, 16416))
|
||||||
.saturating_add(RocksDbWeight::get().reads(21_u64))
|
.saturating_add(RocksDbWeight::get().reads(21_u64))
|
||||||
.saturating_add(RocksDbWeight::get().writes(10_u64))
|
.saturating_add(RocksDbWeight::get().writes(11_u64))
|
||||||
}
|
}
|
||||||
fn mark_as_advisor() -> Weight {
|
fn mark_as_advisor() -> Weight {
|
||||||
Weight::from_parts(10_000_000, 0)
|
Weight::from_parts(9_000_000, 0)
|
||||||
.saturating_add(Weight::from_parts(0, 3513))
|
.saturating_add(Weight::from_parts(0, 3513))
|
||||||
.saturating_add(RocksDbWeight::get().reads(5_u64))
|
.saturating_add(RocksDbWeight::get().reads(5_u64))
|
||||||
.saturating_add(RocksDbWeight::get().writes(3_u64))
|
.saturating_add(RocksDbWeight::get().writes(3_u64))
|
||||||
}
|
}
|
||||||
fn unmark_as_advisor() -> Weight {
|
fn unmark_as_advisor() -> Weight {
|
||||||
Weight::from_parts(11_000_000, 0)
|
Weight::from_parts(10_000_000, 0)
|
||||||
.saturating_add(Weight::from_parts(0, 3513))
|
.saturating_add(Weight::from_parts(0, 3513))
|
||||||
.saturating_add(RocksDbWeight::get().reads(5_u64))
|
.saturating_add(RocksDbWeight::get().reads(5_u64))
|
||||||
.saturating_add(RocksDbWeight::get().writes(3_u64))
|
.saturating_add(RocksDbWeight::get().writes(3_u64))
|
||||||
}
|
}
|
||||||
fn override_council_veto() -> Weight {
|
fn override_council_veto() -> Weight {
|
||||||
Weight::from_parts(58_000_000, 0)
|
Weight::from_parts(49_000_000, 0)
|
||||||
.saturating_add(Weight::from_parts(0, 5338))
|
.saturating_add(Weight::from_parts(0, 5338))
|
||||||
.saturating_add(RocksDbWeight::get().reads(13_u64))
|
.saturating_add(RocksDbWeight::get().reads(14_u64))
|
||||||
.saturating_add(RocksDbWeight::get().writes(7_u64))
|
.saturating_add(RocksDbWeight::get().writes(6_u64))
|
||||||
}
|
}
|
||||||
fn submit_emergency_proposal() -> Weight {
|
fn submit_emergency_proposal() -> Weight {
|
||||||
Weight::from_parts(69_000_000, 0)
|
Weight::from_parts(60_000_000, 0)
|
||||||
.saturating_add(Weight::from_parts(0, 6132))
|
.saturating_add(Weight::from_parts(0, 6132))
|
||||||
.saturating_add(RocksDbWeight::get().reads(11_u64))
|
.saturating_add(RocksDbWeight::get().reads(11_u64))
|
||||||
.saturating_add(RocksDbWeight::get().writes(6_u64))
|
.saturating_add(RocksDbWeight::get().writes(6_u64))
|
||||||
}
|
}
|
||||||
fn treasury_disbursement() -> Weight {
|
fn treasury_disbursement() -> Weight {
|
||||||
Weight::from_parts(58_000_000, 0)
|
Weight::from_parts(49_000_000, 0)
|
||||||
.saturating_add(Weight::from_parts(0, 6196))
|
.saturating_add(Weight::from_parts(0, 6196))
|
||||||
.saturating_add(RocksDbWeight::get().reads(9_u64))
|
.saturating_add(RocksDbWeight::get().reads(9_u64))
|
||||||
.saturating_add(RocksDbWeight::get().writes(5_u64))
|
.saturating_add(RocksDbWeight::get().writes(5_u64))
|
||||||
}
|
}
|
||||||
fn treasury_deposit() -> Weight {
|
fn treasury_deposit() -> Weight {
|
||||||
Weight::from_parts(43_000_000, 0)
|
Weight::from_parts(38_000_000, 0)
|
||||||
.saturating_add(Weight::from_parts(0, 3593))
|
.saturating_add(Weight::from_parts(0, 3593))
|
||||||
.saturating_add(RocksDbWeight::get().reads(6_u64))
|
.saturating_add(RocksDbWeight::get().reads(6_u64))
|
||||||
.saturating_add(RocksDbWeight::get().writes(3_u64))
|
.saturating_add(RocksDbWeight::get().writes(3_u64))
|
||||||
}
|
}
|
||||||
fn endorse_emergency_proposal() -> Weight {
|
fn endorse_emergency_proposal() -> Weight {
|
||||||
Weight::from_parts(23_000_000, 0)
|
Weight::from_parts(20_000_000, 0)
|
||||||
.saturating_add(Weight::from_parts(0, 6132))
|
.saturating_add(Weight::from_parts(0, 6132))
|
||||||
.saturating_add(RocksDbWeight::get().reads(8_u64))
|
.saturating_add(RocksDbWeight::get().reads(8_u64))
|
||||||
.saturating_add(RocksDbWeight::get().writes(3_u64))
|
.saturating_add(RocksDbWeight::get().writes(3_u64))
|
||||||
}
|
}
|
||||||
fn unlock_vote_funds() -> Weight {
|
fn unlock_vote_funds() -> Weight {
|
||||||
Weight::from_parts(40_000_000, 0)
|
Weight::from_parts(30_000_000, 0)
|
||||||
.saturating_add(Weight::from_parts(0, 5338))
|
.saturating_add(Weight::from_parts(0, 5338))
|
||||||
.saturating_add(RocksDbWeight::get().reads(8_u64))
|
.saturating_add(RocksDbWeight::get().reads(8_u64))
|
||||||
.saturating_add(RocksDbWeight::get().writes(5_u64))
|
.saturating_add(RocksDbWeight::get().writes(5_u64))
|
||||||
}
|
}
|
||||||
|
fn uphold_council_veto() -> Weight {
|
||||||
|
Weight::from_parts(50_000_000, 0)
|
||||||
|
.saturating_add(Weight::from_parts(0, 5338))
|
||||||
|
.saturating_add(RocksDbWeight::get().reads(14_u64))
|
||||||
|
.saturating_add(RocksDbWeight::get().writes(6_u64))
|
||||||
|
}
|
||||||
|
fn finalize_override() -> Weight {
|
||||||
|
Weight::from_parts(26_000_000, 0)
|
||||||
|
.saturating_add(Weight::from_parts(0, 3722))
|
||||||
|
.saturating_add(RocksDbWeight::get().reads(10_u64))
|
||||||
|
.saturating_add(RocksDbWeight::get().writes(5_u64))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -584,6 +672,8 @@ mod guard {
|
|||||||
("treasury_deposit", <() as WeightInfo>::treasury_deposit()),
|
("treasury_deposit", <() as WeightInfo>::treasury_deposit()),
|
||||||
("endorse_emergency_proposal", <() as WeightInfo>::endorse_emergency_proposal()),
|
("endorse_emergency_proposal", <() as WeightInfo>::endorse_emergency_proposal()),
|
||||||
("unlock_vote_funds", <() as WeightInfo>::unlock_vote_funds()),
|
("unlock_vote_funds", <() as WeightInfo>::unlock_vote_funds()),
|
||||||
|
("uphold_council_veto", <() as WeightInfo>::uphold_council_veto()),
|
||||||
|
("finalize_override", <() as WeightInfo>::finalize_override()),
|
||||||
];
|
];
|
||||||
for (nombre, w) in casos {
|
for (nombre, w) in casos {
|
||||||
assert!(
|
assert!(
|
||||||
|
|||||||
@@ -536,6 +536,11 @@ impl origin_chain_governance::Config for Runtime {
|
|||||||
type RuntimeFreezeReason = RuntimeFreezeReason;
|
type RuntimeFreezeReason = RuntimeFreezeReason;
|
||||||
// RV15: votos VIVOS simultáneos por cuenta (los vencidos se descartan solos).
|
// RV15: votos VIVOS simultáneos por cuenta (los vencidos se descartan solos).
|
||||||
type MaxActiveVotes = frame_support::traits::ConstU32<32>;
|
type MaxActiveVotes = frame_support::traits::ConstU32<32>;
|
||||||
|
// RV16: participación mínima para revertir un veto del council: 100M OGC, el 10%
|
||||||
|
// del suministro que ratifica PILAR_4 §6.6 como quórum («10% quorum»). Es un
|
||||||
|
// parámetro de la constitución del DAO y lo fija David; mientras, este. Lo pinna
|
||||||
|
// `quorum_de_override_del_runtime` (genesis_config_presets.rs).
|
||||||
|
type MinOverrideQuorum = frame_support::traits::ConstU128<{ 100_000_000 * OGC }>;
|
||||||
type TreasuryPalletId = GovernanceTreasuryPalletId;
|
type TreasuryPalletId = GovernanceTreasuryPalletId;
|
||||||
// Weights INTERIM hand-derived DB-aware (ver weights.rs). Regenerable en HW.
|
// Weights INTERIM hand-derived DB-aware (ver weights.rs). Regenerable en HW.
|
||||||
type WeightInfo = origin_chain_governance::weights::SubstrateWeight<Runtime>;
|
type WeightInfo = origin_chain_governance::weights::SubstrateWeight<Runtime>;
|
||||||
|
|||||||
@@ -615,7 +615,7 @@ mod tests {
|
|||||||
use crate::{Balances, Governance, RuntimeFreezeReason, RuntimeOrigin, System};
|
use crate::{Balances, Governance, RuntimeFreezeReason, RuntimeOrigin, System};
|
||||||
use frame_support::assert_ok;
|
use frame_support::assert_ok;
|
||||||
use frame_support::traits::fungible::{Inspect, InspectFreeze};
|
use frame_support::traits::fungible::{Inspect, InspectFreeze};
|
||||||
use origin_chain_governance::{FreezeReason, MIN_PROPOSAL_STAKE};
|
use origin_chain_governance::{FreezeReason, MIN_PROPOSAL_STAKE, MIN_VOTING_PERIOD_BLOCKS};
|
||||||
use origin_chain_types::VoteStance;
|
use origin_chain_types::VoteStance;
|
||||||
|
|
||||||
let json = get_preset(&PresetId::from(PRESET_LOCAL_TESTNET)).expect("preset");
|
let json = get_preset(&PresetId::from(PRESET_LOCAL_TESTNET)).expect("preset");
|
||||||
@@ -628,7 +628,7 @@ mod tests {
|
|||||||
let bob = account_from_sr25519(BOB_SR25519);
|
let bob = account_from_sr25519(BOB_SR25519);
|
||||||
let charlie = account_from_sr25519(CHARLIE_SR25519);
|
let charlie = account_from_sr25519(CHARLIE_SR25519);
|
||||||
|
|
||||||
let (titulo, cuerpo, periodo) = ([1u8; 32], [2u8; 32], 100u64);
|
let (titulo, cuerpo, periodo) = ([1u8; 32], [2u8; 32], MIN_VOTING_PERIOD_BLOCKS);
|
||||||
assert_ok!(Governance::submit_proposal(
|
assert_ok!(Governance::submit_proposal(
|
||||||
RuntimeOrigin::signed(alice.clone()),
|
RuntimeOrigin::signed(alice.clone()),
|
||||||
titulo,
|
titulo,
|
||||||
@@ -676,4 +676,22 @@ mod tests {
|
|||||||
));
|
));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// RV16: el quórum de override del runtime es el 10% del suministro que
|
||||||
|
/// ratifica PILAR_4 §6.6 (100M OGC). Sin este test, un typo como `{ 100_000_000 }`
|
||||||
|
/// sin `* OGC` (10^-10 OGC) o `ConstU128<0>` pasaba toda la suite (revisión de
|
||||||
|
/// RV16). `integrity_test` exige además que supere el bond de una propuesta.
|
||||||
|
#[test]
|
||||||
|
fn quorum_de_override_del_runtime() {
|
||||||
|
use frame_support::traits::Get;
|
||||||
|
let quorum: u128 =
|
||||||
|
<<crate::Runtime as origin_chain_governance::Config>::MinOverrideQuorum as Get<u128>>::get();
|
||||||
|
assert_eq!(quorum, 100_000_000 * crate::configs::OGC);
|
||||||
|
assert_eq!(
|
||||||
|
quorum,
|
||||||
|
crate::configs::OGC * 1_000_000_000 / 10,
|
||||||
|
"10% del suministro"
|
||||||
|
);
|
||||||
|
assert!(quorum > origin_chain_governance::MIN_PROPOSAL_STAKE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1019,7 +1019,7 @@ pub const VERSION: sp_version::RuntimeVersion = sp_version::RuntimeVersion {
|
|||||||
spec_name: alloc::borrow::Cow::Borrowed("origin-chain"),
|
spec_name: alloc::borrow::Cow::Borrowed("origin-chain"),
|
||||||
impl_name: alloc::borrow::Cow::Borrowed("origin-chain-charon"),
|
impl_name: alloc::borrow::Cow::Borrowed("origin-chain-charon"),
|
||||||
authoring_version: 1,
|
authoring_version: 1,
|
||||||
spec_version: 15,
|
spec_version: 16,
|
||||||
impl_version: 1,
|
impl_version: 1,
|
||||||
apis: RUNTIME_API_VERSIONS,
|
apis: RUNTIME_API_VERSIONS,
|
||||||
transaction_version: 1,
|
transaction_version: 1,
|
||||||
@@ -1087,7 +1087,7 @@ mod tests {
|
|||||||
/// mismo commit en que la version subio a 12.
|
/// mismo commit en que la version subio a 12.
|
||||||
#[test]
|
#[test]
|
||||||
fn spec_version_vigente_y_transaction_version_intacta() {
|
fn spec_version_vigente_y_transaction_version_intacta() {
|
||||||
assert_eq!(RUNTIME_VERSION, 15);
|
assert_eq!(RUNTIME_VERSION, 16);
|
||||||
assert_eq!(TRANSACTION_VERSION, 1);
|
assert_eq!(TRANSACTION_VERSION, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user