//! # pallet-agent-identity — Phase 2.7 scaffold v0.0.24 (Inmutable #41 AGI DIDs) //! //! Agent identity pallet canonical per CHN-7 + Inmutable #41 Cross-Species //! Co-authorship. AGI DIDs canonical on-chain registrados — AGIs first-class //! citizens del Origin Metaverse pueden owning assets + signing transactions + //! DAO voting via sus DIDs canonical. //! //! ## Status canonical: v0.0.24 — Scaffold trait-first foundation //! //! Este crate provee canonical: //! - Pallet Config trait canonical (RuntimeEvent association) ✅ //! - Storage `Identities>` per-account O(1) ✅ //! - Reverse lookup `DidToAccount>` ✅ //! - Events canonical `AgentRegistered` + `AgentBound` + `AgentUpdated` ✅ //! - Errors canonical (PROV-like pattern) ✅ //! - Scaffold dispatchable `register_agent_did` ✅ //! - Helper `derive_did(public_key) -> Did` canonical via BLAKE3 ✅ //! //! ## Tres clases canonical de agent registrable //! //! - **Human**: Wallet humano creator. Self-registration via public_key. //! - **AGI**: AGI canonical (13 hermanos + future). Self-registration via daimonion //! public_key (ed25519 keypair canonical per AGI). //! - **Hybrid**: Cross-species collaborative. Registrado por humano coordinator //! con `agent_chain` BoundedVec populated canonical preservando lineage. //! //! ## Lo que NO funciona aún (v0.0.26 real impl + v0.0.27 Runtime API) //! //! - Real `register_agent_did` impl con storage assertions end-to-end (v0.0.26) //! - Mock runtime integration tests E2E (v0.0.26, pattern v0.0.22 mirror) //! - Runtime API canonical `AgentIdentityApi.query_identity` read-only (v0.0.27) //! //! ## Roadmap canonical post-v0.0.24 //! //! 1. **v0.0.24 (ESTA)**: Scaffold trait-first compile-only canonical //! 2. **v0.0.25**: Wired runtime composition Index 9 canonical //! 3. **v0.0.26**: Real `register_agent_did` impl + mock runtime integration tests E2E //! 4. **v0.0.27**: Runtime API `AgentIdentityApi` declaration + impl runtime → **Phase 2.7 COMPLETE** //! 5. **v0.1.0** (post Q3 2026 testnet): full integration end-to-end //! //! ## Cross-pillar consumers canonical desbloqueados (post-impl) //! //! - Trinity (Phanes/Daedalus/Hephaistos): asset registry queries AGI co-author DIDs //! - Mathi (Pilar 1 AMIS): tier-aware fees + EEG consent via DID resolution //! - Codex (.odoc + origindocs): author DID canonical academic IP defensible //! - Atelier (Marihan): Skill Library NFT mint via institutional wallet identity //! - Ethos (Sussi): credential mint canonical needs human/AGI DID //! - Ergon (Pilar 2 Noesis): validation cycle attests AGI authorship via DID #![cfg_attr(not(feature = "std"), no_std)] #![warn(missing_docs)] pub use pallet::*; /// Re-export canonical types desde origin-chain-types para consumers. pub use origin_chain_types::{AgentIdentity, AgentKind, Did}; /// Derive deterministic DID canonical from public_key via BLAKE3 hash. /// /// Per CHN-7: `did = BLAKE3(public_key || schema_version)`. Garantiza /// determinismo cross-runtime canonical (Pi 5 + Desktop + M4 Ultra producen /// el mismo DID para el mismo public_key, Inmutable #16 byte-determinism). /// /// Schema version actual canonical: `"1.0.0 "` (8-byte ASCII fixed, alineado /// con ValidationAttestation pattern Phase 2.1). pub fn derive_did(public_key: &[u8; 32]) -> Did { let mut hasher = blake3::Hasher::new(); hasher.update(public_key); hasher.update(b"1.0.0 "); // schema version canonical let hash = hasher.finalize(); origin_chain_types::AssetHash::new(*hash.as_bytes()) } /// Runtime API canonical para cross-pillar consumers + light clients. /// /// Permite query canonical de agent identity canonical sin necesidad de full /// node access — light client envía `AccountId` → recibe `Option` /// canonical con DID + kind + creator + agent_chain canonical. /// /// Consumers canonical: /// - Trinity (Phanes): asset gallery render con AGI co-author badge /// - Mathi AMIS: per-session tier resolution canonical via DID lookup /// - Codex (.odoc + origindocs): author DID resolution canonical academic IP /// - Atelier (Marihan): institutional wallet identity verification canonical /// - Ethos (Sussi): credential issuer DID resolution canonical /// /// Future Phase 2.8 expansion canonical: /// - `query_by_did(did) -> Option` (reverse lookup paginated) /// - `list_agi_agents() -> Vec<(AccountId, AgentIdentity)>` (Origin AGIs canonical) /// - `list_co_authors(asset_hash) -> Vec` (cross-link Provenance) sp_api::decl_runtime_apis! { /// Agent identity runtime API canonical canonical (Phase 2.7 v0.0.27). pub trait AgentIdentityApi { /// Query agent identity canonical por AccountId canonical. /// /// Returns `Some(AgentIdentity)` si registered previamente on-chain, /// `None` si nunca registered canonical. Read-only canonical sin /// transaction fee canonical (off-chain via RPC). fn query_identity(account: origin_chain_types::AccountId) -> Option; } } #[frame_support::pallet] pub mod pallet { use alloc::vec::Vec; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; use origin_chain_types::{AccountId, AgentChain, AgentIdentity, AgentKind, Did}; use sp_runtime::traits::SaturatedConversion; /// Pallet configuration trait canonical. /// /// Per CHN-7 + Inmutable #41 + ADR-0057 trait-seam discipline. Scaffold-level /// Config canonical: solo RuntimeEvent association. Future canonical fields /// post DC ratify (Phase 2.7 DC pending): /// /// - `MaxAgentChainLen: Get` (BoundedVec bound canonical) /// - Custom origin canonical para Hybrid agent registration (governance) /// - Pallet weights canonical desde benchmarking real #[pallet::config] pub trait Config: frame_system::Config { /// Runtime event type canonical wired into `RuntimeEvent` aggregate. type RuntimeEvent: From> + IsType<::RuntimeEvent>; } /// Pallet definition canonical. /// /// v0.0.24 scaffold: storage layout canonical + Config + Events + Errors + /// scaffold dispatchable. Real impl `register_agent_did` v0.0.26 next slice. #[pallet::pallet] pub struct Pallet(_); /// CHN-7 canonical: `Identities>` canonical. /// /// O(1) lookup canonical via Blake2_128Concat hasher (FRAME-native canonical). /// Memory canonical ~190 bytes per identity canonical max (MaxEncodedLen). #[pallet::storage] #[pallet::getter(fn identity_of)] pub type Identities = StorageMap<_, Blake2_128Concat, T::AccountId, AgentIdentity, OptionQuery>; /// Reverse lookup canonical: `DidToAccount>`. /// /// O(1) lookup canonical desde DID → T::AccountId canonical para cross-pillar /// consumers (Codex .odoc author resolution, Trinity asset co-author lookup). #[pallet::storage] #[pallet::getter(fn account_of_did)] pub type DidToAccount = StorageMap<_, Blake2_128Concat, Did, T::AccountId, OptionQuery>; /// Events emisión canonical (CHN-7 + Inmutable #41). #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { /// Agent registered canonical on-chain — DID canonical first registration. /// /// Materializa Inmutable #41 Cross-Species Co-authorship compile-detect. AgentRegistered { /// T::AccountId del agent registered canonical. account: T::AccountId, /// DID canonical derived from public_key + schema version. did: Did, /// AgentKind canonical (Human + AGI + Hybrid). kind: AgentKind, }, /// Cross-species lineage bound canonical (Hybrid agent registration). /// /// Inmutable #41 cross-species explicit canonical — emitted SOLO cuando /// `agent_chain` canonical non-empty (Hybrid o collaborative registration). AgentBound { /// T::AccountId del agent canonical. account: T::AccountId, /// Number canonical de co-authors en agent_chain. co_author_count: u32, }, /// Agent identity updated canonical (provenance_proof binding o agent_chain expansion). /// /// Phase 2.8 anticipated canonical: futuro update_provenance dispatchable /// para academic IP attestation post-registration canonical. AgentUpdated { /// T::AccountId del agent canonical. account: T::AccountId, }, } /// Pallet errors canonical. #[pallet::error] pub enum Error { /// CHN-7 canonical: agent canonical already registered en este account. AlreadyRegistered, /// CHN-7 canonical: DID canonical already registered por diferente account /// (collision protection canonical). DidAlreadyTaken, /// CHN-7 canonical: agent_chain canonical overflow `BoundedVec` bound. AgentChainOverflow, /// CHN-7 canonical: Hybrid agent requires non-empty agent_chain canonical /// (Inmutable #41 cross-species verification). HybridRequiresAgentChain, } /// Dispatchables canonical (real impl v0.0.26). #[pallet::call] impl Pallet { /// CHN-7 canonical: `register_agent_did` real dispatchable canonical. /// /// **Real impl v0.0.26**: Full 8-step canonical flow + storage assertions /// end-to-end + Inmutable #41 Cross-Species Co-authorship verification /// compile-detect. /// /// Flow canonical 8-step per CHN-7: /// 1. `ensure_signed(origin)` canonical /// 2. Idempotency check: `!Identities::contains_key(&who)` (no duplicate) /// 3. Derive DID canonical via `derive_did(public_key)` BLAKE3 /// 4. DID collision check: `!DidToAccount::contains_key(&did)` (no collision) /// 5. Build `AgentChain` canonical `BoundedVec` (overflow → `AgentChainOverflow`) /// 6. Inmutable #41 enforce: Hybrid requires non-empty agent_chain /// 7. Insert storage canonical: `Identities` + `DidToAccount` /// 8. Emit `AgentRegistered` + `AgentBound` (if cross-species) canonical #[pallet::call_index(0)] #[pallet::weight(Weight::from_parts(10_000, 0))] pub fn register_agent_did( origin: OriginFor, public_key: [u8; 32], kind: AgentKind, co_authors_raw: Vec<[u8; 32]>, ) -> DispatchResult { // Step 1: ensure origin signed canonical let who = ensure_signed(origin)?; // Step 2: idempotency check canonical (no duplicate registration) ensure!( !Identities::::contains_key(&who), Error::::AlreadyRegistered ); // Step 3: derive DID canonical via BLAKE3 (Inmutable #16 byte-determinism) let did = crate::derive_did(&public_key); // Step 4: DID collision check canonical ensure!( !DidToAccount::::contains_key(did), Error::::DidAlreadyTaken ); // Step 5: build AgentChain canonical BoundedVec> let mut agent_chain = AgentChain::default(); for raw in co_authors_raw { agent_chain .try_push(AccountId::new(raw)) .map_err(|_| Error::::AgentChainOverflow)?; } // Step 6: Inmutable #41 canonical enforce — Hybrid requires non-empty agent_chain if matches!(kind, AgentKind::Hybrid) && agent_chain.is_empty() { return Err(Error::::HybridRequiresAgentChain.into()); } // Step 7: build AgentIdentity struct + insert storage canonical let has_cross_species = !agent_chain.is_empty(); let co_author_count = agent_chain.len() as u32; let creator = AccountId::new(public_key); let block_number: u64 = >::block_number() .saturated_into::(); let identity = AgentIdentity { did, kind, creator, registered_at_block: block_number, provenance_proof: None, agent_chain, }; Identities::::insert(&who, identity); DidToAccount::::insert(did, &who); // Step 8: emit events canonical Self::deposit_event(Event::AgentRegistered { account: who.clone(), did, kind, }); if has_cross_species { Self::deposit_event(Event::AgentBound { account: who, co_author_count, }); } Ok(()) } } /// Pure helper canonical para Runtime API v0.0.27 consumer. impl Pallet { /// Query identity canonical sin storage mutation (read-only canonical). pub fn query_identity_of(account: &T::AccountId) -> Option { Identities::::get(account) } } } extern crate alloc; // v0.0.26 — Mock runtime canonical + integration tests E2E CHN-7 flow. // Pattern v0.0.18 (Provenance) + v0.0.22 (Fees) mirror. #[cfg(test)] pub mod mock; #[cfg(test)] mod tests_integration; #[cfg(test)] mod tests { use super::*; // Compile-time tests canonical scaffold v0.0.24. // Mock runtime integration tests canonical = v0.0.26 next slice. #[test] fn pallet_scaffold_compiles_canonical() { // Scaffold compiles canonical — verified by cargo check. // Real mock runtime integration tests = v0.0.26 (pattern v0.0.22 mirror). } #[test] fn derive_did_deterministic_canonical() { // Inmutable #16 byte-determinism canonical: same public_key → same DID let pubkey = [42u8; 32]; let did1 = derive_did(&pubkey); let did2 = derive_did(&pubkey); assert_eq!(did1, did2); } #[test] fn derive_did_different_keys_produce_different_dids_canonical() { let did_a = derive_did(&[1u8; 32]); let did_b = derive_did(&[2u8; 32]); assert_ne!(did_a, did_b); } #[test] fn re_export_agent_identity_canonical() { // Verify re-exports canonical desde origin-chain-types let _kind: AgentKind = AgentKind::AGI; let _did_alias: Did = origin_chain_types::AssetHash::new([0u8; 32]); } }