fix(genesis): float operacional al sudo_key — cierra MEDIUM de la auditoría ultra
La auditoría ultra adversarial del commit 8e72496 cazó un MEDIUM real: al mover los
250M Foundation al vault og/gvtry, el sudo_key (FNDT) quedó con balance 0 — pero el
OriginFeeAdapter cobra tx-fees al firmante, así que el sudo_key (que debe dispatchar
los calls Root del sunset pre-mainnet: set de keys reales, seed del council) no podría
pagar sus propios extrinsics (withdraw_fee falla upfront; Pays::No no rescata, es
post-dispatch). Verificado en fuente por el refutador.
Fix: el 25% Foundation se carva en vault (grueso, DAO-governed) + SUDO_OPERATIONAL_FLOAT
(1M OGC al sudo_key FNDT para fees operacionales). Sub-split de FOUNDATION_ALLOCATION
(NO una 6ª allocation) ⇒ la suma 1B se preserva (const assert + test). 3 tests nuevos:
el sudo_key tiene float (no inoperante) + balances suman 1B + vault recibe el grueso.
Monto del float ratificable por David pre-launch Q1 2027.
271 workspace tests verde, 0 regresión.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
8e72496901
commit
1a0364db99
1 file changed
+64
-4
@@ -71,6 +71,18 @@ pub const FOUNDING_TEAM_ALLOCATION: Balance = 150_000_000_u128 * Ogc::ONE;
|
|||||||
/// **5% Exchange Liquidity canonical** = 50M OGC.
|
/// **5% Exchange Liquidity canonical** = 50M OGC.
|
||||||
pub const EXCHANGE_LIQUIDITY_ALLOCATION: Balance = 50_000_000_u128 * Ogc::ONE;
|
pub const EXCHANGE_LIQUIDITY_ALLOCATION: Balance = 50_000_000_u128 * Ogc::ONE;
|
||||||
|
|
||||||
|
/// Float operacional del sudo_key (Foundation multisig interim) = 1M OGC.
|
||||||
|
///
|
||||||
|
/// **NO es una 6ª allocation** — es un SUB-SPLIT del 25% Foundation
|
||||||
|
/// ([`FOUNDATION_ALLOCATION`]): el grueso va al vault DAO-governed (og/gvtry) y
|
||||||
|
/// este float al sudo_key (FNDT) para que pueda pagar los tx-fees de sus propios
|
||||||
|
/// extrinsics Root durante el interim pre-sunset (set de keys reales, seed del
|
||||||
|
/// council, transición a governance) — el `OriginFeeAdapter` cobra al firmante,
|
||||||
|
/// así que un sudo_key con balance 0 quedaría inoperante desde el bloque 0.
|
||||||
|
/// `vault + float == FOUNDATION_ALLOCATION` ⇒ la suma 1B no cambia. Monto exacto
|
||||||
|
/// ratificado por David pre-launch Q1 2027.
|
||||||
|
pub const SUDO_OPERATIONAL_FLOAT: Balance = 1_000_000_u128 * Ogc::ONE;
|
||||||
|
|
||||||
/// Compile-time assertion canonical: las allocations canonical suman exactamente
|
/// Compile-time assertion canonical: las allocations canonical suman exactamente
|
||||||
/// el genesis supply canonical. Si alguna constante cambia, el code no compila
|
/// el genesis supply canonical. Si alguna constante cambia, el code no compila
|
||||||
/// hasta que las allocations vuelvan a sumar 1B OGC canonical.
|
/// hasta que las allocations vuelvan a sumar 1B OGC canonical.
|
||||||
@@ -84,6 +96,11 @@ const _: () = {
|
|||||||
total == GENESIS_SUPPLY,
|
total == GENESIS_SUPPLY,
|
||||||
"BM V2 allocations canonical must sum a 1B OGC canonical"
|
"BM V2 allocations canonical must sum a 1B OGC canonical"
|
||||||
);
|
);
|
||||||
|
// El float operacional es un sub-split del 25% Foundation, no lo excede.
|
||||||
|
assert!(
|
||||||
|
SUDO_OPERATIONAL_FLOAT < FOUNDATION_ALLOCATION,
|
||||||
|
"el float operacional del sudo_key debe ser un sub-split del 25% Foundation"
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@@ -356,6 +373,11 @@ pub fn mainnet_genesis_config_genesis() -> RuntimeGenesisConfig {
|
|||||||
// SOLO como sudo_key interim (Root) hasta el sunset a governance pre-mainnet.
|
// SOLO como sudo_key interim (Root) hasta el sunset a governance pre-mainnet.
|
||||||
let governance_vault: ChainAccountId =
|
let governance_vault: ChainAccountId =
|
||||||
GovernanceTreasuryPalletId::get().into_account_truncating();
|
GovernanceTreasuryPalletId::get().into_account_truncating();
|
||||||
|
// El 25% Foundation se carva: el grueso al vault DAO-governed, un float
|
||||||
|
// operacional al sudo_key (FNDT) para que pueda pagar los fees de sus propios
|
||||||
|
// extrinsics Root pre-sunset (el OriginFeeAdapter cobra al firmante; un sudo_key
|
||||||
|
// con balance 0 quedaría inoperante). `vault + float == FOUNDATION_ALLOCATION`.
|
||||||
|
let vault_allocation: Balance = FOUNDATION_ALLOCATION - SUDO_OPERATIONAL_FLOAT;
|
||||||
|
|
||||||
// 5 initial validators canonical Pi 5 placeholders (Dream House Studios + universities).
|
// 5 initial validators canonical Pi 5 placeholders (Dream House Studios + universities).
|
||||||
let validators = vec![
|
let validators = vec![
|
||||||
@@ -370,16 +392,18 @@ pub fn mainnet_genesis_config_genesis() -> RuntimeGenesisConfig {
|
|||||||
// BM V2 allocation canonical exact: 40/25/15/15/5 → 1B OGC total
|
// BM V2 allocation canonical exact: 40/25/15/15/5 → 1B OGC total
|
||||||
vec![
|
vec![
|
||||||
(ecosystem_pool, ECOSYSTEM_ALLOCATION),
|
(ecosystem_pool, ECOSYSTEM_ALLOCATION),
|
||||||
(governance_vault, FOUNDATION_ALLOCATION), // 250M → vault og/gvtry (DAO-governed)
|
(governance_vault, vault_allocation), // grueso del 25% → vault og/gvtry (DAO-governed)
|
||||||
|
(foundation_treasury.clone(), SUDO_OPERATIONAL_FLOAT), // float fees del sudo_key (FNDT)
|
||||||
(public_sale, PUBLIC_SALE_ALLOCATION),
|
(public_sale, PUBLIC_SALE_ALLOCATION),
|
||||||
(founding_team, FOUNDING_TEAM_ALLOCATION),
|
(founding_team, FOUNDING_TEAM_ALLOCATION),
|
||||||
(exchange_liquidity, EXCHANGE_LIQUIDITY_ALLOCATION),
|
(exchange_liquidity, EXCHANGE_LIQUIDITY_ALLOCATION),
|
||||||
],
|
],
|
||||||
validators,
|
validators,
|
||||||
// FIX-5: el placeholder FNDT (Foundation multisig humano) = sudo key interim
|
// FIX-5: el placeholder FNDT (Foundation multisig humano) = sudo key interim
|
||||||
// (Root) en mainnet. PRE-LAUNCH Q1 2027: David ratifica la key real + sunset
|
// (Root) en mainnet, con un float operacional para fees. PRE-LAUNCH Q1 2027:
|
||||||
// a governance (pallet-collective/referenda). El treasury (250M) ya NO vive
|
// David ratifica la key real + sunset a governance (DC GOVERNANCE_SUNSET_V1).
|
||||||
// aquí — está en el vault og/gvtry, gastable solo vía governance.
|
// El grueso del treasury (250M − float) vive en el vault og/gvtry, gastable
|
||||||
|
// solo vía governance.
|
||||||
foundation_treasury,
|
foundation_treasury,
|
||||||
// FIX-6 Stage 2a: mainnet arranca SIN perfiles sembrados — todos en el
|
// FIX-6 Stage 2a: mainnet arranca SIN perfiles sembrados — todos en el
|
||||||
// Default conservador (Desktop+Free) hasta que la attestation real
|
// Default conservador (Desktop+Free) hasta que la attestation real
|
||||||
@@ -481,4 +505,40 @@ mod tests {
|
|||||||
// sp_keyring::Sr25519Keyring::Alice.public().0).
|
// sp_keyring::Sr25519Keyring::Alice.public().0).
|
||||||
assert_eq!(ALICE_SR25519[0..4], [0xd4, 0x35, 0x93, 0xc7]);
|
assert_eq!(ALICE_SR25519[0..4], [0xd4, 0x35, 0x93, 0xc7]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mainnet_sudo_key_tiene_float_operacional_para_fees() {
|
||||||
|
// Regresión cerrada (auditoría ultra MEDIUM): al mover los 250M al vault,
|
||||||
|
// el sudo_key (FNDT) NO debe quedar con balance 0 — el OriginFeeAdapter
|
||||||
|
// cobra fees al firmante, y el sudo_key debe poder pagar sus extrinsics
|
||||||
|
// Root pre-sunset. Tiene el float operacional.
|
||||||
|
let cfg = mainnet_genesis_config_genesis();
|
||||||
|
let sudo_key = cfg.sudo.key.expect("mainnet tiene sudo_key");
|
||||||
|
let sudo_balance = cfg
|
||||||
|
.balances
|
||||||
|
.balances
|
||||||
|
.iter()
|
||||||
|
.find(|(acc, _)| *acc == sudo_key)
|
||||||
|
.map(|(_, bal)| *bal)
|
||||||
|
.expect("el sudo_key debe estar endowed (si no, queda inoperante)");
|
||||||
|
assert_eq!(sudo_balance, SUDO_OPERATIONAL_FLOAT);
|
||||||
|
assert!(sudo_balance > 0, "sudo_key con balance 0 = no paga fees = inoperante");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mainnet_balances_suman_genesis_supply_1b() {
|
||||||
|
// El split Foundation (vault + float) no altera la suma total: 1B OGC.
|
||||||
|
let cfg = mainnet_genesis_config_genesis();
|
||||||
|
let total: Balance = cfg.balances.balances.iter().map(|(_, b)| *b).sum();
|
||||||
|
assert_eq!(total, GENESIS_SUPPLY, "el split vault/float preserva la suma 1B");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mainnet_vault_recibe_el_grueso_del_25pct_foundation() {
|
||||||
|
// El grueso del 25% Foundation (250M − float) va al vault og/gvtry, y
|
||||||
|
// vault + float == FOUNDATION_ALLOCATION (cero pérdida del 25%).
|
||||||
|
let vault_allocation = FOUNDATION_ALLOCATION - SUDO_OPERATIONAL_FLOAT;
|
||||||
|
assert_eq!(vault_allocation + SUDO_OPERATIONAL_FLOAT, FOUNDATION_ALLOCATION);
|
||||||
|
assert!(vault_allocation > SUDO_OPERATIONAL_FLOAT, "el grueso va al vault");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user