Los 19 extrinsics de provenance/fees/agent-identity/emission/governance pasan de weights placeholder `from_parts(10_000, 0)` (proof_size=0, sin contar reads/writes de DB — vector de DoS real) a weights INTERIM hand-derived DB-aware vía el trait `WeightInfo`. Cierra el mispricing de DoS en TODOS los extrinsics del chain. Cada pallet gana: - weights.rs: trait WeightInfo + SubstrateWeight<T> (DB-aware vía T::DbWeight) + impl () - benchmarking.rs: benchmarks FRAME v2, VERIFICADOS por impl_benchmark_test_suite! (19 benchmarks ejecutan+pasan en los mocks — cargo test --features runtime-benchmarks) - Config: `type WeightInfo` + swap de los `#[pallet::weight]` Runtime: - define_benchmarks! con los 5 pallets + runtime API Benchmark (inerte sin la feature) - WeightInfo cableado a SubstrateWeight<Runtime> en cada Config - RUNTIME_VERSION 8→9 (weights = consenso-relevante: fee + llenado de bloque; las firmas de los extrinsics NO cambian ⇒ TRANSACTION_VERSION intacto) 3 bugs latentes cerrados (territorio virgen — el runtime NUNCA se compiló con runtime-benchmarks; las dimensiones nuevas cazan lo que no se había visto): - pallet-grandpa/try-runtime mal ubicado en la feature runtime-benchmarks (copy-paste erróneo: features ortogonales; NUNCA activaba los benchmarks de grandpa) - feature runtime-benchmarks incompleta → E0046 en OriginFeeAdapter (métodos benchmark-gated de OnChargeTransaction) + pallet-staking (StakingUnchecked::migrate_to_direct_staker); ahora lista TODOS los pallets como un runtime de producción Substrate - pallet-revive/runtime-benchmarks omitido a propósito (fixtures RISC-V/PolkaVM indisponibles; ReviveApi deferred, no benchmarkeado) emission: mock extraído del `mod tests` inline a `pub mod mock` (reuso del test-suite, sin duplicar el scaffold; los 15 tests existentes + el benchmark verdes). GATED (documentado en el §Estado de cada weights.rs): la generación de números CANÓNICOS por CLI requiere el node sc-service o omni-bencher+WASM (ambos bloqueados hace sesiones). Los weights interinos son DB-aware/DoS-safe y regenerables de forma mecánica cuando aterrice el HW de referencia (Pi5/VPS) — benchmarking.rs es la fuente de verdad de la FORMA. Verificado: 5 test-suites de pallet verdes (con y sin runtime-benchmarks) + runtime compila en ambos modos + workspace sin regresión + 40 tests de runtime (incl. v9). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
443 lines
20 KiB
Rust
443 lines
20 KiB
Rust
//! # pallet-origin-provenance — Phase 2.1 COMPLETE + v0.0.18 mock runtime integration tests
|
|
//!
|
|
//! Asset attestation pallet canonical per CHN-7 + DC PROVENANCE_PALLET_V1
|
|
//! (6/10 firmas sesión 3+ 2026-05-24, deadline ratificación 2026-05-29).
|
|
//!
|
|
//! ## Status canonical: v0.0.18 — Mock runtime integration tests E2E own fixtures
|
|
//!
|
|
//! Este crate provee:
|
|
//! - Storage layout canonical `StorageMap<AssetHash, AssetAttestation>` (PROV-2) ✅
|
|
//! - Dispatchable canonical `attest_asset(origin, ValidationAttestation)` (PROV-3) ✅
|
|
//! - Real Ed25519 verify canonical via ed25519-dalek (PROV-4) ✅ v0.0.16
|
|
//! - OIT Level >= 3 enforcement canonical (PROV-5) ✅
|
|
//! - is_anchorable check canonical (PROV-6) ✅
|
|
//! - Events canonical `AssetAttested` + `AttestationRejected` (PROV-7) ✅
|
|
//! - Errors canonical (PROV-4/5/6) ✅
|
|
//! - Pallet Config trait canonical ✅
|
|
//! - Runtime API canonical `verify_provenance` (PROV-8) ✅ v0.0.17
|
|
//! - **Mock runtime + integration tests E2E own fixtures (v0.0.18 sesión 3+) ✅**
|
|
//!
|
|
//! ## v0.0.18 sesión 3+ mock runtime integration tests
|
|
//!
|
|
//! Per audit canonical sesión 3+ (`AUDIT_CHARON_SESION_3_STATE_V1.md`),
|
|
//! Ergon E2E fixture diary claim aspirational sin shipped commit verified.
|
|
//! Charon construye own fixtures basados en vendor copy ValidationAttestation
|
|
//! real existente. 9 integration tests canonical:
|
|
//!
|
|
//! - 5 scenarios PROV-3 flow: passed_info anchored + warning anchored +
|
|
//! failed_critical rejected + passed_block_security rejected + anonymous_level_0 rejected
|
|
//! - 4 additional: idempotency + tampered_signature + tampered_payload + 6-step walkthrough
|
|
//!
|
|
//! Mock runtime composition canonical via `construct_runtime!` macro (System + Provenance).
|
|
//!
|
|
//! ## Lo que NO funciona aún (post-Ergon sub-crate Cap 42+)
|
|
//!
|
|
//! - Real `ValidationAttestation` consumption — usa vendor copy interim Option B
|
|
//! (signing_payload usa SCALE encoding canonical; Ergon real sub-crate usará
|
|
//! postcard encoding — swap canonical post Cap 42+ sin breaking pallet logic)
|
|
//! - Runtime API canonical `verify_provenance` (PROV-8) — v0.0.17 next slice
|
|
//!
|
|
//! Per Ergon decision (diary `3f773a03` 2026-05-24): él ships
|
|
//! `arkhe-noesis-attestation-types` standalone canonical Cap 42+ (target
|
|
//! 2026-05-30 → 06-05). Mientras tanto, mi vendor copy en
|
|
//! `origin-chain-types::attestation` permite avanzar canonical compile-only.
|
|
//!
|
|
//! ## Roadmap canonical post-ratificación DC PROVENANCE_PALLET_V1
|
|
//!
|
|
//! 1. **v0.0.14 (ESTA)**: Scaffold skeleton canonical compile-only
|
|
//! 2. **v0.0.15** (post 2026-05-29 DC ratify): Storage + events + errors wired
|
|
//! 3. **v0.0.16** (post 2026-06-05 Ergon sub-crate): Real Ed25519 + OIT verify
|
|
//! 4. **v0.0.17**: Runtime API canonical verify_provenance + wire en runtime
|
|
//! 5. **v0.1.0** (post Q3 2026 testnet): full integration end-to-end
|
|
//!
|
|
//! ## Cross-pillar consumers canonical desbloqueados (post-impl)
|
|
//!
|
|
//! - Trinity (Phanes) asset mesh BLAKE3 → `attest_asset` canonical
|
|
//! - Codex (.odoc) chain_attestation block populated by Vault tier
|
|
//! - Ethos (Sussi) credential NFT mint + chain attestation
|
|
//! - Atelier (Marihan) skill NFT mint via institutional wallet
|
|
//! - Origindocs (David ratificado 2026-05-24): academic IP defensible
|
|
|
|
#![cfg_attr(not(feature = "std"), no_std)]
|
|
#![warn(missing_docs)]
|
|
|
|
pub use pallet::*;
|
|
|
|
/// Weights canonical (INTERIM hand-derived DB-aware — ver `weights.rs` §Estado).
|
|
pub mod weights;
|
|
pub use weights::WeightInfo;
|
|
|
|
/// Re-export canonical types desde origin-chain-types para consumers.
|
|
pub use origin_chain_types::{
|
|
AssetAttestation, AssetHash, OitIdentity, SeverityTier, ValidationAttestation,
|
|
ValidationVerdict,
|
|
};
|
|
|
|
/// PROV-8 canonical: Runtime API canonical para light clients + indexers +
|
|
/// cross-pillar consumers.
|
|
///
|
|
/// Permite query canonical de attestation status sin necesidad de full node
|
|
/// access — light client envía hash → recibe `Option<AssetAttestation>` con
|
|
/// signature canonical + creator + block number canonical + agent chain.
|
|
///
|
|
/// Consumers canonical:
|
|
/// - Trinity (Phanes) — asset gallery render con attestation badge
|
|
/// - Codex (.odoc) — Vault tier verify-before-render canonical
|
|
/// - Codex origindocs — academic IP defensible verify
|
|
/// - Ethos (Sussi) — credential verification canonical
|
|
/// - Atelier (Marihan) — Skill Library Zona 6 verify-before-purchase
|
|
///
|
|
/// Future Phase 2.3 expansion canonical:
|
|
/// - `verify_by_creator(creator) -> Vec<AssetAttestation>` (paginated)
|
|
/// - `list_attestations_in_block(block_num) -> Vec<AssetAttestation>`
|
|
/// - `cross_runtime_proof(hash) -> CrossRuntimeProof`
|
|
sp_api::decl_runtime_apis! {
|
|
/// Provenance runtime API canonical canonical (PROV-8).
|
|
pub trait ProvenanceApi {
|
|
/// Verify provenance canonical de un asset por su hash canonical.
|
|
///
|
|
/// Returns `Some(AssetAttestation)` si attested previamente on-chain,
|
|
/// `None` si nunca attested canonical. Read-only query canonical sin
|
|
/// transaction fee canonical (off-chain via RPC).
|
|
fn verify_provenance(hash: AssetHash) -> Option<AssetAttestation>;
|
|
}
|
|
}
|
|
|
|
#[frame_support::pallet]
|
|
pub mod pallet {
|
|
use frame_support::pallet_prelude::*;
|
|
use frame_system::pallet_prelude::*;
|
|
use frame_support::sp_runtime::traits::{SaturatedConversion, Zero};
|
|
use origin_chain_types::{
|
|
attestation_signed_message, decode_signing_payload, AssetAttestation, AssetHash,
|
|
};
|
|
// Trait de weights en scope para resolver `T::WeightInfo::<method>()`.
|
|
use crate::weights::WeightInfo;
|
|
|
|
/// Cota DoS del `signing_payload` postcard recibido (el real ~115 bytes;
|
|
/// 512 es holgado para schema/identifier futuros sin permitir flooding).
|
|
pub type MaxPayloadLen = ConstU32<512>;
|
|
|
|
/// Pallet configuration trait canonical.
|
|
///
|
|
/// Per DC PROVENANCE_PALLET_V1 PROV-3 + ADR-0057 trait-seam discipline.
|
|
#[pallet::config]
|
|
pub trait Config: frame_system::Config {
|
|
/// Runtime event type canonical wired into `RuntimeEvent` aggregate.
|
|
type RuntimeEvent: From<Event<Self>>
|
|
+ IsType<<Self as frame_system::Config>::RuntimeEvent>;
|
|
|
|
/// Weight information canonical para los extrinsics de este pallet.
|
|
/// El runtime lo cablea a `weights::SubstrateWeight<Runtime>`; los mocks
|
|
/// usan `()`. Ver `weights.rs` + `benchmarking.rs`.
|
|
type WeightInfo: crate::weights::WeightInfo;
|
|
|
|
// Future canonical (post OCAFB ratify):
|
|
// type OitFeeSchedule: arkhe_amis_server::OitFeeSchedule;
|
|
}
|
|
|
|
/// Pallet definition canonical.
|
|
///
|
|
/// v0.0.15: `AssetAttestation.agent_chain` ahora es `BoundedVec<AccountId,
|
|
/// ConstU32<10>>` canonical → MaxEncodedLen ahora satisfied canonical →
|
|
/// `#[pallet::without_storage_info]` ya NO necesario canonical.
|
|
/// StorageVersion en código (FIX-2 lifecycle, audit V1-H2/V2-CA2): arranca
|
|
/// en 0 (genesis). Habilita migraciones futuras vía on_chain_storage_version().
|
|
const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
|
|
|
|
#[pallet::pallet]
|
|
#[pallet::storage_version(STORAGE_VERSION)]
|
|
pub struct Pallet<T>(_);
|
|
|
|
/// PROV-2 canonical: `StorageMap<AssetHash, AssetAttestation>` canonical.
|
|
///
|
|
/// O(1) lookup canonical via Blake2_128Concat hasher (FRAME-native canonical).
|
|
/// Memory canonical ~140 bytes per attestation canonical.
|
|
#[pallet::storage]
|
|
#[pallet::getter(fn attestation_of)]
|
|
pub type Attestations<T: Config> =
|
|
StorageMap<_, Blake2_128Concat, AssetHash, AssetAttestation, OptionQuery>;
|
|
|
|
/// FIX-4 (cierra el HIGH "asset-id squat"): registro on-chain de validadores
|
|
/// Noesis autorizados → nivel OIT asignado por governance. **La membresía es
|
|
/// la VERDAD**, no el `level` que el firmante pone en su propio payload
|
|
/// (que era self-attested → cualquiera con una clave Ed25519 podía anclar
|
|
/// basura institucional bajo el hash público de un asset legítimo y
|
|
/// envenenarlo de forma inmutable). Ahora solo una pubkey REGISTRADA puede
|
|
/// anclar; un validador registrado que ancle basura es revocable y auditable
|
|
/// (problema de governance, no ataque anónimo). Mismo patrón que
|
|
/// `TierProfiles` de FIX-6 Stage 2a: legitimidad on-chain, no caller-supplied.
|
|
///
|
|
/// **Root-only interim** (sudo Index 12 / governance). Sunset: cuando el
|
|
/// registro OIT real de Mathi (PerceptionAdapter + DID binding) aterrice,
|
|
/// migra a auto-registro attestation-driven. Cross-link futuro: AgentIdentity
|
|
/// (Index 9).
|
|
#[pallet::storage]
|
|
#[pallet::getter(fn authorized_validator_level)]
|
|
pub type AuthorizedValidators<T: Config> =
|
|
StorageMap<_, Blake2_128Concat, [u8; 32], u8, OptionQuery>;
|
|
|
|
/// PROV-7 canonical: Events emisión canonical.
|
|
#[pallet::event]
|
|
#[pallet::generate_deposit(pub(super) fn deposit_event)]
|
|
pub enum Event<T: Config> {
|
|
/// Asset attestation canonical anchored on-chain (PROV-7).
|
|
AssetAttested {
|
|
/// BLAKE3 content hash canonical del asset attested.
|
|
asset_hash: AssetHash,
|
|
/// Block number canonical at which attestation landed.
|
|
block_number: BlockNumberFor<T>,
|
|
},
|
|
/// Attestation rejected canonical con reason canonical (PROV-7).
|
|
AttestationRejected {
|
|
/// BLAKE3 content hash canonical que falló validation.
|
|
asset_hash: AssetHash,
|
|
/// Reason canonical de rejection per RejectionReason enum.
|
|
reason: RejectionReason,
|
|
},
|
|
/// FIX-4: validador Noesis autorizado (Root) con su nivel OIT. Auditable
|
|
/// on-chain: quién quedó habilitado para anclar y a qué nivel.
|
|
ValidatorRegistered {
|
|
/// Ed25519 public key del validador autorizado.
|
|
public_key: [u8; 32],
|
|
/// Nivel OIT asignado por governance.
|
|
level: u8,
|
|
},
|
|
/// FIX-4: validador revocado (Root) — ya no puede anclar.
|
|
ValidatorRevoked {
|
|
/// Ed25519 public key del validador revocado.
|
|
public_key: [u8; 32],
|
|
},
|
|
}
|
|
|
|
/// Rejection reasons canonical PROV-7.
|
|
///
|
|
/// **Marihan refinement Phase 2.2 noted** (DC PROVENANCE_PALLET_V1 signature
|
|
/// `2e6b8469`): añadir variant `MalformedValidationAttestation` para casos
|
|
/// donde `signing_payload()` falle parse pre-Ed25519-verify. Non-blocking
|
|
/// Phase 2.1 — incluido aquí canonical anticipated.
|
|
#[derive(
|
|
Encode,
|
|
Decode,
|
|
DecodeWithMemTracking,
|
|
MaxEncodedLen,
|
|
TypeInfo,
|
|
Clone,
|
|
Copy,
|
|
PartialEq,
|
|
Eq,
|
|
RuntimeDebug,
|
|
)]
|
|
pub enum RejectionReason {
|
|
/// Ed25519 signature verification canonical failed (PROV-4).
|
|
InvalidSignature,
|
|
/// Validator OIT level canonical < 3 (institutional required PROV-5).
|
|
ValidatorTierInsufficient,
|
|
/// Attestation NOT anchorable canonical per is_anchorable check (PROV-6).
|
|
NotAnchorable,
|
|
/// Asset hash canonical already attested previamente (idempotency canonical).
|
|
AlreadyAttested,
|
|
/// Validation attestation canonical malformed (Marihan refinement Phase 2.2).
|
|
MalformedValidationAttestation,
|
|
}
|
|
|
|
/// Pallet errors canonical (PROV-4/5/6).
|
|
#[pallet::error]
|
|
pub enum Error<T> {
|
|
/// PROV-4 canonical: Ed25519 signature verification canonical failed.
|
|
InvalidSignature,
|
|
/// PROV-5 canonical: validator OIT level canonical insufficient (< 3).
|
|
ValidatorTierInsufficient,
|
|
/// PROV-6 canonical: attestation NOT anchorable canonical (verdict/severity).
|
|
NotAnchorable,
|
|
/// PROV-2 canonical: asset hash canonical already attested previamente.
|
|
AlreadyAttested,
|
|
/// Marihan refinement Phase 2.2 noted canonical.
|
|
MalformedValidationAttestation,
|
|
/// FIX-4: la pubkey del firmante NO está en el registro on-chain de
|
|
/// validadores autorizados (cierra el squat — solo validadores
|
|
/// registrados por governance pueden anclar).
|
|
ValidatorNotAuthorized,
|
|
}
|
|
|
|
/// Dispatchables (PROV-3: attest_asset).
|
|
#[pallet::call]
|
|
impl<T: Config> Pallet<T> {
|
|
/// PROV-3 + FIX-4: ancla una attestation Noesis verificada on-chain.
|
|
///
|
|
/// El validador firma OFF-CHAIN (con el helper de Ergon
|
|
/// `attestation_signed_message`) sobre `TAG ‖ genesis_hash ‖ payload`,
|
|
/// donde `payload` es el `SigningPayload` postcard (Slice 7.0r). El
|
|
/// extrinsic recibe esos **bytes postcard opacos** + la firma; el pallet
|
|
/// re-deriva el mensaje y verifica — NUNCA re-serializa (contrato ASP-3
|
|
/// DC `076d15cc`). El `public_key` se DECODIFICA del payload (no es un
|
|
/// parámetro aparte: la firma prueba que esa clave firmó esta attestation
|
|
/// para esta cadena).
|
|
///
|
|
/// Flow (gates baratos antes del crypto caro, patrón FIX-3):
|
|
/// 1. `ensure_signed` — el submitter (relayer/pagador del fee).
|
|
/// 2. Decode postcard → (asset_id, public_key, level, verdict, severity).
|
|
/// 3. **Dedup-by-BLAKE3 con RECHAZO EXPLÍCITO**: la storage key ES el
|
|
/// `asset_id` (= BLAKE3 content hash del asset); un replay del mismo
|
|
/// attestation firmado reusa el mismo asset_id → `AlreadyAttested`.
|
|
/// (Decisión Pilar 4 sesión 6+: replay = rechazo explícito, no dedup
|
|
/// silencioso.) Va ANTES del verify para no gastar crypto en replays.
|
|
/// 4. FIX-4: gate de legitimidad ON-CHAIN — la pubkey del firmante debe
|
|
/// estar en `AuthorizedValidators` (registro governance) con nivel
|
|
/// >= 3. NO se confía en el `level` self-attested del payload (cierra
|
|
/// el squat + tier-forgery).
|
|
/// 5. PROV-6: gate anchorable (verdict Passed|Warning, severity Info|Warning).
|
|
/// 6. FIX-4/V3-C1: Ed25519 verify via `sp_io::crypto::ed25519_verify`
|
|
/// (host function, no bloatea wasm) sobre el mensaje domain-separado
|
|
/// con el `genesis_hash` de ESTA cadena (cierra replay cross-chain).
|
|
/// 7. Insert `AssetAttestation` (creator = validador, block real) + evento.
|
|
#[pallet::call_index(0)]
|
|
#[pallet::weight(T::WeightInfo::attest_asset())]
|
|
pub fn attest_asset(
|
|
origin: OriginFor<T>,
|
|
signing_payload: BoundedVec<u8, MaxPayloadLen>,
|
|
signature: [u8; 64],
|
|
) -> DispatchResult {
|
|
// 1. Origin signed (submitter — paga el fee; el creator semántico es
|
|
// el validador cuya clave está dentro del payload firmado).
|
|
let _submitter = ensure_signed(origin)?;
|
|
|
|
// 2. Decode postcard del SigningPayload real de Ergon (total, DoS-safe).
|
|
let decoded = decode_signing_payload(&signing_payload)
|
|
.map_err(|_| Error::<T>::MalformedValidationAttestation)?;
|
|
let asset_hash = AssetHash::new(decoded.asset_id);
|
|
|
|
// 3. Dedup-by-BLAKE3 (asset_id) — rechazo explícito de replay, antes
|
|
// del crypto (FIX-3 discipline).
|
|
ensure!(
|
|
!Attestations::<T>::contains_key(asset_hash),
|
|
Error::<T>::AlreadyAttested
|
|
);
|
|
|
|
// 4. FIX-4: legitimidad ON-CHAIN (cierra el squat + tier-forgery).
|
|
// La pubkey del firmante DEBE estar en el registro de validadores
|
|
// autorizados; el nivel es el del REGISTRO (governance), NO el
|
|
// `level` self-attested del payload. Sin esto, cualquiera con una
|
|
// clave Ed25519 anclaba basura institucional bajo el hash público
|
|
// de un asset legítimo.
|
|
let authorized_level = AuthorizedValidators::<T>::get(decoded.public_key)
|
|
.ok_or(Error::<T>::ValidatorNotAuthorized)?;
|
|
ensure!(authorized_level >= 3, Error::<T>::ValidatorTierInsufficient);
|
|
|
|
// 5. PROV-6: anchorable (Inmutable #26 — una vez anclado es inmutable).
|
|
ensure!(decoded.is_anchorable(), Error::<T>::NotAnchorable);
|
|
|
|
// 6. FIX-4 (V3-C1): re-derivar el mensaje domain-separado con el
|
|
// genesis_hash de ESTA cadena y verificar Ed25519 sobre los BYTES
|
|
// recibidos (nunca re-serializar). Host function = no wasm bloat.
|
|
let genesis_full = <frame_system::Pallet<T>>::block_hash(BlockNumberFor::<T>::zero());
|
|
let genesis_ref: &[u8] = genesis_full.as_ref();
|
|
let mut genesis_hash = [0u8; 32];
|
|
let n = genesis_ref.len().min(32);
|
|
genesis_hash[..n].copy_from_slice(&genesis_ref[..n]);
|
|
|
|
let message = attestation_signed_message(&signing_payload, &genesis_hash);
|
|
let sig = sp_core::ed25519::Signature::from_raw(signature);
|
|
let pubkey = sp_core::ed25519::Public::from_raw(decoded.public_key);
|
|
ensure!(
|
|
sp_io::crypto::ed25519_verify(&sig, &message, &pubkey),
|
|
Error::<T>::InvalidSignature
|
|
);
|
|
|
|
// 7. Anclar: creator = el validador (su Ed25519 pubkey), block real.
|
|
let block_number = <frame_system::Pallet<T>>::block_number();
|
|
let attestation = AssetAttestation {
|
|
hash: asset_hash,
|
|
creator: origin_chain_types::AccountId::new(decoded.public_key),
|
|
signature: origin_chain_types::Signature::new(signature),
|
|
anchored_at_block: block_number.saturated_into::<u64>(),
|
|
agent_chain: origin_chain_types::AgentChain::default(),
|
|
};
|
|
Attestations::<T>::insert(asset_hash, attestation);
|
|
|
|
Self::deposit_event(Event::AssetAttested { asset_hash, block_number });
|
|
Ok(())
|
|
}
|
|
|
|
/// FIX-4: registra (o re-nivela) un validador Noesis autorizado a anclar.
|
|
/// **Root-only** (sudo Index 12 / governance) — cierra el squat: solo
|
|
/// validadores que governance habilita pueden anclar. Sunset a
|
|
/// attestation-driven cuando el registro OIT de Mathi aterrice.
|
|
#[pallet::call_index(1)]
|
|
#[pallet::weight(T::WeightInfo::register_validator())]
|
|
pub fn register_validator(
|
|
origin: OriginFor<T>,
|
|
public_key: [u8; 32],
|
|
level: u8,
|
|
) -> DispatchResult {
|
|
ensure_root(origin)?;
|
|
AuthorizedValidators::<T>::insert(public_key, level);
|
|
Self::deposit_event(Event::ValidatorRegistered { public_key, level });
|
|
Ok(())
|
|
}
|
|
|
|
/// FIX-4: revoca un validador — ya no puede anclar. **Root-only.**
|
|
#[pallet::call_index(2)]
|
|
#[pallet::weight(T::WeightInfo::revoke_validator())]
|
|
pub fn revoke_validator(
|
|
origin: OriginFor<T>,
|
|
public_key: [u8; 32],
|
|
) -> DispatchResult {
|
|
ensure_root(origin)?;
|
|
AuthorizedValidators::<T>::remove(public_key);
|
|
Self::deposit_event(Event::ValidatorRevoked { public_key });
|
|
Ok(())
|
|
}
|
|
}
|
|
}
|
|
|
|
extern crate alloc;
|
|
|
|
/// Benchmarks canonical (FRAME v2) — solo bajo la feature `runtime-benchmarks`.
|
|
/// Verificados por `impl_benchmark_test_suite!` en el mock (ver `benchmarking.rs`).
|
|
#[cfg(feature = "runtime-benchmarks")]
|
|
mod benchmarking;
|
|
|
|
// v0.0.18 — Mock runtime canonical + integration tests E2E PROV-3 flow.
|
|
// Per audit canonical sesión 3+ (`AUDIT_CHARON_SESION_3_STATE_V1.md`),
|
|
// own fixtures basados en vendor copy ValidationAttestation real existente
|
|
// (no depender de Ergon E2E fixture diary claim aspirational).
|
|
#[cfg(test)]
|
|
pub mod mock;
|
|
|
|
#[cfg(test)]
|
|
mod tests_integration;
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
// FIX-4: la verificación Ed25519 real (sp_io host function sobre el mensaje
|
|
// domain-separado, con golden vectors del oráculo de Ergon) vive en
|
|
// `tests_integration`. El decoder postcard + el mensaje firmado se prueban
|
|
// en `origin-chain-types::attestation_verify`. Aquí quedan compile-checks.
|
|
|
|
#[test]
|
|
fn pallet_compiles_canonical() {
|
|
// Verified by cargo check. E2E del fee-path verify = tests_integration.
|
|
}
|
|
|
|
#[test]
|
|
fn rejection_reason_variants_canonical() {
|
|
// Verify enum variants canonical exist + Marihan refinement included.
|
|
let r1 = RejectionReason::InvalidSignature;
|
|
let r2 = RejectionReason::ValidatorTierInsufficient;
|
|
let _r3 = RejectionReason::NotAnchorable;
|
|
let r4 = RejectionReason::AlreadyAttested;
|
|
let r5 = RejectionReason::MalformedValidationAttestation;
|
|
assert_ne!(
|
|
core::mem::discriminant(&r1),
|
|
core::mem::discriminant(&r2)
|
|
);
|
|
assert_ne!(
|
|
core::mem::discriminant(&r4),
|
|
core::mem::discriminant(&r5)
|
|
);
|
|
}
|
|
}
|