feat(gobierno): spec 18 (RV17), quorum del voto ordinario y pago del tesoro atado a la propuesta
Gobierno (origin-chain-governance): - El voto ordinario exige un quorum de Aye, el 10 % de PILAR_4 §6.6. Se fija AL CREAR la propuesta (VoteQuorumOf) y se consume al decidirla: un set_code no mueve la meta de un voto abierto. Cuenta solo el Aye: con el Nay, un voto en contra podria aprobar; con la abstencion, el mismo OGC inflaria el quorum de muchas propuestas. - Un override del veto tiene que alcanzar ese quorum y superar el Nay original, las dos condiciones del voto ordinario. Sin ellas, vetar abarataba una aprobacion (dos bloqueantes de las revisiones adversariales). - submit_treasury_proposal (call 16) fija destinatario y monto (TreasuryActions), y treasury_disbursement solo paga exactamente eso, una vez. Rechaza montos bajo el deposito existencial y como destinatario la propia tesoreria o la cuenta-bond. - Sin entrada en VoteQuorumOf no hay quorum: la emergencia (que nunca lleva accion de tesoro) y lo creado antes de RV17, que se abrio y voto sin quorum. Runtime: los dos quorums son el 10 % de la emision VIGENTE, redondeado hacia arriba y nunca por debajo del bond (DiezPorCientoDeLaEmision). Con 100M fijos, en la testnet de la casa (100M emitidos, todos de Alice) nada se habria aprobado nunca; en mainnet sigue siendo ~100M. Spec 18, transaction_version 1: todo lo nuevo es aditivo, sin migracion. Node: pasa a opaque::Block. Decodificaba los extrinsics con tipos nativos y rechazaba en el pool la llamada que anadia el set_code (1040, el mismo error que una llamada inexistente). Los bytes y los hashes no cambian: el binario nuevo sincronizo desde cero los 39 273 bloques de la testnet de la casa con los mismos hashes en 8 alturas. El RPC sirve los extrinsics en hex. Pesos: seis extrinsics cambiaron de codigo y se midieron en la Pi 5 (smoke declarado: 5 procesos limpios, sin CI en ningun segundo, maximo de los 5). Los otros once siguen siendo los certificados por Laurha. Se descarto una medida contaminada: su muestreador no veia los tests de la CI. Pruebas: - 363 tests verdes en el workspace. - 24 contrafactuales, los 24 en rojo. - Ensayo en la rp1 sobre una cadena con el codigo vivo de spec 17: upgrade a 18 y E2E firmando como un wallet (scripts/e2e/rv17_tesoro.py). - Ejemplo firmar_como_wallet con tesoro, pagar, votar, depositar y finalizar; los eventos llevan su fase. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
abe680e853
commit
b6d05eaf45
14 files changed
+1368
-155
No files matched your search
@@ -13,10 +13,15 @@
|
||||
//! firmar <llamada> <nonce> <genesis_hex> [spec tx] extrinsic firmado por //Alice, en hex
|
||||
//! llamadas: remark · finalize_override (propuesta 0x09…09) · uphold0 (peso 0)
|
||||
//! set_code:<ruta.wasm> (sudo de //Alice, ensayo de upgrade sin fork)
|
||||
//! tesoro:<monto> (RV17: propuesta de tesoro de //Alice que paga <monto> plancks
|
||||
//! a //Bob; 14 días y el bond mínimo) · pagar:<id_hex>:<monto> (sudo de
|
||||
//! treasury_disbursement a //Bob) · votar:<id_hex>:<peso> (Aye de //Alice)
|
||||
//! · depositar:<monto> (treasury_deposit de //Alice al vault)
|
||||
//! · finalizar:<id_hex> (finalize_proposal)
|
||||
//! spec y tx: los que da `state_getRuntimeVersion`. Por defecto, los de `VERSION`
|
||||
//! compilado. Tras un `set_code` hay que pasarlos: el wallet firma con la versión
|
||||
//! nueva que anuncia el nodo, no con la que se compiló aquí.
|
||||
//! eventos <hex de System::Events> decodifica los eventos de un bloque
|
||||
//! eventos <hex de System::Events> decodifica los eventos de un bloque, con su fase
|
||||
//! cuenta "<frase BIP39>" [//derivación] llave sr25519 y dirección SS58 (42) que verá un wallet
|
||||
//! Substrate (substrate-bip39 → mini-secret → sr25519): vectores para la wallet Origin!
|
||||
//! verificar <hex_extrinsic> <genesis_hex> pasa la firma por el check del runtime nativo
|
||||
@@ -44,6 +49,10 @@ fn hex(arg: &str) -> Vec<u8> {
|
||||
sp_core::bytes::from_hex(arg).unwrap_or_else(|e| panic!("hex inválido {arg}: {e:?}"))
|
||||
}
|
||||
|
||||
fn bob() -> sp_runtime::AccountId32 {
|
||||
sr25519::Pair::from_string("//Bob", None).expect("//Bob").public().into()
|
||||
}
|
||||
|
||||
fn llamada(nombre: &str) -> RuntimeCall {
|
||||
let id = origin_chain_governance::ProposalId([9u8; 32]);
|
||||
match nombre {
|
||||
@@ -59,6 +68,53 @@ fn llamada(nombre: &str) -> RuntimeCall {
|
||||
proposal_id: id,
|
||||
stake_weight: 0,
|
||||
}),
|
||||
otra if otra.starts_with("tesoro:") => {
|
||||
let amount: u128 = otra["tesoro:".len()..].parse().expect("tesoro:<monto en plancks>");
|
||||
RuntimeCall::Governance(origin_chain_governance::Call::submit_treasury_proposal {
|
||||
title_hash: sp_core::blake2_256(b"e2e-rv17/titulo"),
|
||||
body_hash: sp_core::blake2_256(b"e2e-rv17/cuerpo"),
|
||||
voting_period_blocks: origin_chain_governance::MIN_VOTING_PERIOD_BLOCKS,
|
||||
stake: origin_chain_governance::MIN_PROPOSAL_STAKE,
|
||||
recipient: bob(),
|
||||
amount,
|
||||
})
|
||||
}
|
||||
otra if otra.starts_with("votar:") => {
|
||||
let (id, peso) =
|
||||
otra["votar:".len()..].split_once(':').expect("votar:<id_hex>:<peso>");
|
||||
let id: [u8; 32] = hex(id).try_into().expect("el id son 32 bytes");
|
||||
RuntimeCall::Governance(origin_chain_governance::Call::cast_vote {
|
||||
proposal_id: origin_chain_governance::ProposalId(id),
|
||||
stance: origin_chain_types::VoteStance::Aye,
|
||||
stake_weight: peso.parse().expect("peso en plancks"),
|
||||
})
|
||||
}
|
||||
otra if otra.starts_with("finalizar:") => {
|
||||
let id: [u8; 32] =
|
||||
hex(&otra["finalizar:".len()..]).try_into().expect("el id son 32 bytes");
|
||||
RuntimeCall::Governance(origin_chain_governance::Call::finalize_proposal {
|
||||
proposal_id: origin_chain_governance::ProposalId(id),
|
||||
})
|
||||
}
|
||||
otra if otra.starts_with("depositar:") => {
|
||||
RuntimeCall::Governance(origin_chain_governance::Call::treasury_deposit {
|
||||
amount: otra["depositar:".len()..].parse().expect("depositar:<monto en plancks>"),
|
||||
})
|
||||
}
|
||||
otra if otra.starts_with("pagar:") => {
|
||||
let (id, amount) =
|
||||
otra["pagar:".len()..].split_once(':').expect("pagar:<id_hex>:<monto>");
|
||||
let id: [u8; 32] = hex(id).try_into().expect("el id son 32 bytes");
|
||||
RuntimeCall::Sudo(pallet_sudo::Call::sudo {
|
||||
call: Box::new(RuntimeCall::Governance(
|
||||
origin_chain_governance::Call::treasury_disbursement {
|
||||
proposal_id: origin_chain_governance::ProposalId(id),
|
||||
recipient: bob(),
|
||||
amount: amount.parse().expect("monto en plancks"),
|
||||
},
|
||||
)),
|
||||
})
|
||||
}
|
||||
otra => match otra.strip_prefix("set_code:") {
|
||||
Some(ruta) => {
|
||||
let code = std::fs::read(ruta).unwrap_or_else(|e| panic!("{ruta}: {e}"));
|
||||
@@ -67,7 +123,9 @@ fn llamada(nombre: &str) -> RuntimeCall {
|
||||
})
|
||||
}
|
||||
None => panic!(
|
||||
"llamada desconocida: {otra} (remark · finalize_override · uphold0 · set_code:<ruta>)"
|
||||
"llamada desconocida: {otra} (remark · finalize_override · uphold0 · \
|
||||
set_code:<ruta> · tesoro:<monto> · pagar:<id_hex>:<monto> · \
|
||||
votar:<id_hex>:<peso> · depositar:<monto> · finalizar:<id_hex>)"
|
||||
),
|
||||
},
|
||||
}
|
||||
@@ -122,8 +180,9 @@ fn main() {
|
||||
let bytes = hex(&args[2]);
|
||||
let eventos = Vec::<EventRecord<RuntimeEvent, H256>>::decode(&mut &bytes[..])
|
||||
.expect("System::Events");
|
||||
// La fase va delante: dice a qué extrinsic pertenece cada evento.
|
||||
for e in eventos {
|
||||
println!("{:?}", e.event);
|
||||
println!("{:?} · {:?}", e.phase, e.event);
|
||||
}
|
||||
}
|
||||
"cuenta" => {
|
||||
|
||||
Reference in new issue
Block a user