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]
|
||||
|
||||
## [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
|
||||
|
||||
**🔌 Phase 2.7 AGENT IDENTITY pallet WIRED runtime composition canonical** —
|
||||
|
||||
@@ -102,9 +102,11 @@ sp_api::decl_runtime_apis! {
|
||||
|
||||
#[frame_support::pallet]
|
||||
pub mod pallet {
|
||||
use alloc::vec::Vec;
|
||||
use frame_support::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.
|
||||
///
|
||||
@@ -197,47 +199,119 @@ pub mod pallet {
|
||||
HybridRequiresAgentChain,
|
||||
}
|
||||
|
||||
/// Dispatchables canonical (scaffold v0.0.24 — real impl v0.0.26).
|
||||
/// Dispatchables canonical (real impl v0.0.26).
|
||||
#[pallet::call]
|
||||
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
|
||||
/// canonical solo validates basic state (origin signed + no duplicate)
|
||||
/// + emite placeholder event 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 6-step per CHN-7 (v0.0.26 real):
|
||||
/// Flow canonical 8-step per CHN-7:
|
||||
/// 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
|
||||
/// 4. Validate `!DidToAccount::contains_key(did)` (no DID collision)
|
||||
/// 5. Validate Hybrid → agent_chain non-empty (Inmutable #41 verification)
|
||||
/// 6. Insert Identities + DidToAccount + emit AgentRegistered + AgentBound (if Hybrid)
|
||||
/// 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<T>,
|
||||
_public_key: [u8; 32],
|
||||
public_key: [u8; 32],
|
||||
kind: AgentKind,
|
||||
co_authors_raw: Vec<[u8; 32]>,
|
||||
) -> DispatchResult {
|
||||
// Verify origin canonical signed.
|
||||
// Step 1: ensure origin signed canonical
|
||||
let who = ensure_signed(origin)?;
|
||||
|
||||
// SCAFFOLD v0.0.24: emit placeholder event canonical.
|
||||
// Real impl v0.0.26: derive_did + storage insert + cross-species validation.
|
||||
// Step 2: idempotency check canonical (no duplicate registration)
|
||||
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 {
|
||||
account: who,
|
||||
did: origin_chain_types::AssetHash::new([0u8; 32]), // placeholder until v0.0.26
|
||||
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<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;
|
||||
|
||||
// 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::*;
|
||||
|
||||
@@ -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