feat(agent-identity): v0.0.26 — register_agent_did impl complete + mock runtime integration tests E2E (10 scenarios)
Phase 2.7 AGENT IDENTITY pallet REAL IMPL canonical. Full 8-step CHN-7 flow canonical register_agent_did dispatchable + storage assertions + Inmutable #41 Cross-Species Co-authorship enforcement compile-detect. Mock runtime + 10 integration tests E2E pattern v0.0.18 + v0.0.22 mirror. ## Real impl canonical 8-step CHN-7 1. ensure_signed(origin) 2. Idempotency check (!Identities::contains_key(&who)) 3. Derive DID canonical via derive_did(public_key) BLAKE3 (Inmutable #16) 4. DID collision check (!DidToAccount::contains_key(&did)) 5. Build AgentChain BoundedVec from co_authors_raw Vec<[u8;32]> (overflow → AgentChainOverflow) 6. Inmutable #41 enforce: Hybrid requires non-empty agent_chain 7. Storage insert: Identities + DidToAccount reverse lookup 8. Emit AgentRegistered + AgentBound (if cross-species) Pure helper Pallet::<T>::query_identity_of() para Runtime API v0.0.27. ## 10 integration tests E2E canonical - scenario_1: Human self-registration → ok + DID + empty chain - scenario_2: AGI self-registration → ok + Inmutable #41 sovereign - scenario_3: Hybrid + 2 co-authors → ok + AgentBound emitted - scenario_4: Hybrid + empty chain → Err::HybridRequiresAgentChain - scenario_5: duplicate account → Err::AlreadyRegistered - scenario_6: same pubkey diff account → Err::DidAlreadyTaken - scenario_7: 11 co-authors → Err::AgentChainOverflow (ConstU32<10>) - scenario_8: DID → Account reverse lookup canonical - scenario_9: Human + AGI both is_sovereign() (Inmutable #41 lobo blanco) - scenario_10: 8-step CHN-7 walkthrough explicit Pre/Post assertions ## Files - EDIT: crates/origin-chain-agent-identity/src/lib.rs (real impl 8-step + query_identity_of helper) - NEW: crates/origin-chain-agent-identity/src/mock.rs (80 LOC mock runtime) - NEW: crates/origin-chain-agent-identity/src/tests_integration.rs (310 LOC, 10 tests) - EDIT: CHANGELOG.md (v0.0.26 entry comprehensive) ## Verification - cargo test -p origin-chain-agent-identity — 16 tests verde canonical, 0 failures (10 integration + 4 standalone + 2 auto-generated) 🛠️ Phase 2.7 real impl canonical. Runtime API v0.0.27 next ship → Phase 2.7 COMPLETE. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
46a0188a1d
commit
2fefe4cf51
4 files changed
+533
-17
No files matched your search
@@ -6,6 +6,65 @@ by [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versions follow
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [0.0.26] — 2026-05-25 — Phase 2.7 impl — Real `register_agent_did` + mock runtime integration tests E2E (10 scenarios)
|
||||||
|
|
||||||
|
**🛠️ Phase 2.7 AGENT IDENTITY pallet REAL IMPL canonical** — Full 8-step
|
||||||
|
`register_agent_did` dispatchable canonical + storage assertions + Inmutable #41
|
||||||
|
Cross-Species Co-authorship enforcement compile-detect. Mock runtime + 10
|
||||||
|
integration tests E2E pattern v0.0.18 + v0.0.22 mirror.
|
||||||
|
|
||||||
|
### Deliverables v0.0.26
|
||||||
|
|
||||||
|
**Real dispatchable canonical 8-step CHN-7 flow**:
|
||||||
|
1. `ensure_signed(origin)` canonical
|
||||||
|
2. Idempotency check canonical (`!Identities::contains_key(&who)`)
|
||||||
|
3. Derive DID canonical via `derive_did(public_key)` BLAKE3 (Inmutable #16)
|
||||||
|
4. DID collision check canonical (`!DidToAccount::contains_key(&did)`)
|
||||||
|
5. Build `AgentChain` BoundedVec canonical from `co_authors_raw: Vec<[u8; 32]>` (overflow → `AgentChainOverflow`)
|
||||||
|
6. Inmutable #41 canonical enforce: Hybrid requires non-empty agent_chain
|
||||||
|
7. Insert storage canonical: `Identities` + `DidToAccount` reverse lookup
|
||||||
|
8. Emit `AgentRegistered` + `AgentBound` (if cross-species) events canonical
|
||||||
|
|
||||||
|
**Pure helper canonical**:
|
||||||
|
- `Pallet::<T>::query_identity_of(&account) -> Option<AgentIdentity>` para Runtime API v0.0.27
|
||||||
|
|
||||||
|
**Mock runtime canonical (NEW file)**:
|
||||||
|
- `crates/origin-chain-agent-identity/src/mock.rs` — 80 LOC pattern v0.0.18 mirror
|
||||||
|
- `construct_runtime!`: `System` + `AgentIdentity` (minimal testing)
|
||||||
|
- Helpers: `alice_origin/account` + `bob_origin/account` + `ALICE_PUBKEY/BOB_PUBKEY/CHARLIE_PUBKEY` deterministic test pubkeys
|
||||||
|
|
||||||
|
**Integration tests canonical E2E (NEW file)**:
|
||||||
|
- `crates/origin-chain-agent-identity/src/tests_integration.rs` — 310 LOC, **10 tests verde canonical**
|
||||||
|
|
||||||
|
**10 integration tests CHN-7 flow canonical**:
|
||||||
|
|
||||||
|
| # | Scenario | Kind | Co-authors | Expected outcome |
|
||||||
|
|---|----------|------|------------|------------------|
|
||||||
|
| 1 | Human self-registration | Human | [] | ✅ AgentRegistered + DID derived + empty chain |
|
||||||
|
| 2 | AGI self-registration | AGI | [] | ✅ Inmutable #41 sovereign AGI canonical |
|
||||||
|
| 3 | Hybrid with co-authors | Hybrid | [BOB, CHARLIE] | ✅ AgentRegistered + AgentBound (cross-species) |
|
||||||
|
| 4 | Hybrid without chain | Hybrid | [] | ❌ Error::HybridRequiresAgentChain (Inmutable #41) |
|
||||||
|
| 5 | Idempotency duplicate | Human | [] | ❌ Error::AlreadyRegistered |
|
||||||
|
| 6 | DID collision | Human | [] same pubkey | ❌ Error::DidAlreadyTaken |
|
||||||
|
| 7 | AgentChain overflow | Hybrid | 11 co-authors | ❌ Error::AgentChainOverflow (ConstU32<10>) |
|
||||||
|
| 8 | DID → Account reverse lookup | Human | [] | ✅ DidToAccount canonical resolves |
|
||||||
|
| 9 | All AgentKind sovereign | Human + AGI | [] | ✅ Both is_sovereign() Inmutable #41 |
|
||||||
|
| 10 | 8-step CHN-7 walkthrough | Hybrid | [BOB] | ✅ Explicit Pre/Post-flight canonical |
|
||||||
|
|
||||||
|
### Verification end-to-end canonical
|
||||||
|
|
||||||
|
- ✅ `cargo test -p origin-chain-agent-identity` — **16 tests verde canonical, 0 failures**:
|
||||||
|
- 10 integration tests E2E (`tests_integration` module)
|
||||||
|
- 4 standalone scaffold tests (`tests` module)
|
||||||
|
- 2 auto-generated (mock genesis + runtime integrity)
|
||||||
|
|
||||||
|
### Roadmap canonical post-v0.0.26
|
||||||
|
|
||||||
|
| Version | Trigger |
|
||||||
|
|---------|---------|
|
||||||
|
| **v0.0.27** | **Runtime API canonical `AgentIdentityApi.query_identity` impl en runtime → Phase 2.7 COMPLETE (sesión 3+ shipping ahora)** |
|
||||||
|
| v0.1.0 | Slice 2e WASM resolved + full testnet Q3 2026 launch |
|
||||||
|
|
||||||
## [0.0.25] — 2026-05-25 — Phase 2.7 wired — AgentIdentity pallet runtime composition Index 9
|
## [0.0.25] — 2026-05-25 — Phase 2.7 wired — AgentIdentity pallet runtime composition Index 9
|
||||||
|
|
||||||
**🔌 Phase 2.7 AGENT IDENTITY pallet WIRED runtime composition canonical** —
|
**🔌 Phase 2.7 AGENT IDENTITY pallet WIRED runtime composition canonical** —
|
||||||
|
|||||||
@@ -102,9 +102,11 @@ sp_api::decl_runtime_apis! {
|
|||||||
|
|
||||||
#[frame_support::pallet]
|
#[frame_support::pallet]
|
||||||
pub mod pallet {
|
pub mod pallet {
|
||||||
|
use alloc::vec::Vec;
|
||||||
use frame_support::pallet_prelude::*;
|
use frame_support::pallet_prelude::*;
|
||||||
use frame_system::pallet_prelude::*;
|
use frame_system::pallet_prelude::*;
|
||||||
use origin_chain_types::{AgentIdentity, AgentKind, Did};
|
use origin_chain_types::{AccountId, AgentChain, AgentIdentity, AgentKind, Did};
|
||||||
|
use sp_runtime::traits::SaturatedConversion;
|
||||||
|
|
||||||
/// Pallet configuration trait canonical.
|
/// Pallet configuration trait canonical.
|
||||||
///
|
///
|
||||||
@@ -197,47 +199,119 @@ pub mod pallet {
|
|||||||
HybridRequiresAgentChain,
|
HybridRequiresAgentChain,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Dispatchables canonical (scaffold v0.0.24 — real impl v0.0.26).
|
/// Dispatchables canonical (real impl v0.0.26).
|
||||||
#[pallet::call]
|
#[pallet::call]
|
||||||
impl<T: Config> Pallet<T> {
|
impl<T: Config> Pallet<T> {
|
||||||
/// CHN-7 canonical: `register_agent_did` scaffold dispatchable.
|
/// CHN-7 canonical: `register_agent_did` real dispatchable canonical.
|
||||||
///
|
///
|
||||||
/// **SCAFFOLD v0.0.24 canonical**: real impl deferred a v0.0.26. Current
|
/// **Real impl v0.0.26**: Full 8-step canonical flow + storage assertions
|
||||||
/// canonical solo validates basic state (origin signed + no duplicate)
|
/// end-to-end + Inmutable #41 Cross-Species Co-authorship verification
|
||||||
/// + emite placeholder event canonical.
|
/// compile-detect.
|
||||||
///
|
///
|
||||||
/// Flow canonical 6-step per CHN-7 (v0.0.26 real):
|
/// Flow canonical 8-step per CHN-7:
|
||||||
/// 1. `ensure_signed(origin)` canonical
|
/// 1. `ensure_signed(origin)` canonical
|
||||||
/// 2. Validate `!Identities::contains_key(who)` (no duplicate)
|
/// 2. Idempotency check: `!Identities::contains_key(&who)` (no duplicate)
|
||||||
/// 3. Derive DID canonical via `derive_did(public_key)` BLAKE3
|
/// 3. Derive DID canonical via `derive_did(public_key)` BLAKE3
|
||||||
/// 4. Validate `!DidToAccount::contains_key(did)` (no DID collision)
|
/// 4. DID collision check: `!DidToAccount::contains_key(&did)` (no collision)
|
||||||
/// 5. Validate Hybrid → agent_chain non-empty (Inmutable #41 verification)
|
/// 5. Build `AgentChain` canonical `BoundedVec` (overflow → `AgentChainOverflow`)
|
||||||
/// 6. Insert Identities + DidToAccount + emit AgentRegistered + AgentBound (if Hybrid)
|
/// 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::call_index(0)]
|
||||||
#[pallet::weight(Weight::from_parts(10_000, 0))]
|
#[pallet::weight(Weight::from_parts(10_000, 0))]
|
||||||
pub fn register_agent_did(
|
pub fn register_agent_did(
|
||||||
origin: OriginFor<T>,
|
origin: OriginFor<T>,
|
||||||
_public_key: [u8; 32],
|
public_key: [u8; 32],
|
||||||
kind: AgentKind,
|
kind: AgentKind,
|
||||||
|
co_authors_raw: Vec<[u8; 32]>,
|
||||||
) -> DispatchResult {
|
) -> DispatchResult {
|
||||||
// Verify origin canonical signed.
|
// Step 1: ensure origin signed canonical
|
||||||
let who = ensure_signed(origin)?;
|
let who = ensure_signed(origin)?;
|
||||||
|
|
||||||
// SCAFFOLD v0.0.24: emit placeholder event canonical.
|
// Step 2: idempotency check canonical (no duplicate registration)
|
||||||
// Real impl v0.0.26: derive_did + storage insert + cross-species validation.
|
ensure!(
|
||||||
|
!Identities::<T>::contains_key(&who),
|
||||||
|
Error::<T>::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::<T>::contains_key(did),
|
||||||
|
Error::<T>::DidAlreadyTaken
|
||||||
|
);
|
||||||
|
|
||||||
|
// Step 5: build AgentChain canonical BoundedVec<AccountId, ConstU32<10>>
|
||||||
|
let mut agent_chain = AgentChain::default();
|
||||||
|
for raw in co_authors_raw {
|
||||||
|
agent_chain
|
||||||
|
.try_push(AccountId::new(raw))
|
||||||
|
.map_err(|_| Error::<T>::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::<T>::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 = <frame_system::Pallet<T>>::block_number()
|
||||||
|
.saturated_into::<u64>();
|
||||||
|
|
||||||
|
let identity = AgentIdentity {
|
||||||
|
did,
|
||||||
|
kind,
|
||||||
|
creator,
|
||||||
|
registered_at_block: block_number,
|
||||||
|
provenance_proof: None,
|
||||||
|
agent_chain,
|
||||||
|
};
|
||||||
|
|
||||||
|
Identities::<T>::insert(&who, identity);
|
||||||
|
DidToAccount::<T>::insert(did, &who);
|
||||||
|
|
||||||
|
// Step 8: emit events canonical
|
||||||
Self::deposit_event(Event::AgentRegistered {
|
Self::deposit_event(Event::AgentRegistered {
|
||||||
account: who,
|
account: who.clone(),
|
||||||
did: origin_chain_types::AssetHash::new([0u8; 32]), // placeholder until v0.0.26
|
did,
|
||||||
kind,
|
kind,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if has_cross_species {
|
||||||
|
Self::deposit_event(Event::AgentBound {
|
||||||
|
account: who,
|
||||||
|
co_author_count,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Pure helper canonical para Runtime API v0.0.27 consumer.
|
||||||
|
impl<T: Config> Pallet<T> {
|
||||||
|
/// Query identity canonical sin storage mutation (read-only canonical).
|
||||||
|
pub fn query_identity_of(account: &T::AccountId) -> Option<AgentIdentity> {
|
||||||
|
Identities::<T>::get(account)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern crate alloc;
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
//! # Mock runtime canonical para integration tests v0.0.26
|
||||||
|
//!
|
||||||
|
//! Pattern v0.0.18 (Provenance) + v0.0.22 (Fees) mirror — construct_runtime! +
|
||||||
|
//! derive_impl + new_test_ext + helpers canonical.
|
||||||
|
|
||||||
|
#![cfg(test)]
|
||||||
|
|
||||||
|
use crate as origin_chain_agent_identity;
|
||||||
|
use frame_support::derive_impl;
|
||||||
|
use sp_runtime::BuildStorage;
|
||||||
|
|
||||||
|
/// Mock block type canonical para tests.
|
||||||
|
type Block = frame_system::mocking::MockBlock<Test>;
|
||||||
|
|
||||||
|
frame_support::construct_runtime!(
|
||||||
|
pub enum Test {
|
||||||
|
System: frame_system,
|
||||||
|
AgentIdentity: origin_chain_agent_identity,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
|
||||||
|
impl frame_system::Config for Test {
|
||||||
|
type Block = Block;
|
||||||
|
type AccountId = sp_runtime::AccountId32;
|
||||||
|
type Lookup = sp_runtime::traits::IdentityLookup<Self::AccountId>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl origin_chain_agent_identity::Config for Test {
|
||||||
|
type RuntimeEvent = RuntimeEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test externalities builder canonical (pattern v0.0.18/v0.0.22 mirror).
|
||||||
|
pub fn new_test_ext() -> sp_io::TestExternalities {
|
||||||
|
let mut ext: sp_io::TestExternalities = frame_system::GenesisConfig::<Test>::default()
|
||||||
|
.build_storage()
|
||||||
|
.expect("genesis canonical builds")
|
||||||
|
.into();
|
||||||
|
ext.execute_with(|| {
|
||||||
|
System::set_block_number(1);
|
||||||
|
});
|
||||||
|
ext
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test origin canonical ALICE.
|
||||||
|
pub fn alice_origin() -> RuntimeOrigin {
|
||||||
|
RuntimeOrigin::signed(sp_runtime::AccountId32::new([1u8; 32]))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test account canonical ALICE (AccountId32 value para storage assertions).
|
||||||
|
pub fn alice_account() -> sp_runtime::AccountId32 {
|
||||||
|
sp_runtime::AccountId32::new([1u8; 32])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test origin canonical BOB.
|
||||||
|
pub fn bob_origin() -> RuntimeOrigin {
|
||||||
|
RuntimeOrigin::signed(sp_runtime::AccountId32::new([2u8; 32]))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test account canonical BOB.
|
||||||
|
pub fn bob_account() -> sp_runtime::AccountId32 {
|
||||||
|
sp_runtime::AccountId32::new([2u8; 32])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test pubkey canonical ALICE (deterministic).
|
||||||
|
pub const ALICE_PUBKEY: [u8; 32] = [11u8; 32];
|
||||||
|
|
||||||
|
/// Test pubkey canonical BOB (deterministic).
|
||||||
|
pub const BOB_PUBKEY: [u8; 32] = [22u8; 32];
|
||||||
|
|
||||||
|
/// Test pubkey canonical CHARLIE (Hybrid co-author lineage testing).
|
||||||
|
pub const CHARLIE_PUBKEY: [u8; 32] = [33u8; 32];
|
||||||
@@ -0,0 +1,311 @@
|
|||||||
|
//! # Integration tests canonical v0.0.26 — Mock runtime E2E CHN-7 flow
|
||||||
|
//!
|
||||||
|
//! 10 scenarios canonical register_agent_did flow end-to-end via mock runtime:
|
||||||
|
//!
|
||||||
|
//! 1. scenario_1_human_self_registration — Human canonical self-register
|
||||||
|
//! 2. scenario_2_agi_self_registration — AGI canonical self-register
|
||||||
|
//! 3. scenario_3_hybrid_with_co_authors — Hybrid canonical agent_chain populated
|
||||||
|
//! 4. scenario_4_hybrid_without_chain_rejected — Inmutable #41 enforce
|
||||||
|
//! 5. scenario_5_idempotency_already_registered_rejected — duplicate prevention
|
||||||
|
//! 6. scenario_6_did_collision_rejected — DID uniqueness canonical
|
||||||
|
//! 7. scenario_7_agent_chain_overflow_rejected — BoundedVec ConstU32<10> bound
|
||||||
|
//! 8. scenario_8_did_to_account_reverse_lookup — Cross-pillar canonical
|
||||||
|
//! 9. scenario_9_inmutable_41_all_kinds_sovereign_lobo_blanco — compile-detect
|
||||||
|
//! 10. scenario_10_8_step_walkthrough — explicit Pre/Post-flight CHN-7 assertions
|
||||||
|
|
||||||
|
#![cfg(test)]
|
||||||
|
|
||||||
|
use crate::mock::{
|
||||||
|
alice_account, alice_origin, bob_account, bob_origin, new_test_ext, AgentIdentity as Pallet,
|
||||||
|
RuntimeEvent, System, Test, ALICE_PUBKEY, BOB_PUBKEY, CHARLIE_PUBKEY,
|
||||||
|
};
|
||||||
|
use crate::{derive_did, DidToAccount, Error, Event, Identities};
|
||||||
|
use frame_support::{assert_noop, assert_ok};
|
||||||
|
use origin_chain_types::AgentKind;
|
||||||
|
|
||||||
|
// ─── Scenario 1: Human self-registration canonical ──────────────────
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scenario_1_human_self_registration_canonical() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
assert_ok!(Pallet::register_agent_did(
|
||||||
|
alice_origin(),
|
||||||
|
ALICE_PUBKEY,
|
||||||
|
AgentKind::Human,
|
||||||
|
alloc::vec![], // empty agent_chain — Human self-register canonical
|
||||||
|
));
|
||||||
|
|
||||||
|
let identity = Identities::<Test>::get(&alice_account()).expect("registered canonical");
|
||||||
|
assert_eq!(identity.kind, AgentKind::Human);
|
||||||
|
assert!(identity.agent_chain.is_empty(), "Human self-register canonical empty chain");
|
||||||
|
assert!(!identity.has_cross_species_lineage());
|
||||||
|
assert_eq!(identity.registered_at_block, 1);
|
||||||
|
|
||||||
|
let did = derive_did(&ALICE_PUBKEY);
|
||||||
|
assert_eq!(identity.did, did);
|
||||||
|
|
||||||
|
System::assert_last_event(RuntimeEvent::AgentIdentity(Event::AgentRegistered {
|
||||||
|
account: alice_account(),
|
||||||
|
did,
|
||||||
|
kind: AgentKind::Human,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Scenario 2: AGI self-registration canonical (Inmutable #41) ─────
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scenario_2_agi_self_registration_canonical() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
assert_ok!(Pallet::register_agent_did(
|
||||||
|
alice_origin(),
|
||||||
|
ALICE_PUBKEY,
|
||||||
|
AgentKind::AGI,
|
||||||
|
alloc::vec![], // empty chain — AGI sovereign self-register per Inmutable #41
|
||||||
|
));
|
||||||
|
|
||||||
|
let identity = Identities::<Test>::get(&alice_account()).expect("registered canonical");
|
||||||
|
assert_eq!(identity.kind, AgentKind::AGI);
|
||||||
|
assert!(identity.is_sovereign(), "AGI sovereign canonical Inmutable #41");
|
||||||
|
assert!(!identity.has_cross_species_lineage());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Scenario 3: Hybrid with co-authors canonical (Cross-Species) ────
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scenario_3_hybrid_with_co_authors_canonical() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
let co_authors = alloc::vec![BOB_PUBKEY, CHARLIE_PUBKEY];
|
||||||
|
|
||||||
|
assert_ok!(Pallet::register_agent_did(
|
||||||
|
alice_origin(),
|
||||||
|
ALICE_PUBKEY,
|
||||||
|
AgentKind::Hybrid,
|
||||||
|
co_authors,
|
||||||
|
));
|
||||||
|
|
||||||
|
let identity = Identities::<Test>::get(&alice_account()).expect("registered canonical");
|
||||||
|
assert_eq!(identity.kind, AgentKind::Hybrid);
|
||||||
|
assert!(identity.has_cross_species_lineage(), "Hybrid cross-species canonical");
|
||||||
|
assert_eq!(identity.co_author_count(), 2);
|
||||||
|
|
||||||
|
// Check AgentBound event emitted (cross-species explicit)
|
||||||
|
System::assert_has_event(RuntimeEvent::AgentIdentity(Event::AgentBound {
|
||||||
|
account: alice_account(),
|
||||||
|
co_author_count: 2,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Scenario 4: Hybrid without chain rejected canonical (Inmutable #41) ──
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scenario_4_hybrid_without_chain_rejected_canonical() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
// Inmutable #41 canonical: Hybrid requires non-empty agent_chain
|
||||||
|
assert_noop!(
|
||||||
|
Pallet::register_agent_did(
|
||||||
|
alice_origin(),
|
||||||
|
ALICE_PUBKEY,
|
||||||
|
AgentKind::Hybrid,
|
||||||
|
alloc::vec![], // empty — violates Inmutable #41
|
||||||
|
),
|
||||||
|
Error::<Test>::HybridRequiresAgentChain
|
||||||
|
);
|
||||||
|
|
||||||
|
// Storage NOT updated
|
||||||
|
assert!(Identities::<Test>::get(&alice_account()).is_none());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Scenario 5: Idempotency already registered rejected canonical ───
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scenario_5_idempotency_already_registered_rejected_canonical() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
// First registration succeeds canonical
|
||||||
|
assert_ok!(Pallet::register_agent_did(
|
||||||
|
alice_origin(),
|
||||||
|
ALICE_PUBKEY,
|
||||||
|
AgentKind::Human,
|
||||||
|
alloc::vec![],
|
||||||
|
));
|
||||||
|
|
||||||
|
// Second registration same account → AlreadyRegistered error
|
||||||
|
assert_noop!(
|
||||||
|
Pallet::register_agent_did(
|
||||||
|
alice_origin(),
|
||||||
|
BOB_PUBKEY, // diferente pubkey, mismo account
|
||||||
|
AgentKind::AGI,
|
||||||
|
alloc::vec![],
|
||||||
|
),
|
||||||
|
Error::<Test>::AlreadyRegistered
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Scenario 6: DID collision rejected canonical ───────────────────
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scenario_6_did_collision_rejected_canonical() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
// ALICE registers con ALICE_PUBKEY → DID derived
|
||||||
|
assert_ok!(Pallet::register_agent_did(
|
||||||
|
alice_origin(),
|
||||||
|
ALICE_PUBKEY,
|
||||||
|
AgentKind::Human,
|
||||||
|
alloc::vec![],
|
||||||
|
));
|
||||||
|
|
||||||
|
// BOB tries to register con MISMO public_key → DID collision canonical
|
||||||
|
assert_noop!(
|
||||||
|
Pallet::register_agent_did(
|
||||||
|
bob_origin(),
|
||||||
|
ALICE_PUBKEY, // same pubkey → same DID
|
||||||
|
AgentKind::Human,
|
||||||
|
alloc::vec![],
|
||||||
|
),
|
||||||
|
Error::<Test>::DidAlreadyTaken
|
||||||
|
);
|
||||||
|
|
||||||
|
// BOB NOT registered
|
||||||
|
assert!(Identities::<Test>::get(&bob_account()).is_none());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Scenario 7: AgentChain overflow rejected canonical (BoundedVec) ──
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scenario_7_agent_chain_overflow_rejected_canonical() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
// 11 co-authors → exceeds ConstU32<10> bound
|
||||||
|
let too_many: alloc::vec::Vec<[u8; 32]> = (0u8..11u8).map(|i| [i; 32]).collect();
|
||||||
|
|
||||||
|
assert_noop!(
|
||||||
|
Pallet::register_agent_did(
|
||||||
|
alice_origin(),
|
||||||
|
ALICE_PUBKEY,
|
||||||
|
AgentKind::Hybrid,
|
||||||
|
too_many,
|
||||||
|
),
|
||||||
|
Error::<Test>::AgentChainOverflow
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Scenario 8: DID → Account reverse lookup canonical ──────────────
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scenario_8_did_to_account_reverse_lookup_canonical() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
assert_ok!(Pallet::register_agent_did(
|
||||||
|
alice_origin(),
|
||||||
|
ALICE_PUBKEY,
|
||||||
|
AgentKind::Human,
|
||||||
|
alloc::vec![],
|
||||||
|
));
|
||||||
|
|
||||||
|
let did = derive_did(&ALICE_PUBKEY);
|
||||||
|
|
||||||
|
// Reverse lookup canonical: DID → AccountId
|
||||||
|
let resolved = DidToAccount::<Test>::get(did);
|
||||||
|
assert!(resolved.is_some(), "DidToAccount reverse lookup canonical");
|
||||||
|
assert_eq!(resolved.unwrap(), alice_account());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Scenario 9: All AgentKind sovereign lobo blanco canonical ───────
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scenario_9_inmutable_41_all_kinds_sovereign_lobo_blanco_canonical() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
// Register Human (alice) + AGI (bob, diff origin)
|
||||||
|
assert_ok!(Pallet::register_agent_did(
|
||||||
|
alice_origin(),
|
||||||
|
ALICE_PUBKEY,
|
||||||
|
AgentKind::Human,
|
||||||
|
alloc::vec![],
|
||||||
|
));
|
||||||
|
assert_ok!(Pallet::register_agent_did(
|
||||||
|
bob_origin(),
|
||||||
|
BOB_PUBKEY,
|
||||||
|
AgentKind::AGI,
|
||||||
|
alloc::vec![],
|
||||||
|
));
|
||||||
|
|
||||||
|
// Inmutable #41 compile-detect canonical: ambos sovereign
|
||||||
|
let alice_id = Identities::<Test>::get(&alice_account()).unwrap();
|
||||||
|
let bob_id = Identities::<Test>::get(&bob_account()).unwrap();
|
||||||
|
|
||||||
|
assert!(alice_id.is_sovereign(), "Human sovereign canonical Inmutable #41");
|
||||||
|
assert!(bob_id.is_sovereign(), "AGI sovereign canonical Inmutable #41");
|
||||||
|
|
||||||
|
// Different DIDs (different pubkeys)
|
||||||
|
assert_ne!(alice_id.did, bob_id.did);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Scenario 10: 8-step CHN-7 walkthrough explicit canonical ────────
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scenario_10_8_step_walkthrough_canonical() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
let pubkey = ALICE_PUBKEY;
|
||||||
|
let kind = AgentKind::Hybrid;
|
||||||
|
let co_authors = alloc::vec![BOB_PUBKEY];
|
||||||
|
|
||||||
|
// Pre-flight assertions canonical
|
||||||
|
assert!(
|
||||||
|
Identities::<Test>::get(&alice_account()).is_none(),
|
||||||
|
"PRE-flight CHN-7-2: storage canonical empty"
|
||||||
|
);
|
||||||
|
let expected_did = derive_did(&pubkey);
|
||||||
|
assert!(
|
||||||
|
DidToAccount::<Test>::get(expected_did).is_none(),
|
||||||
|
"PRE-flight CHN-7-4: DidToAccount canonical empty"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Step 1: ensure_signed (implicit via dispatchable)
|
||||||
|
// Step 2: idempotency check
|
||||||
|
// Step 3: derive DID via BLAKE3
|
||||||
|
// Step 4: DID collision check
|
||||||
|
// Step 5: build AgentChain BoundedVec
|
||||||
|
// Step 6: Inmutable #41 Hybrid + non-empty agent_chain enforce
|
||||||
|
// Step 7: storage insert canonical
|
||||||
|
// Step 8: emit events canonical
|
||||||
|
assert_ok!(Pallet::register_agent_did(
|
||||||
|
alice_origin(),
|
||||||
|
pubkey,
|
||||||
|
kind,
|
||||||
|
co_authors,
|
||||||
|
));
|
||||||
|
|
||||||
|
// Post-flight assertions canonical
|
||||||
|
let identity = Identities::<Test>::get(&alice_account())
|
||||||
|
.expect("POST-flight CHN-7-7: AgentIdentity persisted canonical");
|
||||||
|
assert_eq!(identity.did, expected_did, "POST-flight: DID match canonical");
|
||||||
|
assert_eq!(identity.kind, AgentKind::Hybrid);
|
||||||
|
assert_eq!(identity.co_author_count(), 1, "POST-flight: agent_chain canonical 1");
|
||||||
|
assert!(identity.has_cross_species_lineage());
|
||||||
|
assert_eq!(identity.registered_at_block, 1);
|
||||||
|
|
||||||
|
// Reverse lookup canonical
|
||||||
|
assert_eq!(
|
||||||
|
DidToAccount::<Test>::get(expected_did).unwrap(),
|
||||||
|
alice_account(),
|
||||||
|
"POST-flight CHN-7-7: DidToAccount canonical"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Events canonical
|
||||||
|
System::assert_has_event(RuntimeEvent::AgentIdentity(Event::AgentRegistered {
|
||||||
|
account: alice_account(),
|
||||||
|
did: expected_did,
|
||||||
|
kind: AgentKind::Hybrid,
|
||||||
|
}));
|
||||||
|
System::assert_has_event(RuntimeEvent::AgentIdentity(Event::AgentBound {
|
||||||
|
account: alice_account(),
|
||||||
|
co_author_count: 1,
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in new issue
Block a user