"""Tests for muse.core.hdkeys — six-level domain-first HD key derivation. Test categories --------------- - Unit: constants, path construction, argument validation, return types - Integration: full pipeline from mnemonic → seed → key → signature - Stress: uniqueness across all dimensions (domain, entity, role, index) - Security: domain isolation, agent sub-seed independence, least privilege - Data integrity: consistency between hdkeys API and direct slip010 derivation """ from __future__ import annotations import pytest from muse.core.bip39 import mnemonic_to_seed from muse.core.hdkeys import ( DOMAIN_BLOCKCHAIN, DOMAIN_CODE, DOMAIN_GENERIC, DOMAIN_IDENTITY, DOMAIN_MIDI, DOMAIN_MUSIC, DOMAIN_PAYMENTS, ENTITY_AGENT, ENTITY_HUMAN, ENTITY_ORG, HdKeyError, ROLE_ATTEST, ROLE_DELEGATE, ROLE_PROVISION, ROLE_RECEIVE, ROLE_SIGN, derive_agent_sub_seed, derive_domain_key, derive_identity_key, derive_key, dk_to_ed25519, domain_index, muse_path, public_bytes_from_seed, ) from muse.core.slip010 import ( MUSE_PURPOSE, DerivedKey, child_key, derive_path, hardened, master_key, ) _TEST_MNEMONIC = ( "abandon abandon abandon abandon abandon abandon " "abandon abandon abandon abandon abandon about" ) @pytest.fixture def seed() -> bytes: return mnemonic_to_seed(_TEST_MNEMONIC) @pytest.fixture def raw_seed() -> bytes: return bytes(range(64)) # --------------------------------------------------------------------------- # Unit — constants # --------------------------------------------------------------------------- class TestConstants: def test_domain_values(self) -> None: # Hash-derived: sha256(name)[:4] & 0x7FFFFFFF — same pattern as MUSE_PURPOSE. # These are snapshot assertions; a change here means key derivation paths changed. assert DOMAIN_IDENTITY == domain_index("muse/identity") assert DOMAIN_PAYMENTS == domain_index("muse/payments") assert DOMAIN_CODE == domain_index("muse/code") assert DOMAIN_MUSIC == domain_index("muse/music") assert DOMAIN_MIDI == domain_index("muse/midi") assert DOMAIN_BLOCKCHAIN == domain_index("muse/blockchain") assert DOMAIN_GENERIC == domain_index("muse/generic") def test_domain_index_values_are_stable(self) -> None: # Concrete values to catch any accidental algorithm change. assert domain_index("muse/identity") == 1_660_078_172 assert domain_index("muse/payments") == 284_229_149 assert domain_index("muse/code") == 678_195_575 assert domain_index("muse/music") == 1_755_707_987 assert domain_index("muse/midi") == 1_444_628_350 assert domain_index("muse/blockchain") == 1_556_829_714 assert domain_index("muse/generic") == 2_023_564_266 def test_domain_index_fits_in_31_bits(self) -> None: for name in ["muse/identity", "muse/payments", "muse/code", "muse/music", "muse/midi", "muse/blockchain", "muse/generic"]: idx = domain_index(name) assert 0 <= idx <= 0x7FFF_FFFF, f"domain_index({name!r}) = {idx} out of range" def test_entity_values(self) -> None: assert ENTITY_HUMAN == 0 assert ENTITY_AGENT == 1 assert ENTITY_ORG == 2 def test_role_values(self) -> None: assert ROLE_SIGN == 0 assert ROLE_RECEIVE == 1 assert ROLE_PROVISION == 2 assert ROLE_ATTEST == 3 assert ROLE_DELEGATE == 4 def test_all_domains_distinct(self) -> None: domains = [DOMAIN_IDENTITY, DOMAIN_PAYMENTS, DOMAIN_CODE, DOMAIN_MUSIC, DOMAIN_MIDI, DOMAIN_BLOCKCHAIN, DOMAIN_GENERIC] assert len(set(domains)) == 7 def test_all_entities_distinct(self) -> None: entities = [ENTITY_HUMAN, ENTITY_AGENT, ENTITY_ORG] assert len(set(entities)) == 3 def test_all_roles_distinct(self) -> None: roles = [ROLE_SIGN, ROLE_RECEIVE, ROLE_PROVISION, ROLE_ATTEST, ROLE_DELEGATE] assert len(set(roles)) == 5 # --------------------------------------------------------------------------- # Unit — muse_path() # --------------------------------------------------------------------------- class TestMusePath: def test_default_identity_path(self) -> None: expected = f"m/{MUSE_PURPOSE}'/{DOMAIN_IDENTITY}'/0'/0'/0'/0'" assert muse_path(DOMAIN_IDENTITY) == expected def test_music_agent_path(self) -> None: expected = f"m/{MUSE_PURPOSE}'/{DOMAIN_MUSIC}'/1'/2'/0'/0'" assert muse_path(DOMAIN_MUSIC, entity_type=ENTITY_AGENT, entity_id=2) == expected def test_code_domain_rotation(self) -> None: expected = f"m/{MUSE_PURPOSE}'/{DOMAIN_CODE}'/0'/0'/0'/1'" assert muse_path(DOMAIN_CODE, index=1) == expected def test_payments_receive_role(self) -> None: expected = f"m/{MUSE_PURPOSE}'/{DOMAIN_PAYMENTS}'/0'/0'/1'/0'" assert muse_path(DOMAIN_PAYMENTS, role=ROLE_RECEIVE) == expected def test_all_six_levels_present(self) -> None: path = muse_path(DOMAIN_IDENTITY) parts = path.split("/") assert parts[0] == "m" assert len(parts) == 7 # m + 6 levels def test_all_levels_hardened(self) -> None: path = muse_path(DOMAIN_IDENTITY) for part in path.split("/")[1:]: assert part.endswith("'"), f"Level {part!r} is not hardened" def test_negative_domain_raises(self) -> None: with pytest.raises(HdKeyError, match="domain"): muse_path(-1) def test_negative_entity_id_raises(self) -> None: with pytest.raises(HdKeyError, match="entity_id"): muse_path(DOMAIN_IDENTITY, entity_id=-1) def test_negative_index_raises(self) -> None: with pytest.raises(HdKeyError, match="index"): muse_path(DOMAIN_IDENTITY, index=-1) def test_invalid_entity_type_raises(self) -> None: with pytest.raises(HdKeyError, match="entity_type"): muse_path(DOMAIN_IDENTITY, entity_type=4) def test_invalid_role_raises(self) -> None: with pytest.raises(HdKeyError, match="role"): muse_path(DOMAIN_IDENTITY, role=5) def test_returns_string(self) -> None: assert isinstance(muse_path(DOMAIN_IDENTITY), str) def test_purpose_embedded_in_path(self) -> None: assert str(MUSE_PURPOSE) in muse_path(DOMAIN_IDENTITY) # --------------------------------------------------------------------------- # Unit — derive_key() # --------------------------------------------------------------------------- class TestDeriveKey: def test_returns_derived_key(self, raw_seed: bytes) -> None: assert isinstance(derive_key(raw_seed, DOMAIN_IDENTITY), DerivedKey) def test_private_bytes_32(self, raw_seed: bytes) -> None: assert len(derive_key(raw_seed, DOMAIN_IDENTITY).private_bytes) == 32 def test_chain_code_32(self, raw_seed: bytes) -> None: assert len(derive_key(raw_seed, DOMAIN_IDENTITY).chain_code) == 32 def test_matches_direct_slip010_derivation(self, raw_seed: bytes) -> None: path = muse_path(DOMAIN_IDENTITY, ENTITY_HUMAN, 0, ROLE_SIGN, 0) dk_direct = derive_path(raw_seed, path) dk_api = derive_key(raw_seed, DOMAIN_IDENTITY) assert dk_api == dk_direct def test_negative_domain_raises(self, raw_seed: bytes) -> None: with pytest.raises(HdKeyError): derive_key(raw_seed, -1) def test_invalid_role_raises(self, raw_seed: bytes) -> None: with pytest.raises(HdKeyError): derive_key(raw_seed, DOMAIN_IDENTITY, role=9) def test_deterministic(self, raw_seed: bytes) -> None: dk1 = derive_key(raw_seed, DOMAIN_MUSIC) dk2 = derive_key(raw_seed, DOMAIN_MUSIC) assert dk1 == dk2 # --------------------------------------------------------------------------- # Unit — derive_identity_key() # --------------------------------------------------------------------------- class TestDeriveIdentityKey: def test_defaults_to_domain_identity(self, raw_seed: bytes) -> None: dk = derive_identity_key(raw_seed) expected = derive_path(raw_seed, muse_path(DOMAIN_IDENTITY)) assert dk == expected def test_entity_type_agent(self, raw_seed: bytes) -> None: dk = derive_identity_key(raw_seed, entity_type=ENTITY_AGENT, entity_id=0) expected = derive_path(raw_seed, muse_path(DOMAIN_IDENTITY, ENTITY_AGENT, 0)) assert dk == expected def test_rotation_index(self, raw_seed: bytes) -> None: current = derive_identity_key(raw_seed, index=0) rotated = derive_identity_key(raw_seed, index=1) assert current != rotated def test_negative_entity_id_raises(self, raw_seed: bytes) -> None: with pytest.raises(HdKeyError): derive_identity_key(raw_seed, entity_id=-1) # --------------------------------------------------------------------------- # Unit — derive_domain_key() # --------------------------------------------------------------------------- class TestDeriveDomainKey: @pytest.mark.parametrize("domain", [ DOMAIN_IDENTITY, DOMAIN_PAYMENTS, DOMAIN_CODE, DOMAIN_MUSIC, DOMAIN_MIDI, DOMAIN_BLOCKCHAIN, ]) def test_all_named_domains(self, domain: int, raw_seed: bytes) -> None: dk = derive_domain_key(raw_seed, domain) assert isinstance(dk, DerivedKey) def test_code_differs_from_music(self, raw_seed: bytes) -> None: assert derive_domain_key(raw_seed, DOMAIN_CODE) != derive_domain_key(raw_seed, DOMAIN_MUSIC) def test_role_receive_differs_from_sign(self, raw_seed: bytes) -> None: sign = derive_domain_key(raw_seed, DOMAIN_PAYMENTS, role=ROLE_SIGN) recv = derive_domain_key(raw_seed, DOMAIN_PAYMENTS, role=ROLE_RECEIVE) assert sign != recv # --------------------------------------------------------------------------- # Unit — derive_agent_sub_seed() # --------------------------------------------------------------------------- class TestDeriveAgentSubSeed: def test_returns_64_bytes(self, raw_seed: bytes) -> None: sub = derive_agent_sub_seed(raw_seed, DOMAIN_MUSIC, agent_id=0) assert isinstance(sub, (bytes, bytearray)) and len(sub) == 64 def test_different_domains_produce_different_sub_seeds(self, raw_seed: bytes) -> None: sub_music = derive_agent_sub_seed(raw_seed, DOMAIN_MUSIC, agent_id=0) sub_code = derive_agent_sub_seed(raw_seed, DOMAIN_CODE, agent_id=0) assert sub_music != sub_code def test_different_agent_ids_produce_different_sub_seeds(self, raw_seed: bytes) -> None: sub0 = derive_agent_sub_seed(raw_seed, DOMAIN_MUSIC, agent_id=0) sub1 = derive_agent_sub_seed(raw_seed, DOMAIN_MUSIC, agent_id=1) assert sub0 != sub1 def test_deterministic(self, raw_seed: bytes) -> None: sub1 = derive_agent_sub_seed(raw_seed, DOMAIN_MUSIC, agent_id=0) sub2 = derive_agent_sub_seed(raw_seed, DOMAIN_MUSIC, agent_id=0) assert sub1 == sub2 def test_differs_from_master_seed(self, raw_seed: bytes) -> None: sub = derive_agent_sub_seed(raw_seed, DOMAIN_MUSIC, agent_id=0) assert sub != raw_seed def test_sub_seed_composition(self, raw_seed: bytes) -> None: """Sub-seed = private_bytes + chain_code at m/purpose'/domain'/AGENT'/agent_id'.""" dk = master_key(raw_seed) dk = child_key(dk, hardened(MUSE_PURPOSE)) dk = child_key(dk, hardened(DOMAIN_MUSIC)) dk = child_key(dk, hardened(ENTITY_AGENT)) dk = child_key(dk, hardened(0)) expected = dk.private_bytes + dk.chain_code assert derive_agent_sub_seed(raw_seed, DOMAIN_MUSIC, agent_id=0) == expected def test_agent_can_derive_identity_key_from_sub_seed(self, raw_seed: bytes) -> None: auth_seed = derive_agent_sub_seed(raw_seed, DOMAIN_IDENTITY, agent_id=0) dk = derive_identity_key(auth_seed) assert isinstance(dk, DerivedKey) def test_negative_domain_raises(self, raw_seed: bytes) -> None: with pytest.raises(HdKeyError): derive_agent_sub_seed(raw_seed, -1, agent_id=0) def test_negative_agent_id_raises(self, raw_seed: bytes) -> None: with pytest.raises(HdKeyError): derive_agent_sub_seed(raw_seed, DOMAIN_MUSIC, agent_id=-1) # --------------------------------------------------------------------------- # Unit — dk_to_ed25519() and public_bytes_from_seed() # --------------------------------------------------------------------------- class TestKeyMaterialisation: def test_dk_to_ed25519_returns_signing_key(self, raw_seed: bytes) -> None: from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey dk = derive_identity_key(raw_seed) assert isinstance(dk_to_ed25519(dk), Ed25519PrivateKey) def test_sign_and_verify(self, raw_seed: bytes) -> None: dk = derive_identity_key(raw_seed) priv = dk_to_ed25519(dk) msg = b"muse hd path design" priv.public_key().verify(priv.sign(msg), msg) def test_public_bytes_32(self, raw_seed: bytes) -> None: assert len(public_bytes_from_seed(raw_seed)) == 32 def test_public_bytes_deterministic(self, raw_seed: bytes) -> None: assert public_bytes_from_seed(raw_seed) == public_bytes_from_seed(raw_seed) def test_public_bytes_defaults_to_identity_domain(self, raw_seed: bytes) -> None: pub = public_bytes_from_seed(raw_seed) expected = dk_to_ed25519(derive_identity_key(raw_seed)).public_key().public_bytes_raw() assert pub == expected def test_different_domains_different_public_keys(self, raw_seed: bytes) -> None: pub_id = public_bytes_from_seed(raw_seed, domain=DOMAIN_IDENTITY) pub_code = public_bytes_from_seed(raw_seed, domain=DOMAIN_CODE) assert pub_id != pub_code # --------------------------------------------------------------------------- # Integration — full pipeline # --------------------------------------------------------------------------- class TestFullPipeline: def test_mnemonic_to_signature(self, seed: bytes) -> None: dk = derive_identity_key(seed) priv = dk_to_ed25519(dk) msg = b"muse authentication" priv.public_key().verify(priv.sign(msg), msg) def test_wrong_key_cannot_verify(self, seed: bytes) -> None: from cryptography.exceptions import InvalidSignature current = derive_identity_key(seed, index=0) rotated = derive_identity_key(seed, index=1) msg = b"signed with current key" sig = dk_to_ed25519(current).sign(msg) with pytest.raises(InvalidSignature): dk_to_ed25519(rotated).public_key().verify(sig, msg) def test_human_and_agent_identity_keys_differ(self, seed: bytes) -> None: auth_seed = derive_agent_sub_seed(seed, DOMAIN_IDENTITY, agent_id=0) pub_human = public_bytes_from_seed(seed, domain=DOMAIN_IDENTITY) pub_agent = dk_to_ed25519(derive_identity_key(auth_seed)).public_key().public_bytes_raw() assert pub_human != pub_agent def test_five_agents_distinct_identity_keys(self, seed: bytes) -> None: pub_keys: set[bytes] = set() for agent_id in range(5): auth_seed = derive_agent_sub_seed(seed, DOMAIN_IDENTITY, agent_id=agent_id) pub = dk_to_ed25519(derive_identity_key(auth_seed)).public_key().public_bytes_raw() assert pub not in pub_keys, f"Duplicate at agent_id={agent_id}" pub_keys.add(pub) # --------------------------------------------------------------------------- # Security — domain isolation and least privilege # --------------------------------------------------------------------------- class TestSecurity: def test_identity_key_differs_from_all_domain_keys(self, raw_seed: bytes) -> None: identity_pub = public_bytes_from_seed(raw_seed, domain=DOMAIN_IDENTITY) for domain in (DOMAIN_PAYMENTS, DOMAIN_CODE, DOMAIN_MUSIC, DOMAIN_MIDI, DOMAIN_BLOCKCHAIN): other_pub = public_bytes_from_seed(raw_seed, domain=domain) assert identity_pub != other_pub, f"Identity key leaked into domain={domain}" def test_music_agent_cannot_derive_code_domain_key(self, raw_seed: bytes) -> None: """A music agent's sub-seed produces a different code key than the operator's.""" music_agent_seed = derive_agent_sub_seed(raw_seed, DOMAIN_MUSIC, agent_id=0) operator_code_pub = public_bytes_from_seed(raw_seed, domain=DOMAIN_CODE) agent_code_pub = public_bytes_from_seed(music_agent_seed, domain=DOMAIN_CODE) assert operator_code_pub != agent_code_pub def test_domain_scoped_sub_seeds_are_independent(self, raw_seed: bytes) -> None: domains = [DOMAIN_IDENTITY, DOMAIN_PAYMENTS, DOMAIN_CODE, DOMAIN_MUSIC, DOMAIN_MIDI] sub_seeds = [bytes(derive_agent_sub_seed(raw_seed, d, agent_id=0)) for d in domains] assert len(set(sub_seeds)) == len(domains), "Domain sub-seeds are not independent" def test_all_roles_produce_distinct_keys(self, raw_seed: bytes) -> None: keys: set[bytes] = set() for role in (ROLE_SIGN, ROLE_RECEIVE, ROLE_PROVISION, ROLE_ATTEST, ROLE_DELEGATE): pub = public_bytes_from_seed(raw_seed, domain=DOMAIN_CODE, role=role) assert pub not in keys, f"Duplicate key for role={role}" keys.add(pub) def test_sub_seed_does_not_expose_master_seed(self, raw_seed: bytes) -> None: sub = derive_agent_sub_seed(raw_seed, DOMAIN_MUSIC, agent_id=0) assert sub != raw_seed # First 32 bytes of sub-seed are derived private bytes — must not equal # anything trivially derivable from master seed assert sub[:32] != raw_seed[:32] # --------------------------------------------------------------------------- # Stress — uniqueness across all six dimensions # --------------------------------------------------------------------------- class TestStress: def test_100_domain_keys_all_unique(self, raw_seed: bytes) -> None: # Extend domain namespace — open by design pubs: set[bytes] = set() for domain in range(100): pub = public_bytes_from_seed(raw_seed, domain=domain) assert pub not in pubs, f"Duplicate at domain={domain}" pubs.add(pub) def test_100_entity_ids_all_unique(self, raw_seed: bytes) -> None: pubs: set[bytes] = set() for entity_id in range(100): pub = public_bytes_from_seed(raw_seed, domain=DOMAIN_IDENTITY, entity_id=entity_id) assert pub not in pubs, f"Duplicate at entity_id={entity_id}" pubs.add(pub) def test_100_rotation_indices_all_unique(self, raw_seed: bytes) -> None: pubs: set[bytes] = set() for index in range(100): pub = public_bytes_from_seed(raw_seed, domain=DOMAIN_IDENTITY, index=index) assert pub not in pubs, f"Duplicate at index={index}" pubs.add(pub) def test_50_agent_sub_seeds_all_unique_per_domain(self, raw_seed: bytes) -> None: for domain in (DOMAIN_IDENTITY, DOMAIN_MUSIC, DOMAIN_CODE): subs: set[bytes] = set() for agent_id in range(50): sub = bytes(derive_agent_sub_seed(raw_seed, domain, agent_id=agent_id)) assert sub not in subs, f"Duplicate at domain={domain} agent_id={agent_id}" subs.add(sub) def test_all_6_domains_x_3_entities_x_5_roles_unique(self, raw_seed: bytes) -> None: domains = [DOMAIN_IDENTITY, DOMAIN_PAYMENTS, DOMAIN_CODE, DOMAIN_MUSIC, DOMAIN_MIDI, DOMAIN_BLOCKCHAIN] entities = [ENTITY_HUMAN, ENTITY_AGENT, ENTITY_ORG] roles = [ROLE_SIGN, ROLE_RECEIVE, ROLE_PROVISION, ROLE_ATTEST, ROLE_DELEGATE] pubs: set[bytes] = set() total = 0 for d in domains: for e in entities: for r in roles: pub = public_bytes_from_seed(raw_seed, domain=d, entity_type=e, role=r) assert pub not in pubs, f"Duplicate at domain={d} entity={e} role={r}" pubs.add(pub) total += 1 assert total == 7 * 3 * 5 # 105 unique keys # --------------------------------------------------------------------------- # End-to-end # --------------------------------------------------------------------------- class TestEndToEnd: """Full workflow scenarios from mnemonic to network-ready public key. These tests simulate real caller sequences: the path a MuseHub registration, an agent spawn, and a key rotation take through this stack. """ def test_musehub_registration_flow(self) -> None: """Operator generates mnemonic, derives identity key, gets public key for registration.""" from muse.core.bip39 import generate_mnemonic, mnemonic_to_seed mnemonic = generate_mnemonic() seed = mnemonic_to_seed(mnemonic) pub = public_bytes_from_seed(seed, domain=DOMAIN_IDENTITY) assert len(pub) == 32 fingerprint = pub.hex() assert len(fingerprint) == 64 # 32 bytes → 64 hex chars def test_agent_spawn_flow(self) -> None: """Operator spawns a music agent with domain-scoped sub-seed.""" from muse.core.bip39 import generate_mnemonic, mnemonic_to_seed seed = mnemonic_to_seed(generate_mnemonic()) # Operator derives domain-scoped sub-seeds for the agent music_seed = derive_agent_sub_seed(seed, DOMAIN_MUSIC, agent_id=0) auth_seed = derive_agent_sub_seed(seed, DOMAIN_IDENTITY, agent_id=0) # Agent uses each seed for its respective domain agent_identity_pub = dk_to_ed25519(derive_identity_key(auth_seed)).public_key().public_bytes_raw() agent_music_pub = dk_to_ed25519(derive_domain_key(music_seed, DOMAIN_MUSIC)).public_key().public_bytes_raw() assert len(agent_identity_pub) == 32 assert len(agent_music_pub) == 32 # Agent's identity pub differs from operator's operator_pub = public_bytes_from_seed(seed, domain=DOMAIN_IDENTITY) assert agent_identity_pub != operator_pub def test_key_rotation_flow(self) -> None: """Operator pre-registers next key then rotates.""" from muse.core.bip39 import generate_mnemonic, mnemonic_to_seed seed = mnemonic_to_seed(generate_mnemonic()) # Current key in use current_pub = public_bytes_from_seed(seed, domain=DOMAIN_IDENTITY, index=0) # Pre-register next key before rotation next_pub = public_bytes_from_seed(seed, domain=DOMAIN_IDENTITY, index=1) assert current_pub != next_pub # After rotation, index 1 becomes the active key # Both remain independently derivable from the same seed assert len(current_pub) == len(next_pub) == 32 def test_commit_signing_flow(self) -> None: """Operator signs a commit hash with their code domain key.""" from muse.core.bip39 import generate_mnemonic, mnemonic_to_seed import hashlib seed = mnemonic_to_seed(generate_mnemonic()) dk = derive_domain_key(seed, domain=DOMAIN_CODE) priv = dk_to_ed25519(dk) # Simulate a commit hash commit_bytes = hashlib.sha256(b"tree abc123\nparent def456\nauthor gabriel").digest() sig = priv.sign(commit_bytes) # Any verifier with the public key can verify priv.public_key().verify(sig, commit_bytes) def test_music_project_signing_flow(self) -> None: """Operator signs a Stori project snapshot with their music domain key.""" from muse.core.bip39 import generate_mnemonic, mnemonic_to_seed seed = mnemonic_to_seed(generate_mnemonic()) dk = derive_domain_key(seed, domain=DOMAIN_MUSIC) priv = dk_to_ed25519(dk) project_manifest = b"track: neon_sunrise.wav\nbpm: 128\nkey: Am" sig = priv.sign(project_manifest) priv.public_key().verify(sig, project_manifest) # --------------------------------------------------------------------------- # Performance # --------------------------------------------------------------------------- class TestPerformance: """Timing budgets for hdkeys high-level operations. Key derivation is dominated by HMAC-SHA512 (fast). The only slow operation is mnemonic_to_seed (PBKDF2) which lives in bip39 — once the seed is in hand, all hdkeys operations must be fast. """ def test_derive_identity_key_under_5ms(self) -> None: import time seed = bytes(range(64)) start = time.perf_counter() for _ in range(100): derive_identity_key(seed) elapsed = (time.perf_counter() - start) / 100 assert elapsed < 0.005, f"derive_identity_key averaged {elapsed*1000:.2f}ms — too slow" def test_derive_domain_key_under_5ms(self) -> None: import time seed = bytes(range(64)) start = time.perf_counter() for _ in range(100): derive_domain_key(seed, DOMAIN_MUSIC) elapsed = (time.perf_counter() - start) / 100 assert elapsed < 0.005, f"derive_domain_key averaged {elapsed*1000:.2f}ms — too slow" def test_derive_agent_sub_seed_under_5ms(self) -> None: import time seed = bytes(range(64)) start = time.perf_counter() for _ in range(100): derive_agent_sub_seed(seed, DOMAIN_MUSIC, agent_id=0) elapsed = (time.perf_counter() - start) / 100 assert elapsed < 0.005, f"derive_agent_sub_seed averaged {elapsed*1000:.2f}ms — too slow" def test_public_bytes_from_seed_under_5ms(self) -> None: import time seed = bytes(range(64)) start = time.perf_counter() for _ in range(100): public_bytes_from_seed(seed) elapsed = (time.perf_counter() - start) / 100 assert elapsed < 0.005, f"public_bytes_from_seed averaged {elapsed*1000:.2f}ms — too slow" def test_105_key_matrix_under_100ms(self) -> None: """6 domains × 3 entity types × 5 roles = 90 derivations must complete in < 100ms.""" import time seed = bytes(range(64)) domains = [DOMAIN_IDENTITY, DOMAIN_PAYMENTS, DOMAIN_CODE, DOMAIN_MUSIC, DOMAIN_MIDI, DOMAIN_BLOCKCHAIN] entities = [ENTITY_HUMAN, ENTITY_AGENT, ENTITY_ORG] roles = [ROLE_SIGN, ROLE_RECEIVE, ROLE_PROVISION, ROLE_ATTEST, ROLE_DELEGATE] start = time.perf_counter() for d in domains: for e in entities: for r in roles: derive_key(seed, domain=d, entity_type=e, role=r) elapsed = time.perf_counter() - start assert elapsed < 0.1, f"105-key matrix took {elapsed*1000:.1f}ms — too slow" # --------------------------------------------------------------------------- # Docstrings # --------------------------------------------------------------------------- class TestDocstrings: """Every public symbol in muse.core.hdkeys must have a docstring.""" def test_module_has_docstring(self) -> None: import muse.core.hdkeys as mod assert mod.__doc__, "muse.core.hdkeys module has no docstring" @pytest.mark.parametrize("name", [ "HdKeyError", "muse_path", "derive_key", "derive_identity_key", "derive_domain_key", "derive_agent_sub_seed", "dk_to_ed25519", "public_bytes_from_seed", ]) def test_public_symbol_has_docstring(self, name: str) -> None: import muse.core.hdkeys as mod obj = getattr(mod, name) assert obj.__doc__, f"muse.core.hdkeys.{name} has no docstring"