feat(provenance): FIX-4 — verificador attestation on-chain (cierra V3-C1 + squat)
El verificador attestation queda VIVO: una firma producida por el código REAL de Ergon (arkhe-noesis-core, postcard + mensaje domain-separado) verifica byte-fiel en el pallet Substrate. Cierra el FIX-DEFERRED del roadmap (helper de Ergon ya en árbol) + el replay cross-chain V3-C1. origin-chain-types/attestation_verify.rs (nuevo): - ATTESTATION_DOMAIN_TAG + attestation_signed_message: espejo BYTE-IDÉNTICO del oráculo (DC 076d15cc ASP-3). Mensaje = TAG ‖ genesis_hash(32) ‖ payload. - decode_signing_payload: lector postcard SECUENCIAL total y estricto del SigningPayload de Ergon (sin dep nueva en el runtime wasm frágil). Lectores varint u64/u32 con los bounds exactos de postcard (cierra el LOW de fidelidad flagged: verdict/severity/constraints son u32, máx 5 bytes / 5º ≤ 15). Cotas DoS en strings. NUNCA re-serializa (contrato ASP-3). pallet-origin-provenance: - attest_asset(signing_payload: BoundedVec<u8,512>, signature: [u8;64]): recibe los bytes postcard OPACOS + la firma; decodifica, verifica Ed25519 vía sp_io::crypto::ed25519_verify (host function, no bloatea wasm, precedente FIX-3) sobre el mensaje domain-separado con el genesis_hash de ESTA cadena. La pubkey se decodifica del payload (no param aparte). creator = el validador, anchored_at_block real (cerró los placeholders del scaffold). - Dedup-by-BLAKE3 con rechazo EXPLÍCITO (asset_id = BLAKE3 content hash, dentro del payload firmado → replay del mismo blob reusa el asset_id → AlreadyAttested). Hallazgos de la verificación adversarial (workflow ultracode, 3 lentes) cerrados de RAÍZ: - HIGH asset-id squat / provenance poisoning + MEDIUM tier-forgery: el gate era self-attested (level del payload) → cualquiera con una clave Ed25519 anclaba basura institucional bajo el hash PÚBLICO de un asset legítimo y lo envenenaba de forma inmutable. FIX: registro on-chain AuthorizedValidators (pubkey → nivel OIT) gobernado por Root (register_validator/revoke_validator); la membresía es la VERDAD, no el payload. Mismo patrón que TierProfiles de FIX-6 Stage 2a (legitimidad on-chain, no caller-supplied). Sunset a attestation-driven cuando aterrice el registro OIT de Mathi. - MEDIUM método muerto: removido el ValidationAttestation::signing_payload() SCALE divergente (producía bytes que jamás verificarían — footgun). Decisiones (autoridad Pilar 4): sp_io ed25519_verify (consistente FIX-3, estándar Substrate) · dedup-by-asset_id subsume replay exacto · golden vectors generados desde el oráculo REAL de Ergon (seed [7u8;32], genesis 0xAB) = prueba cross-runtime. PENDIENTE (gap legítimo, no bug): OriginChainAttestationSink (impl del trait ValidationAttestationSink de Ergon) es código cliente off-chain — vive en un crate bridge del monorepo (subxt), NO en el chain repo (dependency direction submódulo→monorepo prohibida). Se cablea con la integración Noesis→chain. Tests: 19 provenance (golden vectors cross-runtime + squat cerrado + revoke + anti-replay cross/intra-chain + tamper + malformed) + 72 types (decoder fuzz- equivalente a postcard + fidelidad u32) + 21 suites workspace verde. cargo +1.88. WASM: build VPS-gated (UnknownOpcode 252 ambiental, ver v0.0.58). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3364994cce
commit
ce18e191d0
7 files changed
+769
-475
No files matched your search
@@ -1,29 +1,20 @@
|
||||
//! # Mock runtime canonical para integration tests v0.0.18
|
||||
//! # Mock runtime para integration tests
|
||||
//!
|
||||
//! Provee mock minimal `Test` runtime composition (`System` + `Provenance` solo)
|
||||
//! para E2E tests dispatchable + storage + event assertions per PROV-3 flow
|
||||
//! 6-step canonical:
|
||||
//! Mock minimal (`System` + `Provenance`) para E2E del flow PROV-3 + FIX-4:
|
||||
//! decode postcard → gates → Ed25519 verify domain-separado → anclar.
|
||||
//!
|
||||
//! 1. PROV-4 Ed25519 signature verification
|
||||
//! 2. PROV-5 validator OIT level >= 3 institutional
|
||||
//! 3. PROV-6 is_anchorable check (verdict + severity)
|
||||
//! 4. PROV-2 ValidationAttestation → AssetAttestation conversion
|
||||
//! 5. PROV-2 StorageMap insert
|
||||
//! 6. PROV-7 AssetAttested event emission
|
||||
//!
|
||||
//! ## Cross-reference canonical
|
||||
//!
|
||||
//! - DC `ORIGIN_PROVENANCE_PALLET_V1` PROV-1..8 sub-decisions
|
||||
//! - `tests_integration.rs` 5 scenarios canonical E2E
|
||||
//! - `lib.rs::pallet::attest_asset` dispatchable impl real
|
||||
//! FIX-4: `new_test_ext_with_genesis` siembra `block_hash(0)` = el genesis con
|
||||
//! el que se firmaron los golden vectors (generados desde el oráculo REAL de
|
||||
//! Ergon `arkhe-noesis-core`), para probar la verificación cross-runtime.
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
use crate as origin_chain_provenance;
|
||||
use frame_support::derive_impl;
|
||||
use sp_core::H256;
|
||||
use sp_runtime::BuildStorage;
|
||||
|
||||
/// Mock block type canonical para tests.
|
||||
/// Mock block type para tests.
|
||||
type Block = frame_system::mocking::MockBlock<Test>;
|
||||
|
||||
frame_support::construct_runtime!(
|
||||
@@ -44,34 +35,52 @@ impl origin_chain_provenance::Config for Test {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
}
|
||||
|
||||
/// Test externalities builder canonical.
|
||||
///
|
||||
/// Returns new `sp_io::TestExternalities` con frame_system genesis configurado
|
||||
/// y block_number set to 1 (PROV-7 events requieren block_number > 0 para
|
||||
/// proper testing canonical).
|
||||
pub fn new_test_ext() -> sp_io::TestExternalities {
|
||||
/// Genesis hash de los golden vectors ASP-3 (`/tmp/golden-asp3`, GENESIS=0xAB×32).
|
||||
/// El pallet lee `block_hash(0)`; el mock lo siembra con ESTE valor para que la
|
||||
/// verificación Ed25519 coincida con las firmas del oráculo.
|
||||
pub const GOLDEN_GENESIS: [u8; 32] = [0xABu8; 32];
|
||||
|
||||
/// Pubkey del validador golden (seed [7u8;32] → todos los vectores la usan).
|
||||
pub const GOLDEN_VALIDATOR_PUBKEY: [u8; 32] = [
|
||||
0xea, 0x4a, 0x6c, 0x63, 0xe2, 0x9c, 0x52, 0x0a, 0xbe, 0xf5, 0x50, 0x7b, 0x13, 0x2e, 0xc5, 0xf9,
|
||||
0x95, 0x47, 0x76, 0xae, 0xbe, 0xbe, 0x7b, 0x92, 0x42, 0x1e, 0xea, 0x69, 0x14, 0x46, 0xd2, 0x2c,
|
||||
];
|
||||
|
||||
/// Registra el validador golden con el nivel dado (vía Root) — helper de tests.
|
||||
pub fn register_golden_validator(level: u8) {
|
||||
crate::pallet::Pallet::<Test>::register_validator(
|
||||
RuntimeOrigin::root(),
|
||||
GOLDEN_VALIDATOR_PUBKEY,
|
||||
level,
|
||||
)
|
||||
.expect("Root registra el validador");
|
||||
}
|
||||
|
||||
/// Test externalities con `block_hash(0)` sembrado al genesis dado.
|
||||
pub fn new_test_ext_with_genesis(genesis: [u8; 32]) -> sp_io::TestExternalities {
|
||||
let mut ext: sp_io::TestExternalities = frame_system::GenesisConfig::<Test>::default()
|
||||
.build_storage()
|
||||
.expect("genesis canonical builds")
|
||||
.expect("genesis builds")
|
||||
.into();
|
||||
ext.execute_with(|| {
|
||||
System::set_block_number(1);
|
||||
// Siembra el hash del bloque génesis (que el pallet lee para el dominio).
|
||||
frame_system::BlockHash::<Test>::insert(0u64, H256(genesis));
|
||||
});
|
||||
ext
|
||||
}
|
||||
|
||||
/// Test signing key seed canonical (deterministic Ed25519 keypair).
|
||||
///
|
||||
/// Per ed25519-dalek pattern: seed [7u8; 32] produces deterministic verifying_key.
|
||||
/// Used across integration tests para reproducible signing.
|
||||
pub const TEST_SIGNING_SEED: [u8; 32] = [7u8; 32];
|
||||
/// Test externalities con el genesis de los golden vectors (caso por defecto).
|
||||
pub fn new_test_ext() -> sp_io::TestExternalities {
|
||||
new_test_ext_with_genesis(GOLDEN_GENESIS)
|
||||
}
|
||||
|
||||
/// Test origin account canonical (32-byte zero pattern).
|
||||
/// Submitter de prueba (un relayer cualquiera — paga el fee, no es el validador).
|
||||
pub fn alice_origin() -> RuntimeOrigin {
|
||||
RuntimeOrigin::signed(sp_runtime::AccountId32::new([1u8; 32]))
|
||||
}
|
||||
|
||||
/// Test origin account canonical (BOB pattern).
|
||||
/// Segundo submitter (BOB).
|
||||
pub fn bob_origin() -> RuntimeOrigin {
|
||||
RuntimeOrigin::signed(sp_runtime::AccountId32::new([2u8; 32]))
|
||||
}
|
||||
Reference in new issue
Block a user