fix(emission): integrity_test guard — shares autor+treasury ≤ 100% (audit ultra)
Hallazgo MEDIUM del audit ultra: si un cambio de config hiciera AuthorShareBps + TreasuryShareBps > 10_000 bps, el `subsidy = reward.saturating_sub(author). saturating_sub(treasury)` daría subsidy = 0 SILENCIOSAMENTE (el pool #16 se quedaría sin fondos sin ruido). El hook `integrity_test()` lo convierte en un fallo ruidoso del integrity-check del runtime. +test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1fdc47e859
commit
d6dd08da66
1 file changed
+24
-2
@@ -180,9 +180,23 @@ pub mod pallet {
|
|||||||
|
|
||||||
#[pallet::hooks]
|
#[pallet::hooks]
|
||||||
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
|
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
|
||||||
|
/// Guard de integridad (audit ultra 2026-07-02): las shares autor +
|
||||||
|
/// treasury JAMÁS pueden exceder 100% — el residual es el subsidy pool
|
||||||
|
/// (`subsidy = reward − author − treasury`). Si un futuro cambio de
|
||||||
|
/// config las hiciera sumar > 10_000 bps, el `saturating_sub` daría
|
||||||
|
/// subsidy = 0 SILENCIOSAMENTE. Esto lo convierte en un fallo ruidoso
|
||||||
|
/// en el build/integrity-check del runtime.
|
||||||
|
fn integrity_test() {
|
||||||
|
assert!(
|
||||||
|
T::AuthorShareBps::get().saturating_add(T::TreasuryShareBps::get()) <= 10_000,
|
||||||
|
"emisión: AuthorShareBps + TreasuryShareBps no pueden exceder 100% \
|
||||||
|
(el residual es el subsidy pool #16)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Cada bloque: identifica al autor Aura (desde los pre-runtime digests),
|
/// Cada bloque: identifica al autor Aura (desde los pre-runtime digests),
|
||||||
/// mintea el BlockReward y lo reparte. Si no hay autor (e.g. bloque de
|
/// libera el BlockReward del pool Ecosystem y lo reparte. Si no hay autor
|
||||||
/// inherentes sin pre-digest), no mintea.
|
/// (e.g. bloque de inherentes sin pre-digest), no libera.
|
||||||
fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
|
fn on_initialize(_n: BlockNumberFor<T>) -> Weight {
|
||||||
// Kill-switch (governance): si la emisión está pausada, no libera nada.
|
// Kill-switch (governance): si la emisión está pausada, no libera nada.
|
||||||
// Lee 1 storage value; default `false` ⇒ activa (cero regresión).
|
// Lee 1 storage value; default `false` ⇒ activa (cero regresión).
|
||||||
@@ -496,6 +510,14 @@ mod tests {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn integrity_test_shares_no_exceden_100pct() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
// Mock: 7000 + 2000 = 9000 ≤ 10000 ⇒ pasa (el residual 1000 = subsidy).
|
||||||
|
<Emission as Hooks<u64>>::integrity_test();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn depletion_event_se_anuncia_una_sola_vez() {
|
fn depletion_event_se_anuncia_una_sola_vez() {
|
||||||
new_test_ext_with_pool(500).execute_with(|| {
|
new_test_ext_with_pool(500).execute_with(|| {
|
||||||
|
|||||||
Reference in new issue
Block a user