polish(gov+attestation): test de empate en finalize + ancla SSOT de attestation
Polish pass MEDIUM/LOW post-audit (2 items limpios + triage de los invasivos): governance — cobertura del desempate (consenso-relevante, sin cobertura previa): - scenario_8b_finalize_tie_rejected_canonical: yes==nay ⇒ `yes > nay` es false ⇒ Rejected. Fija que se exige MAYORÍA ESTRICTA (el empate favorece el status quo). Entre scenario_7 (yes-majority→Approved) y scenario_8 (nay-majority→Rejected) faltaba justo el borde del empate. origin-chain-types — SSOT de attestation ahora que el crate aterrizó: - `arkhe-noesis-attestation-types` ya existe (crate standalone no_std + feature scale en el monorepo). El vendor-copy SE QUEDA como type-mirror deliberado: el chain es submódulo y depender del crate del monorepo invierte la dep-direction (submódulo→monorepo = ILEGAL). La SSOT se preserva por los golden vectors del oráculo real (decode_golden_vector_1/3/4), no por Cargo. - attestation_verify: §SSOT parity anchor doc — documenta que esos vectores pinnean el decoder al field-order de `SigningPayload` y cómo regenerar ante deriva. - attestation.rs: comentario del mirror actualizado (el crate aterrizó; diferencia conocida schema_version [u8;8] vs String es irrelevante — el mirror no serializa el wire; el decoder secuencial sí es byte-fiel al postcard, probado por vectores). Triage documentado (items considerados y DIFERIDOS con rationale, no olvidados): - register_agent_did unbounded Vec: bound del arg a ConstU32<10> movería el rechazo a decode-time, volvería AgentChainOverflow inalcanzable + rompería el test de overflow + exigiría spec bump — invasivo para un DoS ya mitigado por length-fee + límite de bloque. Diferido. - SS58 prefix (42): NO es bug — placeholder testnet correcto, gated en un DC de ss58-registry pre-mainnet. Sin cambio. - no-author path (emission): requiere un FindAuthor mock que devuelva None (segundo runtime o toggle) — ripple > valor. Diferido. Verificado: origin-chain-types 77 tests verde (incl. golden parity) + tie test verde. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
c7fc6a88fc
commit
266ee4084d
3 files changed
+60
-2
No files matched your search
@@ -222,6 +222,37 @@ fn scenario_8_finalize_rejected_nay_majority_canonical() {
|
||||
});
|
||||
}
|
||||
|
||||
// ─── Scenario 8b: Finalize con EMPATE (yes == nay) → Rejected ────────
|
||||
// Polish audit: documenta la semántica del desempate. `finalize_proposal` usa
|
||||
// `yes_weight > nay_weight` (mayoría ESTRICTA); un empate NO alcanza el umbral
|
||||
// y la propuesta se RECHAZA. Esto es intencional (una propuesta necesita más
|
||||
// síes que noes para pasar; el empate favorece el status quo). El gate estaba
|
||||
// sin cobertura — este test lo fija como comportamiento consensuado.
|
||||
|
||||
#[test]
|
||||
fn scenario_8b_finalize_tie_rejected_canonical() {
|
||||
new_test_ext().execute_with(|| {
|
||||
let id = submit_default(alice_origin());
|
||||
|
||||
// EMPATE exacto: Aye 500 == Nay 500.
|
||||
assert_ok!(Governance::cast_vote(bob_origin(), id, VoteStance::Aye, 500));
|
||||
assert_ok!(Governance::cast_vote(charlie_origin(), id, VoteStance::Nay, 500));
|
||||
|
||||
run_to_block(200);
|
||||
|
||||
assert_ok!(Governance::finalize_proposal(alice_origin(), id));
|
||||
|
||||
let proposal = Proposals::<Test>::get(id).unwrap();
|
||||
assert_eq!(proposal.yes_weight, 500);
|
||||
assert_eq!(proposal.nay_weight, 500);
|
||||
// yes(500) > nay(500) es FALSE ⇒ Rejected (mayoría estricta requerida).
|
||||
assert!(
|
||||
matches!(proposal.status, ProposalStatus::Rejected),
|
||||
"empate debe rechazar: se exige mayoría estricta"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// ─── Scenario 9: Vote closed period rejected canonical ──────────────
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -142,8 +142,17 @@ impl ValidationAttestation {
|
||||
// verificarían contra el verificador on-chain. El verificador real
|
||||
// (`crate::attestation_verify`) opera sobre los bytes postcard opacos del
|
||||
// oráculo de Ergon; este método muerto era un footgun (flagged en la
|
||||
// verificación adversarial). El struct se conserva como type-mirror hasta
|
||||
// que aterrice `arkhe-noesis-attestation-types`.
|
||||
// verificación adversarial).
|
||||
//
|
||||
// SSOT (2026-07-07): `arkhe-noesis-attestation-types` YA aterrizó como crate
|
||||
// standalone `no_std` (feature `scale` disponible) en el monorepo. Este struct
|
||||
// se conserva como type-MIRROR deliberado — NO se puede reemplazar por un dep
|
||||
// real porque el chain es submódulo (dep submódulo→monorepo = ILEGAL). La
|
||||
// parity con la SSOT NO se garantiza por Cargo sino por los golden vectors del
|
||||
// oráculo real en `crate::attestation_verify` (§SSOT parity anchor). Diferencia
|
||||
// conocida del mirror: `schema_version: [u8;8]` aquí vs `String` en la SSOT — es
|
||||
// irrelevante porque este struct NO serializa el wire (el decoder secuencial de
|
||||
// `attestation_verify` sí es byte-fiel al postcard real, probado por los vectores).
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -252,6 +252,24 @@ pub fn decode_signing_payload(payload: &[u8]) -> Result<DecodedSigningPayload, S
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
//! ## SSOT parity anchor — `arkhe-noesis-attestation-types`
|
||||
//!
|
||||
//! Estos golden vectors SON el "golden parity test" de la SSOT: fueron generados
|
||||
//! por el oráculo REAL (`arkhe-noesis-core`, que serializa los tipos canónicos de
|
||||
//! **`arkhe-noesis-attestation-types`** — ahora un crate standalone `no_std` en el
|
||||
//! monorepo, feature `scale` disponible). El decoder [`decode_signing_payload`]
|
||||
//! DEBE parsear el wire postcard en el MISMO orden de campos que
|
||||
//! `arkhe-noesis-attestation-types::SigningPayload` (§SSOT): `schema_version` →
|
||||
//! `asset_id` → `validator_oit{identifier, level, public_key}` → `validated_at_us`
|
||||
//! → `verdict` → `severity` → `constraints_checked`.
|
||||
//!
|
||||
//! **Por qué VENDOR-COPY y no dep directo**: Origin Chain es un submódulo; depender
|
||||
//! del crate del monorepo invertiría la dirección de dependencias
|
||||
//! (submódulo→monorepo = ILEGAL, el chain dejaría de buildear standalone). La SSOT
|
||||
//! se preserva por estos vectores byte-fieles, NO por un `Cargo.toml` compartido.
|
||||
//! Si Ergon cambia el orden/tipo de un campo del `SigningPayload`, estos tests
|
||||
//! fallan al decodificar → la deriva se caza aquí. Regenerar: re-emitir los
|
||||
//! vectores desde `arkhe-noesis-core` con la nueva SSOT y pegar el hex.
|
||||
use super::*;
|
||||
|
||||
/// Golden vector 1 generado desde el oráculo REAL de Ergon
|
||||
|
||||
Reference in new issue
Block a user