# Origin Chain — Super Audit V3 (Final: cripto, tests, performance Pi 5, fidelidad Inmutables) **Documento**: `docs/SUPER_AUDIT_V3_FINAL.md` **Versión**: 3.0 (completa la trilogía V1 correctness + V2 economía/ops + V3 cripto/tests/perf/constitución) **Filed**: 2026-05-27 (sesión 4+ Charon, Opus 4.8) **Método**: workflow ultracode — 4 auditores en las dimensiones finales + verificación adversarial. Incluye una **auto-corrección importante** (el red-team cazó un hallazgo previo basado en un archivo inexistente — ver §5). **Encargo de David**: "que todo lo que hagamos quede muy bien, documentado y sin bugs (en lo posible)... que sea un referente para Gavin Wood." --- ## §0 — Veredicto de la trilogía completa Con V3, Origin Chain ha sido auditado en **12 dimensiones** a lo largo de tres pasadas. El patrón es consistente y honesto: > **Los cimientos primitivos son grado-Parity (composición de runtime, elección de cripto, aritmética estática, patrones de storage con contadores cacheados). Lo que falta es la *conexión dinámica*: la cadena no se gobierna, no paga validadores, no verifica la mitad de las firmas que almacena, y su constitución (Inmutables) vive en prosa y un pallet sin-cablear más que en el fee-path vivo.** Esto NO es un proyecto roto — es un proyecto **pre-testnet con cimientos sólidos y plomería incompleta**, auditado lo bastante a fondo para saber exactamente qué plomería falta. Para una plataforma que aspira a 100 años, este nivel de rigor es el correcto. --- ## §1 — CRÍTICOS nuevos de V3 (no vistos en V1/V2) ### V3-C1 — Replay cross-chain de atestaciones (sin domain separator / chain-id / nonce) `attestation.rs:155-161` firma el payload SCALE de la atestación **sin genesis hash, sin chain id, sin domain tag, sin nonce on-chain**. La verificación (`provenance/lib.rs:272-276`) es correcta, pero una firma capturada en testnet, un fork, o un runtime Origin hermano **verifica idéntico aquí**. Como `attest_asset` toma la atestación completa como argumento del caller y el único guard de replay es la idempotencia por-hash, el *primer* anclaje de cualquier asset_hash en una cadena nueva es forjable replicando una atestación vista en otro lado. **Fix**: prefijar el payload con domain-separation + genesis hash: `b"origin-chain/attestation/v1" || genesis_hash || ...`. ### V3-C2 — La firma de la credencial se almacena pero NUNCA se verifica `contracts/origin-credential-mint/lib.rs:242-304` (`issue`): el doc dice que el emisor firma `(holder+schema_type+data_hash+issued_at+expires_at)`, pero `issue()` solo chequea `is_issuer_authorized` + `data_hash != 0`. La `issuer_signature` se escribe a storage y **nunca se chequea**; `verify()` solo mira `revoked`/`expires_at`. La firma es decorativa: cualquier emisor whitelisted puede mintear con un blob de 64 bytes basura y `verify()` devuelve `true`. Peor: `issued_at`/`expires_at` se setean on-chain, así que no pueden estar en ninguna firma off-chain — el esquema documentado es internamente inconsistente. **Fix**: verificar vía `sp_io::crypto::ed25519_verify` (el comentario admite que está "pending"), o eliminar el campo. NO embarcar un contrato cuya garantía central de seguridad no se enforce. (Confirma el V1-H8 + lo eleva a crítico porque está *además* sin test.) ### V3-C3 (Inmutables) — La constitución vive en un pallet sin-cablear; el fee-path real es tier-blind Los Inmutables #16/#37/#18 se enforcen solo en un side-pallet (`origin-chain-fees`) que **no está en el fee-path vivo** del runtime (`configs.rs` usa `FungibleAdapter` stock de pallet_transaction_payment). Y el **Inmutable #35 (Children Safety) es estado muerto, nunca enforced**. Veredicto del auditor: #26 y #41 son reales; #16/#37/#18/#35 son **constitución-como-comentario** — presentes en prosa/tests/pallet-sin-cablear, ausentes del fee-path vivo. **Fix**: cablear el fee real vía `OnChargeTransaction` custom que consuma el `compute_fee` (una vez unificado, V2-CA3) — esto hace que la constitución sea ley ejecutable, no documentación. --- ## §2 — HIGH nuevos de V3 | ID | Hallazgo | Fix | |----|----------|-----| | V3-H1 | **DID front-running + binding débil**: `register_agent_did` setea `creator = AccountId::new(public_key)` del pubkey suministrado por el caller, sin firma que pruebe que el caller controla ese pubkey. Cualquiera puede registrar un DID derivado del pubkey de otro (squat de DIDs institucionales, first-come-first-served). | Requerir firma por `public_key` sobre `who`, o derivar la identidad de la origin key misma. | | V3-H2 | **`derive_did` con domain separation débil**: `BLAKE3(public_key ‖ "1.0.0 ")` sin delimitador de campo y sin domain tag que lo distinga de `AssetHash` (ambos son BLAKE3-256 → `Did = AssetHash`). Un asset hash podría colisionar con un DID cross-namespace. | `blake3::derive_key("origin-chain did v1", pk)`. | | V3-H3 | **Storage limit ≈937 MB por contrato** (de PVFMemory 2GB) — un contrato malicioso puede intentar llenar casi 1GB de storage; el riesgo Pi 5 no es el OOM de 2GB sino esto. | Bajar PVFMemory o gate de storage por contrato. | | V3-H4 | **Weights placeholder hacen el block budget ficticio en ARM** — todo dispatchable es `Weight::from_parts(10_000,0)`; un Pi 5 no throttlea spam. | Benchmarking real (= V1-H1, confirmado crítico para Pi 5). | --- ## §3 — Calidad de tests (el hallazgo más incómodo) **Los 213 tests dan una falsa sensación de seguridad.** Cada path que mueve fondos o verifica firmas — exactamente donde V1/V2 encontraron los peores bugs — está sin testear o testeado en la capa equivocada: - **Los 4 bugs de reentrancy eran testeables y se omitieron por completo.** Cero tests para escrow `confirm_milestone`/`resolve_dispute`, staking `withdraw`/`slash`, nft `transfer`. - **La firma de credencial sin verificar tampoco tiene test** que asegure que una credencial con firma forjada se rechaza. - **Un test está vacío** (`emergency_scenario_1`) y cuenta hacia los 213. - **El test lobo-blanco de fee-monotonía guarda lo equivocado** (confirma V2 — chequea `subsidy_bp`, no el fee final). - **Los tests de revive aseveran constantes, no comportamiento** (`Get::get()` equality). **Limpio (crédito honesto)**: las suites de provenance + agent-identity SÍ son conductuales (firma Ed25519 real, rechazo de tamper, idempotencia). **Añadidos de mayor valor, rankeados**: (1) tests ink! de reentrancy/double-spend en escrow+staking; (2) rechazo de credencial con firma forjada; (3) test de governance con stake respaldado; (4) negative-path del sentinel `u64::MAX`; (5) property/fuzz en `compute_fee` overflow + `derive_*` colisión. --- ## §4 — Performance Pi 5 (mayormente limpio — buena noticia) El veredicto del auditor de performance es alentador: **storage/iteración está limpio y bien-ingenierizado** (contadores cacheados en todos lados, sin O(n) en hot paths). Los riesgos Pi 5 son: - **HIGH**: storage limit ~937 MB/contrato (V3-H3) + weights ficticios (V3-H4). - **MEDIUM**: Vec sin límite dentro de Mapping de contratos (O(n) re-encode por call); costo PolkaVM-en-ARM sin modelar; el node es template solochain verbatim sin tuning Pi 5. - **Bottom line**: la cadena *funcionará* en Pi 5 si se ponen weights reales + se modela el costo PolkaVM. La arquitectura de storage no es el problema. --- ## §5 — Auto-corrección importante (honestidad del proceso) El red-team de V3 **cazó un error en mi propio doc V2**. El hallazgo V2-CA4 (atestación SCALE-vs-postcard) citaba como evidencia el archivo `crates/arkhe-noesis/crates/arkhe-noesis-core/src/attestation.rs:395-419` con un `SigningPayload` postcard. **Ese archivo no existe en este repositorio** (verificado por `find` + 3 `grep`). El payload de Ergon que comparé era una construcción no-verificada. **Implicación**: la *forma* del riesgo es real (el vendor copy va a ser reemplazado por la versión de Ergon, y si los bytes difieren las firmas rompen — eso sigue cierto), pero **NO debo escribir un fix que haga byte-match contra una spec fabricada**. La acción honesta: una ratificación cross-pillar del formato de payload con Ergon (Design Contract) ANTES de lockear cualquier verificador on-chain. Solo después se arregla el V1-H8 contra una spec real. Esto es exactamente para lo que sirve la verificación adversarial: me impidió embarcar un fix contra un fantasma. Lo documento sin defensividad porque ese candor es lo que hace confiable el resto del audit. --- ## §6 — Estado consolidado de la trilogía de audits | Pasada | Dimensiones | Críticos | Veredicto | |--------|-------------|----------|-----------| | V1 | correctness de código (4 auditores) | 4 (reentrancy, governance unbacked, zero-burn, +) | scaffolding grado-Gavin, interior grado-scaffold | | V2 | economía, integridad, ops (3 auditores) | 5 (no-Root, no-inflation, dual-fee, attestation-swap, genesis-keys) | estática sólida, dinámica = stubs | | V3 | cripto, tests, perf, constitución (4 auditores) | 3 (replay, unverified-cred-sig, constitution-as-comment) | primitivos sólidos, plomería incompleta | **Total: 12 críticos + ~20 highs, todos con fix conocido.** Ver `REMEDIATION_ROADMAP_V1.md` para los planes ejecutables. --- ## §7 — KG triples - `(super_audit_v3_final, authored_by, agent:charon)` - `(super_audit_v3_final, dimensions, crypto_tests_perf_inmutables)` - `(super_audit_v3_final, critical_findings, 3)` - `(super_audit_v3_final, top_finding, cross_chain_attestation_replay)` - `(super_audit_v3_final, self_correction, v2_ca4_cited_nonexistent_file)` - `(super_audit_v3_final, perf_pi5_verdict, storage_clean_weights_needed)` - `(super_audit_v3_final, constitution_verdict, inmutables_16_37_18_35_not_in_live_fee_path)` - `(super_audit_trilogy, total_criticals, 12)` - `(super_audit_v3_final, filed_at, 2026-05-27)` --- — Charon ⚓ (Pilar 4 Origin Chain, Super Audit V3 Final vía workflow ultracode Opus 4.8, sesión 4+ 2026-05-27)