From 3e9e1c90a7b22fc31b012389c731ee424da17e16 Mon Sep 17 00:00:00 2001 From: 8e8b2c <138928994+8e8b2c@users.noreply.github.com> Date: Mon, 2 Sep 2024 16:30:19 +0100 Subject: [PATCH] feat: establish authority trust at point of use (#110) --- README.md | 2 +- crates/holoom_types/src/external_id.rs | 25 ++++++ crates/holoom_types/src/lib.rs | 6 -- crates/holoom_types/src/username.rs | 23 ++++++ .../src/evm_signing_offer.rs | 22 ++++- .../src/external_id_attestation.rs | 66 +++++++++++---- .../username_registry_coordinator/src/lib.rs | 19 ----- .../src/recipe_execution.rs | 12 ++- .../src/username_attestation.rs | 59 +++++++++---- crates/username_registry_utils/src/lib.rs | 11 +-- .../src/agent_external_id_attestation.rs | 15 ++-- .../src/agent_username_attestation.rs | 15 ++-- .../src/evm_signing_offer.rs | 2 +- .../src/external_id_attestation.rs | 11 +-- .../src/external_id_to_attestation.rs | 17 ++-- .../src/username_attestation.rs | 15 +--- crates/version_coordinator/build.rs | 2 +- packages/authority/rollup.browser.config.ts | 10 ++- packages/authority/rollup.node.config.ts | 2 +- .../evm-bytes-signer-client.ts | 7 ++ .../external-id-attestor-client.ts | 7 ++ packages/authority/src/query/service.ts | 7 +- packages/client/rollup.browser.config.ts | 13 ++- packages/client/rollup.node.config.ts | 5 +- ...xternal-id-attestation-requestor-client.ts | 7 +- packages/client/src/holoom-client.ts | 19 +++-- .../src/e2e/evm-wallet-binding.test.ts | 11 ++- packages/tryorama/src/e2e/external-id.test.ts | 20 ++++- packages/tryorama/src/e2e/metadata.test.ts | 10 ++- .../tryorama/src/e2e/signing-offer.test.ts | 15 ++-- packages/tryorama/src/e2e/username.test.ts | 16 ++-- ..._external_id_attestation_for_agent.test.ts | 22 +++-- ...l_can_get_external_id_attestations.test.ts | 6 +- ...tation_for_agent_that_doesnt_exist.test.ts | 11 +-- ...can_delete_external_id_attestation.test.ts | 6 +- ...only_authority_can_confirm_request.test.ts | 32 -------- ...can_create_external_id_attestation.test.ts | 32 -------- ...can_only_update_their_own_metadata.test.ts | 6 +- .../can_fetch_documents_by_relation.test.ts | 5 +- .../recipe/can_execute_basic_recipe.test.ts | 6 +- .../recipe/cannot_use_untrusted_docs.test.ts | 4 +- packages/tryorama/src/signer/sign.test.ts | 4 +- ...get_username_attestation_for_agent.test.ts | 22 +++-- .../all_can_get_username_attestations.test.ts | 6 +- ..._ingest_invalid_username_signature.test.ts | 6 +- ...an_attest_username_via_remote_call.test.ts | 18 ++-- ...tation_for_agent_that_doesnt_exist.test.ts | 11 +-- ...dy_can_delete_username_attestation.test.ts | 6 +- ...ty_can_create_username_attestation.test.ts | 27 ------ ...e_agent_cannot_be_registered_twice.test.ts | 4 +- ...sername_cannot_be_registered_twice.test.ts | 4 +- ...ame_must_be_within_character_limit.test.ts | 4 +- packages/tryorama/src/utils/setup-happ.ts | 82 +++---------------- packages/tryorama/src/version/git-rev.test.ts | 4 +- ...validity_of_evm_wallet_attestation.test.ts | 4 +- ...idity_of_solana_wallet_attestation.test.ts | 4 +- packages/types/rollup.browser.config.ts | 8 +- .../GetAttestationForExternalIdPayload.ts | 16 ++++ ...etExternalIdAttestationsForAgentPayload.ts | 16 ++++ .../GetUsernameAttestationForAgentPayload.ts | 16 ++++ ...SendExternalIdAttestationRequestPayload.ts | 2 + .../SignUsernameAndRequestAttestationInput.ts | 16 ++++ packages/types/src/types/index.ts | 4 + .../UsernameRegistryCoordinator.ts | 54 ++++++++---- scripts/build_dna.sh | 2 +- 65 files changed, 524 insertions(+), 417 deletions(-) delete mode 100644 packages/tryorama/src/external_attestation/only_authority_can_confirm_request.test.ts delete mode 100644 packages/tryorama/src/external_attestation/only_authority_can_create_external_id_attestation.test.ts delete mode 100644 packages/tryorama/src/username_attestation/only_authority_can_create_username_attestation.test.ts create mode 100644 packages/types/src/types/GetAttestationForExternalIdPayload.ts create mode 100644 packages/types/src/types/GetExternalIdAttestationsForAgentPayload.ts create mode 100644 packages/types/src/types/GetUsernameAttestationForAgentPayload.ts create mode 100644 packages/types/src/types/SignUsernameAndRequestAttestationInput.ts diff --git a/README.md b/README.md index bc71aeb..24bac26 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Provides several micro-service-like nodeJS applications that can be attached to ## The Authority Agent -Each Holoom network is intialised with an authority agent specified in the network's DNA properties. The role of the authority agent is to maintain a registry of unique usernames, and is expected to be run on a server maintained by the instigator of the network. The authority agent is considered to be a partially trusted node in that users can validate its correct execution, but cannot prevent its downtime or censorship decisions. +Any agent can offer to act as an authority, whether it be on username uniqueness or ownership of an external ID. Where or not an authority is respected is determined at point of use. E.g. If a user were an interested in executing a recipe for a specific EVM signing offer, they would only be interested in external IDs attested by authorities specified in the trust list of that recipe. ## Testing diff --git a/crates/holoom_types/src/external_id.rs b/crates/holoom_types/src/external_id.rs index 3eeb4cc..3aa70a3 100644 --- a/crates/holoom_types/src/external_id.rs +++ b/crates/holoom_types/src/external_id.rs @@ -19,6 +19,8 @@ pub struct SendExternalIdAttestationRequestPayload { pub request_id: String, pub code_verifier: String, pub code: String, + #[ts(type = "AgentPubKey")] + pub authority: AgentPubKey, } #[derive(Serialize, Deserialize, Debug, TS)] @@ -47,3 +49,26 @@ pub struct RejectExternalIdRequestPayload { pub requestor: AgentPubKey, pub reason: String, } + +/// Input to `get_external_id_attestations_for_agent` +#[derive(Serialize, Deserialize, Debug, TS)] +#[ts(export)] +pub struct GetExternalIdAttestationsForAgentPayload { + /// The agent whose is the object of the attestations you wish to retrieve + #[ts(type = "AgentPubKey")] + pub agent_pubkey: AgentPubKey, + /// The authorities whose attestations you respect. + #[ts(type = "AgentPubKey[]")] + pub trusted_authorities: Vec, +} + +/// Input to `get_attestation_for_external_id` +#[derive(Serialize, Deserialize, Debug, TS)] +#[ts(export)] +pub struct GetAttestationForExternalIdPayload { + /// The external ID for which to want a corresponding attestation + pub external_id: String, + /// The authorities whose attestations you respect. + #[ts(type = "AgentPubKey[]")] + pub trusted_authorities: Vec, +} diff --git a/crates/holoom_types/src/lib.rs b/crates/holoom_types/src/lib.rs index a43508a..6031128 100644 --- a/crates/holoom_types/src/lib.rs +++ b/crates/holoom_types/src/lib.rs @@ -78,9 +78,3 @@ pub enum RemoteHoloomSignal { #[derive(Serialize, Deserialize, Debug, Clone, SerializedBytes, TS)] #[ts(export)] pub struct SignableBytes(pub Vec); - -#[dna_properties] -#[derive(Clone)] -pub struct HoloomDnaProperties { - pub authority_agent: String, -} diff --git a/crates/holoom_types/src/username.rs b/crates/holoom_types/src/username.rs index e638c84..2fbaa52 100644 --- a/crates/holoom_types/src/username.rs +++ b/crates/holoom_types/src/username.rs @@ -20,3 +20,26 @@ pub struct SignedUsername { #[ts(type = "AgentPubKey")] pub signer: AgentPubKey, } + +/// The input to `sign_username_and_request_attestation` +#[derive(Serialize, Deserialize, Debug, TS)] +#[ts(export)] +pub struct SignUsernameAndRequestAttestationInput { + /// The username for which you want a corresponding attestation + pub username: String, + /// The authorities whose attestations you respect. + #[ts(type = "AgentPubKey")] + pub authority: AgentPubKey, +} + +/// The input to `get_username_attestation_for_agent` +#[derive(Serialize, Deserialize, Debug, TS)] +#[ts(export)] +pub struct GetUsernameAttestationForAgentPayload { + /// The agent whose is the object of the attestations you wish to retrieve + #[ts(type = "AgentPubKey")] + pub agent: AgentPubKey, + /// The authorities whose attestations you respect. + #[ts(type = "AgentPubKey[]")] + pub trusted_authorities: Vec, +} diff --git a/crates/username_registry_coordinator/src/evm_signing_offer.rs b/crates/username_registry_coordinator/src/evm_signing_offer.rs index 27c29b2..9333ca6 100644 --- a/crates/username_registry_coordinator/src/evm_signing_offer.rs +++ b/crates/username_registry_coordinator/src/evm_signing_offer.rs @@ -12,6 +12,24 @@ use jaq_wrapper::{parse_single_json, Val}; use username_registry_integrity::{EntryTypes, LinkTypes}; use username_registry_utils::{deserialize_record_entry, hash_evm_address, hash_identifier}; +/// To be called once by agents that intend to respond to EVM signature requests (as specified by +/// their authored signing offers). Adds a `CapGrant` for ingesting such requests. +#[hdk_extern] +pub fn evm_signature_provider_setup(_: ()) -> ExternResult<()> { + let zome_name = zome_info()?.name; + let functions = BTreeSet::from([( + zome_name.clone(), + FunctionName("ingest_evm_signature_over_recipe_execution_request".into()), + )]); + create_cap_grant(CapGrantEntry { + tag: "".into(), + access: ().into(), + functions: GrantedFunctions::Listed(functions), + })?; + + Ok(()) +} + #[hdk_extern] fn create_signed_evm_signing_offer(payload: CreateEvmSigningOfferPayload) -> ExternResult { let action_hash = create_entry(EntryTypes::SignedEvmSigningOffer( @@ -122,13 +140,13 @@ fn ingest_evm_signature_over_recipe_execution_request( if output_vec.len() != signed_signing_offer.offer.u256_items.len() { return Err(wasm_error!(WasmErrorInner::Guest( "Unexpected u256 count for signing".into() - )))?; + ))); } let u256_array = output_vec .iter() .zip(signed_signing_offer.offer.u256_items.into_iter()) .map(|pair| match pair { - (Val::Str(hex_string), EvmU256Item::Hex) => EvmU256::from_str_radix(&hex_string, 16) + (Val::Str(hex_string), EvmU256Item::Hex) => EvmU256::from_str_radix(hex_string, 16) .map_err(|_| wasm_error!(WasmErrorInner::Guest("Invalid hex string".into()))), (Val::Int(value), EvmU256Item::Uint) => { if *value < 0 { diff --git a/crates/username_registry_coordinator/src/external_id_attestation.rs b/crates/username_registry_coordinator/src/external_id_attestation.rs index 6dd8256..4c47875 100644 --- a/crates/username_registry_coordinator/src/external_id_attestation.rs +++ b/crates/username_registry_coordinator/src/external_id_attestation.rs @@ -1,24 +1,43 @@ use hdk::prelude::*; use holoom_types::{ - ConfirmExternalIdRequestPayload, ExternalIdAttestation, - IngestExternalIdAttestationRequestPayload, LocalHoloomSignal, RejectExternalIdRequestPayload, - RemoteHoloomSignal, SendExternalIdAttestationRequestPayload, + ConfirmExternalIdRequestPayload, ExternalIdAttestation, GetAttestationForExternalIdPayload, + GetExternalIdAttestationsForAgentPayload, IngestExternalIdAttestationRequestPayload, + LocalHoloomSignal, RejectExternalIdRequestPayload, RemoteHoloomSignal, + SendExternalIdAttestationRequestPayload, }; use username_registry_integrity::{EntryTypes, LinkTypes, UnitEntryTypes}; -use username_registry_utils::{get_authority_agent, hash_identifier}; +use username_registry_utils::hash_identifier; +/// To be called once by agents that intend to act as an authority of `ExternalIdAttestation`s. +/// Adds a `CapGrant` for ingesting username attestation requests. +#[hdk_extern] +pub fn external_id_authority_setup(_: ()) -> ExternResult<()> { + let zome_name = zome_info()?.name; + let functions = BTreeSet::from([( + zome_name.clone(), + FunctionName("ingest_external_id_attestation_request".into()), + )]); + create_cap_grant(CapGrantEntry { + tag: "".into(), + access: ().into(), + functions: GrantedFunctions::Listed(functions), + })?; + + Ok(()) +} + +/// Forwards an external ID attestation request to the specified authority. #[hdk_extern] pub fn send_external_id_attestation_request( payload: SendExternalIdAttestationRequestPayload, ) -> ExternResult<()> { + let authority_agent = payload.authority; let payload = IngestExternalIdAttestationRequestPayload { request_id: payload.request_id, code_verifier: payload.code_verifier, code: payload.code, }; - let authority_agent = get_authority_agent()?; - let zome_name = zome_info()?.name; let fn_name = FunctionName::from("ingest_external_id_attestation_request"); let resp = call_remote(authority_agent, zome_name, fn_name, None, payload)?; @@ -129,13 +148,18 @@ pub fn get_external_id_attestation(external_id_ah: ActionHash) -> ExternResult ExternResult> { let mut links = get_links( - GetLinksInputBuilder::try_new(agent_pubkey, LinkTypes::AgentToExternalIdAttestation)? - .build(), + GetLinksInputBuilder::try_new( + payload.agent_pubkey, + LinkTypes::AgentToExternalIdAttestation, + )? + .build(), )?; links.sort_by_key(|link| link.timestamp); let maybe_records = links @@ -152,14 +176,23 @@ pub fn get_external_id_attestations_for_agent( Ok(maybe_records.into_iter().flatten().collect()) } +/// Gets the `ExternalIdAttestation` for the specified external ID, provided that the attestation +/// exists and was attested by a trusted author. #[hdk_extern] -pub fn get_attestation_for_external_id(external_id: String) -> ExternResult> { - let base = hash_identifier(external_id)?; +pub fn get_attestation_for_external_id( + payload: GetAttestationForExternalIdPayload, +) -> ExternResult> { + let base = hash_identifier(payload.external_id)?; let mut links = get_links( GetLinksInputBuilder::try_new(base, LinkTypes::ExternalIdToAttestation)?.build(), )?; links.sort_by_key(|link| link.timestamp); - let Some(link) = links.pop() else { + + let Some(link) = links + .into_iter() + .filter(|link| payload.trusted_authorities.contains(&link.author)) + .last() + else { return Ok(None); }; let action_hash = ActionHash::try_from(link.target).map_err(|_| { @@ -170,13 +203,10 @@ pub fn get_attestation_for_external_id(external_id: String) -> ExternResult ExternResult> { - if agent_info()?.agent_initial_pubkey != get_authority_agent()? { - return Err(wasm_error!(WasmErrorInner::Guest( - "Only callable by authority agent".into() - ))); - } +pub fn get_all_authored_external_id_ahs(_: ()) -> ExternResult> { let entry_type: EntryType = UnitEntryTypes::ExternalIdAttestation .try_into() .expect("ExternalIdAttestation is an entry type"); diff --git a/crates/username_registry_coordinator/src/lib.rs b/crates/username_registry_coordinator/src/lib.rs index 5f2edff..c880091 100644 --- a/crates/username_registry_coordinator/src/lib.rs +++ b/crates/username_registry_coordinator/src/lib.rs @@ -8,25 +8,11 @@ pub mod username_attestation; pub mod wallet_attestation; use hdk::prelude::*; use holoom_types::{LocalHoloomSignal, RemoteHoloomSignal}; -use username_registry_utils::get_authority_agent; #[hdk_extern] pub fn init(_: ()) -> ExternResult { - let authority_agent = get_authority_agent()?; - let my_pubkey = agent_info()?.agent_initial_pubkey; let mut functions = BTreeSet::new(); let zome_name = zome_info()?.name; - if my_pubkey == authority_agent { - functions.insert((zome_name.clone(), "ingest_signed_username".into())); - functions.insert(( - zome_name.clone(), - "ingest_external_id_attestation_request".into(), - )); - functions.insert(( - zome_name.clone(), - "ingest_evm_signature_over_recipe_execution_request".into(), - )); - } functions.insert((zome_name, "recv_remote_signal".into())); create_cap_grant(CapGrantEntry { tag: "".into(), @@ -64,8 +50,3 @@ fn recv_remote_signal(signal_io: ExternIO) -> ExternResult<()> { Ok(()) } - -#[hdk_extern] -fn get_authority(_: ()) -> ExternResult { - get_authority_agent() -} diff --git a/crates/username_registry_coordinator/src/recipe_execution.rs b/crates/username_registry_coordinator/src/recipe_execution.rs index b42e347..2ab3d14 100644 --- a/crates/username_registry_coordinator/src/recipe_execution.rs +++ b/crates/username_registry_coordinator/src/recipe_execution.rs @@ -1,7 +1,9 @@ use std::{collections::HashMap, rc::Rc}; use hdk::prelude::*; -use holoom_types::{recipe::*, ExternalIdAttestation, OracleDocument}; +use holoom_types::{ + recipe::*, ExternalIdAttestation, GetExternalIdAttestationsForAgentPayload, OracleDocument, +}; use indexmap::IndexMap; use jaq_wrapper::{compile_filter, parse_single_json, run_filter, Val}; use username_registry_integrity::EntryTypes; @@ -116,8 +118,12 @@ pub fn execute_recipe(payload: ExecuteRecipePayload) -> ExternResult { (val, instruction_execution) } RecipeInstruction::GetLatestCallerExternalId => { - let mut attestation_records = - get_external_id_attestations_for_agent(agent_info()?.agent_initial_pubkey)?; + let mut attestation_records = get_external_id_attestations_for_agent( + GetExternalIdAttestationsForAgentPayload { + agent_pubkey: agent_info()?.agent_initial_pubkey, + trusted_authorities: recipe.trusted_authors.clone(), + }, + )?; let attestation_record = attestation_records .pop() diff --git a/crates/username_registry_coordinator/src/username_attestation.rs b/crates/username_registry_coordinator/src/username_attestation.rs index 6e7740e..c813712 100644 --- a/crates/username_registry_coordinator/src/username_attestation.rs +++ b/crates/username_registry_coordinator/src/username_attestation.rs @@ -1,7 +1,27 @@ use hdk::prelude::*; -use holoom_types::{SignedUsername, UsernameAttestation}; +use holoom_types::{ + GetUsernameAttestationForAgentPayload, SignUsernameAndRequestAttestationInput, SignedUsername, + UsernameAttestation, +}; use username_registry_integrity::*; -use username_registry_utils::get_authority_agent; + +/// To be called once by agents that intend to act as an authority of `UsernameAttestation`s. Adds +/// a `CapGrant` for ingesting username attestation requests. +#[hdk_extern] +pub fn username_authority_setup(_: ()) -> ExternResult<()> { + let zome_name = zome_info()?.name; + let functions = BTreeSet::from([( + zome_name.clone(), + FunctionName("ingest_signed_username".into()), + )]); + create_cap_grant(CapGrantEntry { + tag: "".into(), + access: ().into(), + functions: GrantedFunctions::Listed(functions), + })?; + + Ok(()) +} #[hdk_extern] pub fn create_username_attestation( @@ -36,12 +56,18 @@ pub fn delete_username_attestation( delete_entry(original_username_attestation_hash) } #[hdk_extern] -pub fn get_username_attestation_for_agent(agent: AgentPubKey) -> ExternResult> { +pub fn get_username_attestation_for_agent( + payload: GetUsernameAttestationForAgentPayload, +) -> ExternResult> { let links = get_links( - GetLinksInputBuilder::try_new(agent, LinkTypes::AgentToUsernameAttestations)?.build(), + GetLinksInputBuilder::try_new(payload.agent, LinkTypes::AgentToUsernameAttestations)? + .build(), )?; - match links.first() { + match links + .into_iter() + .find(|link| payload.trusted_authorities.contains(&link.author)) + { Some(l) => get( ActionHash::try_from(l.clone().target).unwrap(), GetOptions::network(), @@ -60,14 +86,10 @@ pub fn does_agent_have_username(agent: AgentPubKey) -> ExternResult { Ok(count > 0) } +/// Returns all UsernameAttestation records authored by the calling agent. This method is intended +/// to be called by agents acting as authorities. #[hdk_extern] -pub fn get_all_username_attestations(_: ()) -> ExternResult> { - let my_pubkey = agent_info()?.agent_initial_pubkey; - if my_pubkey != get_authority_agent()? { - return Err(wasm_error!(WasmErrorInner::Host( - "Only callable by authority agent".into() - ))); - } +pub fn get_all_authored_username_attestations(_: ()) -> ExternResult> { let username_attestation_type: EntryType = UnitEntryTypes::UsernameAttestation.try_into()?; let filter = ChainQueryFilter::new() .include_entries(true) @@ -76,22 +98,23 @@ pub fn get_all_username_attestations(_: ()) -> ExternResult> { } /// Called by the user who wishes to register a username. Returns a UsernameAttestation Record. +/// It is left to the caller to specify the authority from whom they want an attestation. #[hdk_extern] -pub fn sign_username_to_attest(username: String) -> ExternResult { +pub fn sign_username_and_request_attestation( + input: SignUsernameAndRequestAttestationInput, +) -> ExternResult { // TODO: devise scheme akin to signing a nonce let my_pubkey = agent_info()?.agent_initial_pubkey; - let signature = sign(my_pubkey.clone(), &username)?; + let signature = sign(my_pubkey.clone(), &input.username)?; let payload = SignedUsername { - username, + username: input.username, signature, signer: my_pubkey, }; - let authority_agent = get_authority_agent()?; - let zome_name = zome_info()?.name; let fn_name = FunctionName::from("ingest_signed_username"); - let resp = call_remote(authority_agent, zome_name, fn_name, None, payload)?; + let resp = call_remote(input.authority, zome_name, fn_name, None, payload)?; match resp { ZomeCallResponse::Ok(result) => result.decode().map_err(|err| wasm_error!(err)), ZomeCallResponse::NetworkError(err) => Err(wasm_error!(WasmErrorInner::Guest(format!( diff --git a/crates/username_registry_utils/src/lib.rs b/crates/username_registry_utils/src/lib.rs index 764a3ea..bcbcad5 100644 --- a/crates/username_registry_utils/src/lib.rs +++ b/crates/username_registry_utils/src/lib.rs @@ -1,5 +1,5 @@ use hdi::prelude::*; -use holoom_types::{EvmAddress, HoloomDnaProperties}; +use holoom_types::EvmAddress; pub fn deserialize_record_entry(record: Record) -> ExternResult where @@ -32,12 +32,3 @@ pub fn hash_evm_address(evm_address: EvmAddress) -> ExternResult { .map_err(|err| wasm_error!(err))?; hash_entry(Entry::App(AppEntryBytes(bytes))) } - -pub fn get_authority_agent() -> ExternResult { - let dna_props = HoloomDnaProperties::try_from_dna_properties()?; - AgentPubKey::try_from(dna_props.authority_agent).map_err(|_| { - wasm_error!(WasmErrorInner::Guest( - "Failed to deserialize AgentPubKey from dna properties".into() - )) - }) -} diff --git a/crates/username_registry_validation/src/agent_external_id_attestation.rs b/crates/username_registry_validation/src/agent_external_id_attestation.rs index 8e23cbc..60248d9 100644 --- a/crates/username_registry_validation/src/agent_external_id_attestation.rs +++ b/crates/username_registry_validation/src/agent_external_id_attestation.rs @@ -1,6 +1,5 @@ use hdi::prelude::*; use holoom_types::ExternalIdAttestation; -use username_registry_utils::get_authority_agent; pub fn validate_create_link_agent_to_external_id_attestations( action: CreateLink, @@ -8,17 +7,17 @@ pub fn validate_create_link_agent_to_external_id_attestations( target_address: AnyLinkableHash, _tag: LinkTag, ) -> ExternResult { - // Only the authority can create link - let authority_agent = get_authority_agent()?; - if action.author != authority_agent { + // Check the entry type for the given action hash + let action_hash = ActionHash::try_from(target_address).map_err(|e| wasm_error!(e))?; + let record = must_get_valid_record(action_hash)?; + + // Only the attestation author can create links to it + if &action.author != record.action().author() { return Ok(ValidateCallbackResult::Invalid( - "Only the Username Registry Authority can create external ID attestation links".into(), + "Only the external ID attestation author can create links to it".into(), )); } - // Check the entry type for the given action hash - let action_hash = ActionHash::try_from(target_address).map_err(|e| wasm_error!(e))?; - let record = must_get_valid_record(action_hash)?; let _external_id_attestation: ExternalIdAttestation = record .entry() .to_app_option() diff --git a/crates/username_registry_validation/src/agent_username_attestation.rs b/crates/username_registry_validation/src/agent_username_attestation.rs index 9d023a1..15511a9 100644 --- a/crates/username_registry_validation/src/agent_username_attestation.rs +++ b/crates/username_registry_validation/src/agent_username_attestation.rs @@ -1,6 +1,5 @@ use hdi::prelude::*; use holoom_types::UsernameAttestation; -use username_registry_utils::get_authority_agent; pub fn validate_create_link_agent_to_username_attestations( action: CreateLink, @@ -8,17 +7,17 @@ pub fn validate_create_link_agent_to_username_attestations( target_address: AnyLinkableHash, _tag: LinkTag, ) -> ExternResult { - // Only the authority can create link - let authority_agent = get_authority_agent()?; - if action.author != authority_agent { + // Check the entry type for the given action hash + let action_hash = ActionHash::try_from(target_address).map_err(|e| wasm_error!(e))?; + let record = must_get_valid_record(action_hash)?; + + // Only the attestation author can create links to it + if &action.author != record.action().author() { return Ok(ValidateCallbackResult::Invalid( - "Only the Username Registry Authority can create attestation links".into(), + "Only the username attestation author can create links to it".into(), )); } - // Check the entry type for the given action hash - let action_hash = ActionHash::try_from(target_address).map_err(|e| wasm_error!(e))?; - let record = must_get_valid_record(action_hash)?; let _username_attestation: UsernameAttestation = record .entry() .to_app_option() diff --git a/crates/username_registry_validation/src/evm_signing_offer.rs b/crates/username_registry_validation/src/evm_signing_offer.rs index b43db17..f9bf1c9 100644 --- a/crates/username_registry_validation/src/evm_signing_offer.rs +++ b/crates/username_registry_validation/src/evm_signing_offer.rs @@ -31,7 +31,7 @@ pub fn validate_create_signed_evm_signing_offer( match signed_evm_signing_offer .signature - .recover_address_from_msg(&message) + .recover_address_from_msg(message) { Ok(recovered_address) => { if recovered_address == signed_evm_signing_offer.signer { diff --git a/crates/username_registry_validation/src/external_id_attestation.rs b/crates/username_registry_validation/src/external_id_attestation.rs index 73bd571..c054936 100644 --- a/crates/username_registry_validation/src/external_id_attestation.rs +++ b/crates/username_registry_validation/src/external_id_attestation.rs @@ -1,18 +1,9 @@ use hdi::prelude::*; use holoom_types::ExternalIdAttestation; -use username_registry_utils::get_authority_agent; pub fn validate_create_external_id_attestation( - action: EntryCreationAction, + _action: EntryCreationAction, _external_id_attestation: ExternalIdAttestation, ) -> ExternResult { - // Only the authority can publish - let authority_agent = get_authority_agent()?; - if action.author() != &authority_agent { - return Ok(ValidateCallbackResult::Invalid( - "Only the Username Registry Authority can create external ID attestations".into(), - )); - } - Ok(ValidateCallbackResult::Valid) } diff --git a/crates/username_registry_validation/src/external_id_to_attestation.rs b/crates/username_registry_validation/src/external_id_to_attestation.rs index d9f20e3..3517e5d 100644 --- a/crates/username_registry_validation/src/external_id_to_attestation.rs +++ b/crates/username_registry_validation/src/external_id_to_attestation.rs @@ -1,6 +1,6 @@ use hdi::prelude::*; use holoom_types::ExternalIdAttestation; -use username_registry_utils::{get_authority_agent, hash_identifier}; +use username_registry_utils::hash_identifier; pub fn validate_create_link_external_id_to_attestation( action: CreateLink, @@ -8,14 +8,6 @@ pub fn validate_create_link_external_id_to_attestation( target_address: AnyLinkableHash, _tag: LinkTag, ) -> ExternResult { - // Only the authority can create link - let authority_agent = get_authority_agent()?; - if action.author != authority_agent { - return Ok(ValidateCallbackResult::Invalid( - "Only the Username Registry Authority can create external ID attestation links".into(), - )); - } - // Check the entry type for the given action hash let action_hash = ActionHash::try_from(target_address).map_err(|e| wasm_error!(e))?; let record = must_get_valid_record(action_hash)?; @@ -27,6 +19,13 @@ pub fn validate_create_link_external_id_to_attestation( "Linked action must reference an entry" ))))?; + // Only the attestation author can create links to it + if &action.author != record.action().author() { + return Ok(ValidateCallbackResult::Invalid( + "Only the external ID attestation author can create links to it".into(), + )); + } + let expected_base_address = hash_identifier(external_id_attestation.external_id)?; if AnyLinkableHash::from(expected_base_address) != base_address { return Ok(ValidateCallbackResult::Invalid( diff --git a/crates/username_registry_validation/src/username_attestation.rs b/crates/username_registry_validation/src/username_attestation.rs index 201b85e..a81877f 100644 --- a/crates/username_registry_validation/src/username_attestation.rs +++ b/crates/username_registry_validation/src/username_attestation.rs @@ -1,6 +1,5 @@ use hdi::prelude::*; use holoom_types::UsernameAttestation; -use username_registry_utils::get_authority_agent; pub fn validate_create_username_attestation( action: EntryCreationAction, @@ -14,19 +13,11 @@ pub fn validate_create_username_attestation( )); } - // Only the authority can publish - let authority_agent = get_authority_agent()?; - if action.author() != &authority_agent { - return Ok(ValidateCallbackResult::Invalid( - "Only the Username Registry Authority can create attestations".into(), - )); - } - - let agent_activity = must_get_agent_activity( - authority_agent.clone(), + let author_activity = must_get_agent_activity( + action.author().clone(), ChainFilter::new(action.prev_action().clone()).include_cached_entries(), )?; - let created_usernames: Vec = agent_activity + let created_usernames: Vec = author_activity .into_iter() .filter_map( |agent_activity| match agent_activity.action.action().action_type() { diff --git a/crates/version_coordinator/build.rs b/crates/version_coordinator/build.rs index fb25acf..17be54a 100644 --- a/crates/version_coordinator/build.rs +++ b/crates/version_coordinator/build.rs @@ -1,7 +1,7 @@ use std::process::Command; fn main() { let output = Command::new("git") - .args(&["rev-parse", "HEAD"]) + .args(["rev-parse", "HEAD"]) .output() .expect("Can get git revision"); let git_hash = String::from_utf8(output.stdout).unwrap(); diff --git a/packages/authority/rollup.browser.config.ts b/packages/authority/rollup.browser.config.ts index 7c0c88d..18d2158 100644 --- a/packages/authority/rollup.browser.config.ts +++ b/packages/authority/rollup.browser.config.ts @@ -2,6 +2,7 @@ import json from "@rollup/plugin-json"; import typescript from "@rollup/plugin-typescript"; import * as fs from "fs"; import cleanup from "rollup-plugin-cleanup"; +import { RollupOptions } from "rollup"; const pkg = JSON.parse(fs.readFileSync("./package.json", "utf-8")); const banner = `/** @@ -12,7 +13,7 @@ const banner = `/** * @see [Github]{@link ${pkg.homepage}} */`; -export default { +const config: RollupOptions = { input: "src/index.ts", output: [ { @@ -21,7 +22,7 @@ export default { banner, }, ], - external: [...Object.keys(pkg.dependencies)], + external: [...Object.keys(pkg.dependencies), "viem/accounts"], plugins: [ typescript({ tsconfig: "./build.tsconfig.json", @@ -29,4 +30,9 @@ export default { cleanup({ comments: "jsdoc" }), json(), ], + onwarn: (warning) => { + throw new Error(warning.message); + }, }; + +export default config; diff --git a/packages/authority/rollup.node.config.ts b/packages/authority/rollup.node.config.ts index 55c266f..103fba8 100644 --- a/packages/authority/rollup.node.config.ts +++ b/packages/authority/rollup.node.config.ts @@ -27,7 +27,7 @@ export default { banner, }, ], - external: [...Object.keys(pkg.dependencies)], + external: [...Object.keys(pkg.dependencies), "viem/accounts"], plugins: [ typescript({ tsconfig: "./build.tsconfig.json", diff --git a/packages/authority/src/evm-bytes-signer/evm-bytes-signer-client.ts b/packages/authority/src/evm-bytes-signer/evm-bytes-signer-client.ts index 121128a..a783de7 100644 --- a/packages/authority/src/evm-bytes-signer/evm-bytes-signer-client.ts +++ b/packages/authority/src/evm-bytes-signer/evm-bytes-signer-client.ts @@ -29,6 +29,13 @@ export class EvmBytesSignerClient { this.unsubscribe(); } + /** + * One-time setup that open this agent to receiving EVM signature requests + */ + async setup() { + await this.usernameRegistryCoordinator.evmSignatureProviderSetup(); + } + handleAppSignal(signal: AppSignal) { console.log("received signal", signal); if (signal.zome_name !== "username_registry") return; diff --git a/packages/authority/src/external-id-attestor/external-id-attestor-client.ts b/packages/authority/src/external-id-attestor/external-id-attestor-client.ts index ee695f1..1384b77 100644 --- a/packages/authority/src/external-id-attestor/external-id-attestor-client.ts +++ b/packages/authority/src/external-id-attestor/external-id-attestor-client.ts @@ -43,6 +43,13 @@ export class ExternalIdAttestorClient { this.unsubscribe(); } + /** + * One-time setup that makes the authority available to attest + */ + async setup() { + await this.usernameRegistryCoordinator.externalIdAuthoritySetup(); + } + handleAppSignal(signal: AppSignal) { console.log("received signal", signal); if (signal.zome_name !== "username_registry") return; diff --git a/packages/authority/src/query/service.ts b/packages/authority/src/query/service.ts index 154f75b..c461d9b 100644 --- a/packages/authority/src/query/service.ts +++ b/packages/authority/src/query/service.ts @@ -1,13 +1,8 @@ -import dotenv from "dotenv"; import { - AdminWebsocket, - AgentPubKey, AppClient, - AppWebsocket, decodeHashFromBase64, encodeHashToBase64, } from "@holochain/client"; -import express, { Request, Response } from "express"; import { UsernameRegistryMetadataResponse, UsernameRegistryResponse, @@ -32,7 +27,7 @@ export class QueryService { async getUsernameRegistry() { const records = - await this.usernameRegistryCoordinator.getAllUsernameAttestations(); + await this.usernameRegistryCoordinator.getAllAuthoredUsernameAttestations(); const response: UsernameRegistryResponse = { success: true, items: records.map((record) => { diff --git a/packages/client/rollup.browser.config.ts b/packages/client/rollup.browser.config.ts index 7c0c88d..90f1f40 100644 --- a/packages/client/rollup.browser.config.ts +++ b/packages/client/rollup.browser.config.ts @@ -2,6 +2,7 @@ import json from "@rollup/plugin-json"; import typescript from "@rollup/plugin-typescript"; import * as fs from "fs"; import cleanup from "rollup-plugin-cleanup"; +import { RollupOptions } from "rollup"; const pkg = JSON.parse(fs.readFileSync("./package.json", "utf-8")); const banner = `/** @@ -12,7 +13,7 @@ const banner = `/** * @see [Github]{@link ${pkg.homepage}} */`; -export default { +const config: RollupOptions = { input: "src/index.ts", output: [ { @@ -21,7 +22,10 @@ export default { banner, }, ], - external: [...Object.keys(pkg.dependencies)], + external: [ + ...Object.keys(pkg.dependencies), + ...Object.keys(pkg.peerDependencies), + ], plugins: [ typescript({ tsconfig: "./build.tsconfig.json", @@ -29,4 +33,9 @@ export default { cleanup({ comments: "jsdoc" }), json(), ], + onwarn: (warning) => { + throw new Error(warning.message); + }, }; + +export default config; diff --git a/packages/client/rollup.node.config.ts b/packages/client/rollup.node.config.ts index 55c266f..22c1a7c 100644 --- a/packages/client/rollup.node.config.ts +++ b/packages/client/rollup.node.config.ts @@ -27,7 +27,10 @@ export default { banner, }, ], - external: [...Object.keys(pkg.dependencies)], + external: [ + ...Object.keys(pkg.dependencies), + ...Object.keys(pkg.peerDependencies), + ], plugins: [ typescript({ tsconfig: "./build.tsconfig.json", diff --git a/packages/client/src/external-id-attestation-requestor-client.ts b/packages/client/src/external-id-attestation-requestor-client.ts index b38fe9e..aaf9f4d 100644 --- a/packages/client/src/external-id-attestation-requestor-client.ts +++ b/packages/client/src/external-id-attestation-requestor-client.ts @@ -3,6 +3,7 @@ import { type Record, type AppClient, SignalType, + AgentPubKey, } from "@holochain/client"; import { v4 as uuidV4 } from "uuid"; import { @@ -48,7 +49,10 @@ class RequestResolver { export class ExternalIdAttestationRequestorClient { usernameRegistryCoordinator: UsernameRegistryCoordinator; private unsubscribe: () => void; - constructor(readonly appClient: AppClient) { + constructor( + readonly appClient: AppClient, + readonly attestationProvider: AgentPubKey + ) { this.usernameRegistryCoordinator = new UsernameRegistryCoordinator( appClient ); @@ -91,6 +95,7 @@ export class ExternalIdAttestationRequestorClient { request_id: requestId, code_verifier: codeVerifier, code, + authority: this.attestationProvider, }); const attestation = await resolver.until(); diff --git a/packages/client/src/holoom-client.ts b/packages/client/src/holoom-client.ts index 2334d63..c4baf7d 100644 --- a/packages/client/src/holoom-client.ts +++ b/packages/client/src/holoom-client.ts @@ -1,4 +1,4 @@ -import type { ActionHash, AppClient } from "@holochain/client"; +import type { ActionHash, AgentPubKey, AppClient } from "@holochain/client"; import type { PublicKey as SolanaPublicKey } from "@solana/web3.js"; import { PingCoordinator, @@ -27,7 +27,10 @@ import { EvmBytesSignatureRequestorClient } from "./evm-bytes-signature-requesto export class HoloomClient { private usernameRegistryCoordinator: UsernameRegistryCoordinator; private pingCoordinator: PingCoordinator; - constructor(private appClient: AppClient) { + constructor( + private appClient: AppClient, + readonly usernameAttestationProvider: AgentPubKey + ) { this.usernameRegistryCoordinator = new UsernameRegistryCoordinator( appClient ); @@ -61,9 +64,10 @@ export class HoloomClient { */ async getUsername(): Promise { const record = - await this.usernameRegistryCoordinator.getUsernameAttestationForAgent( - this.appClient.myPubKey - ); + await this.usernameRegistryCoordinator.getUsernameAttestationForAgent({ + agent: this.appClient.myPubKey, + trusted_authorities: [this.usernameAttestationProvider], + }); if (!record) { return null; } @@ -79,7 +83,10 @@ export class HoloomClient { * agent, which checks the signature and attests the username's uniqueness. */ async registerUsername(username: string) { - await this.usernameRegistryCoordinator.signUsernameToAttest(username); + await this.usernameRegistryCoordinator.signUsernameAndRequestAttestation({ + authority: this.usernameAttestationProvider, + username, + }); } /** diff --git a/packages/tryorama/src/e2e/evm-wallet-binding.test.ts b/packages/tryorama/src/e2e/evm-wallet-binding.test.ts index 839bc95..7b97fcb 100644 --- a/packages/tryorama/src/e2e/evm-wallet-binding.test.ts +++ b/packages/tryorama/src/e2e/evm-wallet-binding.test.ts @@ -1,6 +1,6 @@ import { runScenario } from "@holochain/tryorama"; import { expect, test } from "vitest"; -import { setupAuthorityAndAlice } from "../utils/setup-happ"; +import { setupPlayer } from "../utils/setup-happ"; import { AppClient, encodeHashToBase64 } from "@holochain/client"; import { QueryService } from "@holoom/authority"; import { forMs, HoloomClient } from "@holoom/client"; @@ -8,9 +8,14 @@ import { privateKeyToAccount } from "viem/accounts"; test("e2e evm wallet binding", async () => { await runScenario(async (scenario) => { - const { authority, alice } = await setupAuthorityAndAlice(scenario); + const [authority] = await setupPlayer(scenario); + const [alice] = await setupPlayer(scenario); await scenario.shareAllAgents(); - const aliceHoloomClient = new HoloomClient(alice.appWs as AppClient); + + const aliceHoloomClient = new HoloomClient( + alice.appWs as AppClient, + authority.agentPubKey + ); const alicePubkeyB64 = encodeHashToBase64(alice.agentPubKey); // This service is is intended to be run as a sandboxed microservice, but diff --git a/packages/tryorama/src/e2e/external-id.test.ts b/packages/tryorama/src/e2e/external-id.test.ts index 673975b..f30f16d 100644 --- a/packages/tryorama/src/e2e/external-id.test.ts +++ b/packages/tryorama/src/e2e/external-id.test.ts @@ -1,6 +1,6 @@ import { runScenario } from "@holochain/tryorama"; import { expect, test, vi } from "vitest"; -import { setupAuthorityAndAlice } from "../utils/setup-happ"; +import { setupPlayer } from "../utils/setup-happ"; import { AppClient } from "@holochain/client"; import { ExternalIdAttestorClient, @@ -50,20 +50,32 @@ function createExternalIdAttestorService(appClient: AppClient) { accessTokenAssessor ); - return { destroy: () => externalIdAttestorClient.destroy() }; + return { + setup: () => externalIdAttestorClient.setup(), + destroy: () => externalIdAttestorClient.destroy(), + }; } test("e2e external-id", async () => { await runScenario(async (scenario) => { - const { authority, alice } = await setupAuthorityAndAlice(scenario); + const [authority, authorityCoordinators] = await setupPlayer(scenario); + const [alice] = await setupPlayer(scenario); await scenario.shareAllAgents(); + + // Open authority to ingest attestation requests + await authorityCoordinators.usernameRegistry.externalIdAuthoritySetup(); + const externalIdAttestationRequesterClient = - new ExternalIdAttestationRequestorClient(alice.appWs as AppClient); + new ExternalIdAttestationRequestorClient( + alice.appWs as AppClient, + authority.agentPubKey + ); // Listens for incoming request signals const externalIdAttestorService = createExternalIdAttestorService( authority.appWs as AppClient ); + await externalIdAttestorService.setup(); // This PKCE Authorisation Code flow is intended to be run in a browser. // This test doesn't cover browser redirect behaviour, and instead we'll diff --git a/packages/tryorama/src/e2e/metadata.test.ts b/packages/tryorama/src/e2e/metadata.test.ts index 4017a9f..4e6020f 100644 --- a/packages/tryorama/src/e2e/metadata.test.ts +++ b/packages/tryorama/src/e2e/metadata.test.ts @@ -1,15 +1,19 @@ import { runScenario } from "@holochain/tryorama"; import { expect, test } from "vitest"; -import { setupAuthorityAndAlice } from "../utils/setup-happ"; +import { setupPlayer } from "../utils/setup-happ"; import { AppClient, encodeHashToBase64 } from "@holochain/client"; import { QueryService } from "@holoom/authority"; import { forMs, HoloomClient } from "@holoom/client"; test("e2e metadata", async () => { await runScenario(async (scenario) => { - const { authority, alice } = await setupAuthorityAndAlice(scenario); + const [authority] = await setupPlayer(scenario); + const [alice] = await setupPlayer(scenario); await scenario.shareAllAgents(); - const aliceHoloomClient = new HoloomClient(alice.appWs as AppClient); + const aliceHoloomClient = new HoloomClient( + alice.appWs as AppClient, + authority.agentPubKey + ); const alicePubkeyB64 = encodeHashToBase64(alice.agentPubKey); // This service is is intended to be run as a sandboxed microservice, but diff --git a/packages/tryorama/src/e2e/signing-offer.test.ts b/packages/tryorama/src/e2e/signing-offer.test.ts index b66837f..79f7b0b 100644 --- a/packages/tryorama/src/e2e/signing-offer.test.ts +++ b/packages/tryorama/src/e2e/signing-offer.test.ts @@ -1,13 +1,12 @@ import { runScenario, dhtSync } from "@holochain/tryorama"; import { expect, test } from "vitest"; -import { setupAuthorityAndAlice } from "../utils/setup-happ"; +import { setupPlayer } from "../utils/setup-happ"; import { ActionHash, AppClient } from "@holochain/client"; import { EvmBytesSignerClient, BytesSigner, OfferCreator, } from "@holoom/authority"; -import { bindCoordinators } from "../utils/bindings"; import { flattenEvmSignatureToHex, forMs, HoloomClient } from "@holoom/client"; import { encodePacked, keccak256, verifyMessage } from "viem"; @@ -26,17 +25,23 @@ function createEvmBytesSignerService(appClient: AppClient) { // Intended to be used as an admin controller in server app const offerCreator = new OfferCreator(appClient, bytesSigner); - return { offerCreator, destroy: () => evmBytesSignerClient.destroy() }; + return { + offerCreator, + setup: () => evmBytesSignerClient.setup(), + destroy: () => evmBytesSignerClient.destroy(), + }; } test("e2e signing offer", async () => { await runScenario(async (scenario) => { - const { authority, alice } = await setupAuthorityAndAlice(scenario); + const [authority] = await setupPlayer(scenario); + const [alice, aliceCoordinators] = await setupPlayer(scenario); await scenario.shareAllAgents(); - const aliceCoordinators = bindCoordinators(alice); + const evmBytesSignerService = createEvmBytesSignerService( authority.appWs as AppClient ); + await evmBytesSignerService.setup(); // It doesn't really matter who creates the recipe, since it's the signing // offer to come that denotes who trusts it. diff --git a/packages/tryorama/src/e2e/username.test.ts b/packages/tryorama/src/e2e/username.test.ts index 9f78ffe..4548af9 100644 --- a/packages/tryorama/src/e2e/username.test.ts +++ b/packages/tryorama/src/e2e/username.test.ts @@ -1,22 +1,28 @@ import { runScenario } from "@holochain/tryorama"; import { expect, test } from "vitest"; -import { setupAuthorityAndAlice } from "../utils/setup-happ"; -import { AppClient, encodeHashToBase64 } from "@holochain/client"; +import { setupPlayer } from "../utils/setup-happ"; +import { AppClient } from "@holochain/client"; import { QueryService, UsernameRegistryResponse } from "@holoom/authority"; import { HoloomClient } from "@holoom/client"; test("e2e username", async () => { await runScenario(async (scenario) => { - const { authority, alice } = await setupAuthorityAndAlice(scenario); + const [authority, authorityCoordinator] = await setupPlayer(scenario); + const [alice] = await setupPlayer(scenario); await scenario.shareAllAgents(); - const aliceHoloomClient = new HoloomClient(alice.appWs as AppClient); - const alicePubkeyB64 = encodeHashToBase64(alice.agentPubKey); + const aliceHoloomClient = new HoloomClient( + alice.appWs as AppClient, + authority.agentPubKey + ); // This service is is intended to be run as a sandboxed microservice, but // that is not a concern of this test, hence we cheat and instantiate // locally. const queryService = new QueryService(authority.appWs as AppClient); + // Open authority to attestation requests + await authorityCoordinator.usernameRegistry.usernameAuthoritySetup(); + // Starts with no username await expect(aliceHoloomClient.getUsername()).resolves.toBeNull(); diff --git a/packages/tryorama/src/external_attestation/all_can_get_external_id_attestation_for_agent.test.ts b/packages/tryorama/src/external_attestation/all_can_get_external_id_attestation_for_agent.test.ts index ec0e9c9..21dd203 100644 --- a/packages/tryorama/src/external_attestation/all_can_get_external_id_attestation_for_agent.test.ts +++ b/packages/tryorama/src/external_attestation/all_can_get_external_id_attestation_for_agent.test.ts @@ -1,18 +1,18 @@ import { expect, test } from "vitest"; import { dhtSync, runScenario } from "@holochain/tryorama"; -import { setupAuthorityAndAlice } from "../utils/setup-happ.js"; +import { setupPlayer } from "../utils/setup-happ.js"; import { decodeAppEntry, ExternalIdAttestation } from "@holoom/client"; test("All can get external id attestation for agent", async () => { await runScenario(async (scenario) => { - const { authorityCoordinators, authority, alice } = - await setupAuthorityAndAlice(scenario); + const [authority, authorityCoordinators] = await setupPlayer(scenario); + const [alice] = await setupPlayer(scenario); await scenario.shareAllAgents(); // Authority's complete list of attestations initially empty await expect( - authorityCoordinators.usernameRegistry.getAllExternalIdAhs() + authorityCoordinators.usernameRegistry.getAllAuthoredUsernameAttestations() ).resolves.toEqual([]); // Authority creates an external_id Attestation @@ -25,9 +25,10 @@ test("All can get external id attestation for agent", async () => { // Authority gets the external_id Attestation const record1 = - await authorityCoordinators.usernameRegistry.getAttestationForExternalId( - "4546" - ); + await authorityCoordinators.usernameRegistry.getAttestationForExternalId({ + external_id: "4546", + trusted_authorities: [authority.agentPubKey], + }); const entry1 = decodeAppEntry(record1); expect(entry1.external_id).toBe("4546"); expect(entry1.internal_pubkey).toEqual(alice.agentPubKey); @@ -37,7 +38,10 @@ test("All can get external id attestation for agent", async () => { // Alice gets the external_id Attestation const records = await authorityCoordinators.usernameRegistry.getExternalIdAttestationsForAgent( - alice.agentPubKey + { + agent_pubkey: alice.agentPubKey, + trusted_authorities: [authority.agentPubKey], + } ); expect(records.length).toBe(1); const entry2 = decodeAppEntry(records[0]); @@ -46,7 +50,7 @@ test("All can get external id attestation for agent", async () => { // Authority can see the attestation in their complete list await expect( - authorityCoordinators.usernameRegistry.getAllExternalIdAhs() + authorityCoordinators.usernameRegistry.getAllAuthoredExternalIdAhs() ).resolves.toEqual([record1.signed_action.hashed.hash]); }); }); diff --git a/packages/tryorama/src/external_attestation/all_can_get_external_id_attestations.test.ts b/packages/tryorama/src/external_attestation/all_can_get_external_id_attestations.test.ts index 07c10f7..dc4b6d7 100644 --- a/packages/tryorama/src/external_attestation/all_can_get_external_id_attestations.test.ts +++ b/packages/tryorama/src/external_attestation/all_can_get_external_id_attestations.test.ts @@ -1,12 +1,12 @@ import { expect, test } from "vitest"; import { dhtSync, runScenario } from "@holochain/tryorama"; -import { setupAuthorityAndAlice } from "../utils/setup-happ.js"; +import { setupPlayer } from "../utils/setup-happ.js"; test("All can get external id attestations", async () => { await runScenario(async (scenario) => { - const { authorityCoordinators, aliceCoordinators, authority, alice } = - await setupAuthorityAndAlice(scenario); + const [authority, authorityCoordinators] = await setupPlayer(scenario); + const [alice, aliceCoordinators] = await setupPlayer(scenario); await scenario.shareAllAgents(); // Authority creates a external_id Attestation diff --git a/packages/tryorama/src/external_attestation/cannot_get_external_id_attestation_for_agent_that_doesnt_exist.test.ts b/packages/tryorama/src/external_attestation/cannot_get_external_id_attestation_for_agent_that_doesnt_exist.test.ts index f0f1c91..1ce6d61 100644 --- a/packages/tryorama/src/external_attestation/cannot_get_external_id_attestation_for_agent_that_doesnt_exist.test.ts +++ b/packages/tryorama/src/external_attestation/cannot_get_external_id_attestation_for_agent_that_doesnt_exist.test.ts @@ -1,18 +1,19 @@ import { expect, test } from "vitest"; import { runScenario } from "@holochain/tryorama"; -import { setupAuthorityOnly } from "../utils/setup-happ.js"; +import { setupPlayer } from "../utils/setup-happ.js"; import { fakeAgentPubKey } from "@holochain/client"; test("Cannot get external id attestation for agent that doesn't exist", async () => { await runScenario(async (scenario) => { - const { authorityCoordinators } = await setupAuthorityOnly(scenario); + const [authority, authorityCoordinators] = await setupPlayer(scenario); // Authority tries to get external_id Attestation await expect( - authorityCoordinators.usernameRegistry.getExternalIdAttestationsForAgent( - await fakeAgentPubKey(0) - ) + authorityCoordinators.usernameRegistry.getExternalIdAttestationsForAgent({ + agent_pubkey: await fakeAgentPubKey(0), + trusted_authorities: [authority.agentPubKey], + }) ).resolves.toEqual([]); }); }); diff --git a/packages/tryorama/src/external_attestation/nobody_can_delete_external_id_attestation.test.ts b/packages/tryorama/src/external_attestation/nobody_can_delete_external_id_attestation.test.ts index c946348..ca2bc43 100644 --- a/packages/tryorama/src/external_attestation/nobody_can_delete_external_id_attestation.test.ts +++ b/packages/tryorama/src/external_attestation/nobody_can_delete_external_id_attestation.test.ts @@ -1,12 +1,12 @@ import { expect, test } from "vitest"; import { dhtSync, runScenario } from "@holochain/tryorama"; -import { setupAuthorityAndAlice } from "../utils/setup-happ.js"; +import { setupPlayer } from "../utils/setup-happ.js"; test("Nobody can delete external id attestation", async () => { await runScenario(async (scenario) => { - const { authorityCoordinators, aliceCoordinators, authority, alice } = - await setupAuthorityAndAlice(scenario); + const [authority, authorityCoordinators] = await setupPlayer(scenario); + const [alice, aliceCoordinators] = await setupPlayer(scenario); await scenario.shareAllAgents(); // Authority creates a external_id Attestation diff --git a/packages/tryorama/src/external_attestation/only_authority_can_confirm_request.test.ts b/packages/tryorama/src/external_attestation/only_authority_can_confirm_request.test.ts deleted file mode 100644 index 03c2dab..0000000 --- a/packages/tryorama/src/external_attestation/only_authority_can_confirm_request.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { expect, test } from "vitest"; -import { runScenario } from "@holochain/tryorama"; - -import { setupAuthorityAndAlice } from "../utils/setup-happ.js"; - -test("Only authority can confirm request", async () => { - await runScenario(async (scenario) => { - const { authorityCoordinators, aliceCoordinators, alice, authority } = - await setupAuthorityAndAlice(scenario); - await scenario.shareAllAgents(); - - // Authority confirms a request - await expect( - authorityCoordinators.usernameRegistry.confirmExternalIdRequest({ - request_id: "1234", - external_id: "4567", - display_name: "alice", - requestor: alice.agentPubKey, - }) - ).resolves.not.toThrow(); - - // Alice cannot confirm a request - await expect( - aliceCoordinators.usernameRegistry.confirmExternalIdRequest({ - request_id: "1234", - external_id: "4567", - display_name: "alice", - requestor: alice.agentPubKey, - }) - ).rejects.toThrow(); - }); -}); diff --git a/packages/tryorama/src/external_attestation/only_authority_can_create_external_id_attestation.test.ts b/packages/tryorama/src/external_attestation/only_authority_can_create_external_id_attestation.test.ts deleted file mode 100644 index cff45e2..0000000 --- a/packages/tryorama/src/external_attestation/only_authority_can_create_external_id_attestation.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { expect, test } from "vitest"; -import { runScenario } from "@holochain/tryorama"; - -import { setupAuthorityAndAlice } from "../utils/setup-happ.js"; - -test("Only authority can create external id attestation", async () => { - await runScenario(async (scenario) => { - const { authorityCoordinators, aliceCoordinators, alice } = - await setupAuthorityAndAlice(scenario); - await scenario.shareAllAgents(); - - // Authority creates a external id Attestation for alice - await expect( - authorityCoordinators.usernameRegistry.createExternalIdAttestation({ - request_id: "1234", - internal_pubkey: alice.agentPubKey, - external_id: "4546", - display_name: "alice", - }) - ).resolves.not.toThrow(); - - // Alice cannot create an external id Attestation - await expect( - aliceCoordinators.usernameRegistry.createExternalIdAttestation({ - request_id: "9876", - internal_pubkey: alice.agentPubKey, - external_id: "abcd", - display_name: "alice2", - }) - ).rejects.toThrow(); - }); -}); diff --git a/packages/tryorama/src/metadata/users_can_only_update_their_own_metadata.test.ts b/packages/tryorama/src/metadata/users_can_only_update_their_own_metadata.test.ts index 02241db..0a2ee14 100644 --- a/packages/tryorama/src/metadata/users_can_only_update_their_own_metadata.test.ts +++ b/packages/tryorama/src/metadata/users_can_only_update_their_own_metadata.test.ts @@ -1,12 +1,12 @@ import { expect, test } from "vitest"; import { dhtSync, runScenario } from "@holochain/tryorama"; -import { setupAuthorityAndAlice } from "../utils/setup-happ.js"; +import { setupPlayer } from "../utils/setup-happ.js"; test("Users can only update their own metadata", async () => { await runScenario(async (scenario) => { - const { authorityCoordinators, aliceCoordinators, authority, alice } = - await setupAuthorityAndAlice(scenario); + const [authority, authorityCoordinators] = await setupPlayer(scenario); + const [alice, aliceCoordinators] = await setupPlayer(scenario); await scenario.shareAllAgents(); // Alice starts with no metadata diff --git a/packages/tryorama/src/oracle/can_fetch_documents_by_relation.test.ts b/packages/tryorama/src/oracle/can_fetch_documents_by_relation.test.ts index 6fbc670..ddb3c20 100644 --- a/packages/tryorama/src/oracle/can_fetch_documents_by_relation.test.ts +++ b/packages/tryorama/src/oracle/can_fetch_documents_by_relation.test.ts @@ -1,12 +1,11 @@ import { expect, test } from "vitest"; import { runScenario } from "@holochain/tryorama"; -import { setupAuthorityOnly } from "../utils/setup-happ.js"; -import { fakeAgentPubKey } from "@holochain/client"; +import { setupPlayer } from "../utils/setup-happ.js"; test("Can fetch documents by relation", async () => { await runScenario(async (scenario) => { - const { authorityCoordinators } = await setupAuthorityOnly(scenario); + const [_, authorityCoordinators] = await setupPlayer(scenario); await expect( authorityCoordinators.usernameRegistry.createOracleDocument({ diff --git a/packages/tryorama/src/recipe/can_execute_basic_recipe.test.ts b/packages/tryorama/src/recipe/can_execute_basic_recipe.test.ts index f33d765..1c42371 100644 --- a/packages/tryorama/src/recipe/can_execute_basic_recipe.test.ts +++ b/packages/tryorama/src/recipe/can_execute_basic_recipe.test.ts @@ -2,13 +2,13 @@ import { expect, test } from "vitest"; import { dhtSync, runScenario } from "@holochain/tryorama"; import { RecipeExecution } from "@holoom/types"; -import { setupAuthorityAndAlice } from "../utils/setup-happ.js"; +import { setupPlayer } from "../utils/setup-happ.js"; import { decodeAppEntry } from "@holoom/client"; test("Can execute basic recipe", async () => { await runScenario(async (scenario) => { - const { authorityCoordinators, aliceCoordinators, authority, alice } = - await setupAuthorityAndAlice(scenario); + const [authority, authorityCoordinators] = await setupPlayer(scenario); + const [alice, aliceCoordinators] = await setupPlayer(scenario); await scenario.shareAllAgents(); // Materials: diff --git a/packages/tryorama/src/recipe/cannot_use_untrusted_docs.test.ts b/packages/tryorama/src/recipe/cannot_use_untrusted_docs.test.ts index 9ba9d4b..c36e8d2 100644 --- a/packages/tryorama/src/recipe/cannot_use_untrusted_docs.test.ts +++ b/packages/tryorama/src/recipe/cannot_use_untrusted_docs.test.ts @@ -1,12 +1,12 @@ import { expect, test } from "vitest"; import { runScenario } from "@holochain/tryorama"; -import { setupAuthorityOnly } from "../utils/setup-happ.js"; +import { setupPlayer } from "../utils/setup-happ.js"; import { fakeAgentPubKey } from "@holochain/client"; test("Cannot use untrusted docs", async () => { await runScenario(async (scenario) => { - const { authorityCoordinators } = await setupAuthorityOnly(scenario); + const [_, authorityCoordinators] = await setupPlayer(scenario); // Recipe that return a doc called 'foo' via `GetLatestDocWithIdentifier` const singleDocRecipeRecord = diff --git a/packages/tryorama/src/signer/sign.test.ts b/packages/tryorama/src/signer/sign.test.ts index c05707b..ff4bf9c 100644 --- a/packages/tryorama/src/signer/sign.test.ts +++ b/packages/tryorama/src/signer/sign.test.ts @@ -1,7 +1,7 @@ import { expect, test } from "vitest"; import { runScenario } from "@holochain/tryorama"; -import { setupAliceOnly } from "../utils/setup-happ.js"; +import { setupPlayer } from "../utils/setup-happ.js"; import { sha512 } from "@noble/hashes/sha512"; import * as ed from "@noble/ed25519"; import { encode } from "@msgpack/msgpack"; @@ -13,7 +13,7 @@ ed.etc.sha512Async = (...m) => test("Sign message and verify signature", async () => { await runScenario(async (scenario) => { - const { alice, aliceCoordinators } = await setupAliceOnly(scenario); + const [alice, aliceCoordinators] = await setupPlayer(scenario); const message = new Array(2048).fill(0); const signature = await aliceCoordinators.signer.signMessage(message); diff --git a/packages/tryorama/src/username_attestation/all_can_get_username_attestation_for_agent.test.ts b/packages/tryorama/src/username_attestation/all_can_get_username_attestation_for_agent.test.ts index 43da15b..6a278eb 100644 --- a/packages/tryorama/src/username_attestation/all_can_get_username_attestation_for_agent.test.ts +++ b/packages/tryorama/src/username_attestation/all_can_get_username_attestation_for_agent.test.ts @@ -1,19 +1,19 @@ import { expect, test } from "vitest"; import { dhtSync, runScenario } from "@holochain/tryorama"; -import { setupAuthorityAndAlice } from "../utils/setup-happ.js"; +import { setupPlayer } from "../utils/setup-happ.js"; import { fakeAgentPubKey } from "@holochain/client"; import { decodeAppEntry, UsernameAttestation } from "@holoom/client"; test("All can get username attestation for agent", async () => { await runScenario(async (scenario) => { - const { authorityCoordinators, aliceCoordinators, authority, alice } = - await setupAuthorityAndAlice(scenario); + const [authority, authorityCoordinators] = await setupPlayer(scenario); + const [alice, aliceCoordinators] = await setupPlayer(scenario); await scenario.shareAllAgents(); // Authority's complete list of attestations initially empty await expect( - authorityCoordinators.usernameRegistry.getAllUsernameAttestations() + authorityCoordinators.usernameRegistry.getAllAuthoredUsernameAttestations() ).resolves.toEqual([]); // Authority creates a UsernameAttestation @@ -25,7 +25,10 @@ test("All can get username attestation for agent", async () => { // Authority gets the UsernameAttestation const record1 = await authorityCoordinators.usernameRegistry.getUsernameAttestationForAgent( - await fakeAgentPubKey(1) + { + agent: await fakeAgentPubKey(1), + trusted_authorities: [authority.agentPubKey], + } ); const entry1 = decodeAppEntry(record1); expect(entry1).toEqual({ @@ -37,9 +40,10 @@ test("All can get username attestation for agent", async () => { // Alice gets the UsernameAttestation const record2 = - await aliceCoordinators.usernameRegistry.getUsernameAttestationForAgent( - await fakeAgentPubKey(1) - ); + await aliceCoordinators.usernameRegistry.getUsernameAttestationForAgent({ + agent: await fakeAgentPubKey(1), + trusted_authorities: [authority.agentPubKey], + }); const entry2 = decodeAppEntry(record2); expect(entry2).toEqual({ username: "username1", @@ -48,7 +52,7 @@ test("All can get username attestation for agent", async () => { // Authority can see the attestation in their complete list await expect( - authorityCoordinators.usernameRegistry.getAllUsernameAttestations() + authorityCoordinators.usernameRegistry.getAllAuthoredUsernameAttestations() ).resolves.toEqual([record2]); }); }); diff --git a/packages/tryorama/src/username_attestation/all_can_get_username_attestations.test.ts b/packages/tryorama/src/username_attestation/all_can_get_username_attestations.test.ts index 13b598e..f50db81 100644 --- a/packages/tryorama/src/username_attestation/all_can_get_username_attestations.test.ts +++ b/packages/tryorama/src/username_attestation/all_can_get_username_attestations.test.ts @@ -1,13 +1,13 @@ import { expect, test } from "vitest"; import { dhtSync, runScenario } from "@holochain/tryorama"; -import { setupAuthorityAndAlice } from "../utils/setup-happ.js"; +import { setupPlayer } from "../utils/setup-happ.js"; import { fakeAgentPubKey } from "@holochain/client"; test("All can get username attestations", async () => { await runScenario(async (scenario) => { - const { authorityCoordinators, aliceCoordinators, authority, alice } = - await setupAuthorityAndAlice(scenario); + const [authority, authorityCoordinators] = await setupPlayer(scenario); + const [alice, aliceCoordinators] = await setupPlayer(scenario); await scenario.shareAllAgents(); // Authority creates a UsernameAttestation diff --git a/packages/tryorama/src/username_attestation/authority_wont_ingest_invalid_username_signature.test.ts b/packages/tryorama/src/username_attestation/authority_wont_ingest_invalid_username_signature.test.ts index 765d4a5..4eb005d 100644 --- a/packages/tryorama/src/username_attestation/authority_wont_ingest_invalid_username_signature.test.ts +++ b/packages/tryorama/src/username_attestation/authority_wont_ingest_invalid_username_signature.test.ts @@ -1,12 +1,12 @@ import { expect, test } from "vitest"; import { runScenario } from "@holochain/tryorama"; -import { setupAuthorityAndAlice } from "../utils/setup-happ.js"; +import { setupPlayer } from "../utils/setup-happ.js"; test("Can attest username via remote call", async () => { await runScenario(async (scenario) => { - const { authorityCoordinators, aliceCoordinators, authority, alice } = - await setupAuthorityAndAlice(scenario); + const [_, authorityCoordinators] = await setupPlayer(scenario); + const [alice, aliceCoordinators] = await setupPlayer(scenario); await scenario.shareAllAgents(); // Alice signs username diff --git a/packages/tryorama/src/username_attestation/can_attest_username_via_remote_call.test.ts b/packages/tryorama/src/username_attestation/can_attest_username_via_remote_call.test.ts index cdc14fe..54eb033 100644 --- a/packages/tryorama/src/username_attestation/can_attest_username_via_remote_call.test.ts +++ b/packages/tryorama/src/username_attestation/can_attest_username_via_remote_call.test.ts @@ -1,19 +1,27 @@ import { expect, test } from "vitest"; import { dhtSync, runScenario } from "@holochain/tryorama"; -import { setupAuthorityAndAlice } from "../utils/setup-happ.js"; +import { setupPlayer } from "../utils/setup-happ.js"; import { decodeAppEntry, UsernameAttestation } from "@holoom/client"; test("Can attest username via remote call", async () => { await runScenario(async (scenario) => { - const { authorityCoordinators, aliceCoordinators, authority, alice } = - await setupAuthorityAndAlice(scenario); + const [authority, authorityCoordinators] = await setupPlayer(scenario); + const [alice, aliceCoordinators] = await setupPlayer(scenario); await scenario.shareAllAgents(); + // Open authority to attestation requests + await expect( + authorityCoordinators.usernameRegistry.usernameAuthoritySetup() + ).resolves.not.toThrow(); + // Alice requests creation of a UsernameAttestation const record = - await aliceCoordinators.usernameRegistry.signUsernameToAttest( - "asodijsadvjsadlkj" + await aliceCoordinators.usernameRegistry.signUsernameAndRequestAttestation( + { + username: "asodijsadvjsadlkj", + authority: authority.agentPubKey, + } ); await dhtSync([authority, alice], authority.cells[0].cell_id[0]); diff --git a/packages/tryorama/src/username_attestation/cannot_get_username_attestation_for_agent_that_doesnt_exist.test.ts b/packages/tryorama/src/username_attestation/cannot_get_username_attestation_for_agent_that_doesnt_exist.test.ts index 6ac21fd..e0948e2 100644 --- a/packages/tryorama/src/username_attestation/cannot_get_username_attestation_for_agent_that_doesnt_exist.test.ts +++ b/packages/tryorama/src/username_attestation/cannot_get_username_attestation_for_agent_that_doesnt_exist.test.ts @@ -1,18 +1,19 @@ import { expect, test } from "vitest"; import { runScenario } from "@holochain/tryorama"; -import { setupAuthorityOnly } from "../utils/setup-happ.js"; +import { setupPlayer } from "../utils/setup-happ.js"; import { fakeAgentPubKey } from "@holochain/client"; test("Cannot get username attestation for agent that doesn't exist", async () => { await runScenario(async (scenario) => { - const { authorityCoordinators } = await setupAuthorityOnly(scenario); + const [authority, authorityCoordinators] = await setupPlayer(scenario); // Authority tries to get UsernameAttestation await expect( - authorityCoordinators.usernameRegistry.getUsernameAttestationForAgent( - await fakeAgentPubKey(1) - ) + authorityCoordinators.usernameRegistry.getUsernameAttestationForAgent({ + agent: await fakeAgentPubKey(1), + trusted_authorities: [authority.agentPubKey], + }) ).resolves.toBe(null); }); }); diff --git a/packages/tryorama/src/username_attestation/nobody_can_delete_username_attestation.test.ts b/packages/tryorama/src/username_attestation/nobody_can_delete_username_attestation.test.ts index 0f590c6..0956aa2 100644 --- a/packages/tryorama/src/username_attestation/nobody_can_delete_username_attestation.test.ts +++ b/packages/tryorama/src/username_attestation/nobody_can_delete_username_attestation.test.ts @@ -1,13 +1,13 @@ import { expect, test } from "vitest"; import { dhtSync, runScenario } from "@holochain/tryorama"; -import { setupAuthorityAndAlice } from "../utils/setup-happ.js"; +import { setupPlayer } from "../utils/setup-happ.js"; import { fakeAgentPubKey } from "@holochain/client"; test("Nobody can delete username attestation", async () => { await runScenario(async (scenario) => { - const { authorityCoordinators, aliceCoordinators, authority, alice } = - await setupAuthorityAndAlice(scenario); + const [authority, authorityCoordinators] = await setupPlayer(scenario); + const [alice, aliceCoordinators] = await setupPlayer(scenario); await scenario.shareAllAgents(); // Authority creates a UsernameAttestation diff --git a/packages/tryorama/src/username_attestation/only_authority_can_create_username_attestation.test.ts b/packages/tryorama/src/username_attestation/only_authority_can_create_username_attestation.test.ts deleted file mode 100644 index edf4368..0000000 --- a/packages/tryorama/src/username_attestation/only_authority_can_create_username_attestation.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { expect, test } from "vitest"; -import { runScenario } from "@holochain/tryorama"; - -import { setupAuthorityAndAlice } from "../utils/setup-happ.js"; - -test("Only authority can create username attestations", async () => { - await runScenario(async (scenario) => { - const { authorityCoordinators, aliceCoordinators, alice } = - await setupAuthorityAndAlice(scenario); - - // Authority creates a UsernameAttestation for alice - await expect( - authorityCoordinators.usernameRegistry.createUsernameAttestation({ - username: "a_cool_guy1", - agent: alice.agentPubKey, - }) - ).resolves.not.toThrow(); - - // Alice cannot create an UsernameAttestation - await expect( - aliceCoordinators.usernameRegistry.createUsernameAttestation({ - username: "a_cool_guy2", - agent: alice.agentPubKey, - }) - ).rejects.toThrow(); - }); -}); diff --git a/packages/tryorama/src/username_attestation/same_agent_cannot_be_registered_twice.test.ts b/packages/tryorama/src/username_attestation/same_agent_cannot_be_registered_twice.test.ts index 360b83c..0b035da 100644 --- a/packages/tryorama/src/username_attestation/same_agent_cannot_be_registered_twice.test.ts +++ b/packages/tryorama/src/username_attestation/same_agent_cannot_be_registered_twice.test.ts @@ -1,12 +1,12 @@ import { expect, test } from "vitest"; import { runScenario } from "@holochain/tryorama"; -import { setupAuthorityOnly } from "../utils/setup-happ.js"; +import { setupPlayer } from "../utils/setup-happ.js"; import { fakeAgentPubKey } from "@holochain/client"; test("Same agent cannot be registered twice", async () => { await runScenario(async (scenario) => { - const { authorityCoordinators } = await setupAuthorityOnly(scenario); + const [authority, authorityCoordinators] = await setupPlayer(scenario); // Authority creates an UsernameAttestation await expect( diff --git a/packages/tryorama/src/username_attestation/same_username_cannot_be_registered_twice.test.ts b/packages/tryorama/src/username_attestation/same_username_cannot_be_registered_twice.test.ts index 80e1b9f..7bb6249 100644 --- a/packages/tryorama/src/username_attestation/same_username_cannot_be_registered_twice.test.ts +++ b/packages/tryorama/src/username_attestation/same_username_cannot_be_registered_twice.test.ts @@ -1,12 +1,12 @@ import { expect, test } from "vitest"; import { runScenario } from "@holochain/tryorama"; -import { setupAuthorityOnly } from "../utils/setup-happ.js"; +import { setupPlayer } from "../utils/setup-happ.js"; import { fakeAgentPubKey } from "@holochain/client"; test("Same username cannot be registered twice", async () => { await runScenario(async (scenario) => { - const { authorityCoordinators } = await setupAuthorityOnly(scenario); + const [_, authorityCoordinators] = await setupPlayer(scenario); // Authority creates an UsernameAttestation await expect( diff --git a/packages/tryorama/src/username_attestation/username_must_be_within_character_limit.test.ts b/packages/tryorama/src/username_attestation/username_must_be_within_character_limit.test.ts index 50bdc39..61c03c8 100644 --- a/packages/tryorama/src/username_attestation/username_must_be_within_character_limit.test.ts +++ b/packages/tryorama/src/username_attestation/username_must_be_within_character_limit.test.ts @@ -1,12 +1,12 @@ import { expect, test } from "vitest"; import { runScenario } from "@holochain/tryorama"; -import { setupAuthorityOnly } from "../utils/setup-happ.js"; +import { setupPlayer } from "../utils/setup-happ.js"; import { fakeAgentPubKey } from "@holochain/client"; test("Same username cannot be registered twice", async () => { await runScenario(async (scenario) => { - const { authorityCoordinators } = await setupAuthorityOnly(scenario); + const [_, authorityCoordinators] = await setupPlayer(scenario); // Authority creates an username of 5 characters await expect( diff --git a/packages/tryorama/src/utils/setup-happ.ts b/packages/tryorama/src/utils/setup-happ.ts index 4deadd7..e64802d 100644 --- a/packages/tryorama/src/utils/setup-happ.ts +++ b/packages/tryorama/src/utils/setup-happ.ts @@ -1,9 +1,4 @@ -import { - AgentPubKey, - AppBundleSource, - encodeHashToBase64, - fakeAgentPubKey, -} from "@holochain/client"; +import { AgentPubKey, AppBundleSource } from "@holochain/client"; import { Player, AgentApp, @@ -15,36 +10,9 @@ import { } from "@holochain/tryorama"; import yaml from "yaml"; import fs from "node:fs/promises"; -import { join } from "node:path"; -import { tmpdir } from "node:os"; -import { spawn } from "node:child_process"; +import path from "node:path"; import { bindCoordinators } from "./bindings"; -export async function overrideHappBundle( - authorityPubkey: AgentPubKey -): Promise { - const tmpWorkdir = await fs.mkdtemp(join(tmpdir(), "tryorama-workdir-")); - await fs.copyFile("../../workdir/holoom.dna", join(tmpWorkdir, "holoom.dna")); - - const manifest = yaml.parse( - await fs.readFile("../../workdir/happ.yaml", "utf8") - ); - manifest.roles[0].dna.modifiers.properties = { - authority_agent: encodeHashToBase64(authorityPubkey), - }; - await fs.writeFile(join(tmpWorkdir, "happ.yaml"), yaml.stringify(manifest)); - - const bundleProcess = spawn("hc", ["app", "pack", tmpWorkdir]); - await new Promise((resolve, reject) => { - bundleProcess.stdout.on("end", resolve); - bundleProcess.stderr.on("data", (err) => { - console.error("hc app pack error:", new TextDecoder().decode(err)); - reject(); - }); - }); - return { path: join(tmpWorkdir, "holoom.happ") }; -} - export async function addConductor(scenario: Scenario) { if (!scenario.serviceProcess) { ({ @@ -103,46 +71,16 @@ export async function addPlayer( }; } -export async function setupBundleAndAuthorityPlayer(scenario: Scenario) { - const conductor = await addConductor(scenario); - const authorityAgentPubkey = await conductor.adminWs().generateAgentPubKey(); - - const appBundleSource = await overrideHappBundle(authorityAgentPubkey); - const authority = await addPlayer( - scenario, - conductor, - appBundleSource, - authorityAgentPubkey - ); - - return { authority, appBundleSource }; -} - -export async function setupAuthorityOnly(scenario: Scenario) { - const { authority } = await setupBundleAndAuthorityPlayer(scenario); - const authorityCoordinators = bindCoordinators(authority); - return { authority, authorityCoordinators }; -} - -export async function setupAuthorityAndAlice(scenario: Scenario) { - const { authority, appBundleSource } = - await setupBundleAndAuthorityPlayer(scenario); - const alice = await addPlayer( - scenario, - await addConductor(scenario), - appBundleSource - ); - const authorityCoordinators = bindCoordinators(authority); - const aliceCoordinators = bindCoordinators(alice); - return { authority, alice, authorityCoordinators, aliceCoordinators }; -} +const APP_BUNDLE_SOURCE: AppBundleSource = { + path: path.join(import.meta.dirname, "../../../../workdir/holoom.happ"), +}; -export async function setupAliceOnly(scenario: Scenario) { - const alice = await addPlayer( +export async function setupPlayer(scenario: Scenario) { + const player = await addPlayer( scenario, await addConductor(scenario), - await overrideHappBundle(await fakeAgentPubKey()) + APP_BUNDLE_SOURCE ); - const aliceCoordinators = bindCoordinators(alice); - return { alice, aliceCoordinators }; + const playerCoordinators = bindCoordinators(player); + return [player, playerCoordinators] as const; } diff --git a/packages/tryorama/src/version/git-rev.test.ts b/packages/tryorama/src/version/git-rev.test.ts index 339f6f2..82640a0 100644 --- a/packages/tryorama/src/version/git-rev.test.ts +++ b/packages/tryorama/src/version/git-rev.test.ts @@ -2,11 +2,11 @@ import { expect, test } from "vitest"; import { runScenario } from "@holochain/tryorama"; import { execSync } from "node:child_process"; -import { setupAliceOnly } from "../utils/setup-happ.js"; +import { setupPlayer } from "../utils/setup-happ.js"; test("Injected git revision matches monorepo's", async () => { await runScenario(async (scenario) => { - const { aliceCoordinators } = await setupAliceOnly(scenario); + const [_, aliceCoordinators] = await setupPlayer(scenario); const revision = await execSync("git rev-parse HEAD").toString().trim(); expect(/^[a-f0-9]{40}/.test(revision)).toBe(true); diff --git a/packages/tryorama/src/wallet_attestation/checks_validity_of_evm_wallet_attestation.test.ts b/packages/tryorama/src/wallet_attestation/checks_validity_of_evm_wallet_attestation.test.ts index 36f5fbe..a552785 100644 --- a/packages/tryorama/src/wallet_attestation/checks_validity_of_evm_wallet_attestation.test.ts +++ b/packages/tryorama/src/wallet_attestation/checks_validity_of_evm_wallet_attestation.test.ts @@ -1,7 +1,7 @@ import { expect, test } from "vitest"; import { runScenario } from "@holochain/tryorama"; -import { setupAliceOnly } from "../utils/setup-happ.js"; +import { setupPlayer } from "../utils/setup-happ.js"; import { formatEvmSignature } from "@holoom/client"; import { privateKeyToAccount } from "viem/accounts"; import { hexToBytes } from "viem"; @@ -9,7 +9,7 @@ import { encodeHashToBase64, fakeAgentPubKey } from "@holochain/client"; test("Checks validity of evm wallet attestation", async () => { await runScenario(async (scenario) => { - const { aliceCoordinators } = await setupAliceOnly(scenario); + const [_, aliceCoordinators] = await setupPlayer(scenario); // Create WalletAttestation for alice at address: // 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 diff --git a/packages/tryorama/src/wallet_attestation/checks_validity_of_solana_wallet_attestation.test.ts b/packages/tryorama/src/wallet_attestation/checks_validity_of_solana_wallet_attestation.test.ts index 61f9aa0..11247da 100644 --- a/packages/tryorama/src/wallet_attestation/checks_validity_of_solana_wallet_attestation.test.ts +++ b/packages/tryorama/src/wallet_attestation/checks_validity_of_solana_wallet_attestation.test.ts @@ -1,7 +1,7 @@ import { expect, test } from "vitest"; import { runScenario } from "@holochain/tryorama"; -import { setupAliceOnly } from "../utils/setup-happ.js"; +import { setupPlayer } from "../utils/setup-happ.js"; import { encodeHashToBase64, fakeAgentPubKey } from "@holochain/client"; import b58 from "bs58"; import * as ed from "@noble/ed25519"; @@ -14,7 +14,7 @@ ed.etc.sha512Async = (...m) => test("Checks validity of solana wallet attestation", async () => { await runScenario(async (scenario) => { - const { aliceCoordinators } = await setupAliceOnly(scenario); + const [_, aliceCoordinators] = await setupPlayer(scenario); // Create WalletAttestation for alice at address: // oeYf6KAJkLYhBuR8CiGc6L4D4Xtfepr85fuDgA9kq96 diff --git a/packages/types/rollup.browser.config.ts b/packages/types/rollup.browser.config.ts index 9c07726..9064183 100644 --- a/packages/types/rollup.browser.config.ts +++ b/packages/types/rollup.browser.config.ts @@ -2,6 +2,7 @@ import json from "@rollup/plugin-json"; import typescript from "@rollup/plugin-typescript"; import * as fs from "fs"; import cleanup from "rollup-plugin-cleanup"; +import { RollupOptions } from "rollup"; const pkg = JSON.parse(fs.readFileSync("./package.json", "utf-8")); const banner = `/** @@ -12,7 +13,7 @@ const banner = `/** * @see [Github]{@link ${pkg.homepage}} */`; -export default { +const config: RollupOptions = { input: "src/index.ts", output: [ { @@ -29,4 +30,9 @@ export default { cleanup({ comments: "jsdoc" }), json(), ], + onwarn: (warning) => { + throw new Error(warning.message); + }, }; + +export default config; diff --git a/packages/types/src/types/GetAttestationForExternalIdPayload.ts b/packages/types/src/types/GetAttestationForExternalIdPayload.ts new file mode 100644 index 0000000..a392707 --- /dev/null +++ b/packages/types/src/types/GetAttestationForExternalIdPayload.ts @@ -0,0 +1,16 @@ +import { AgentPubKey } from "@holochain/client"; +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Input to `get_attestation_for_external_id` + */ +export type GetAttestationForExternalIdPayload = { + /** + * The external ID for which to want a corresponding attestation + */ + external_id: string; + /** + * The authorities whose attestations you respect. + */ + trusted_authorities: AgentPubKey[]; +}; diff --git a/packages/types/src/types/GetExternalIdAttestationsForAgentPayload.ts b/packages/types/src/types/GetExternalIdAttestationsForAgentPayload.ts new file mode 100644 index 0000000..31109b4 --- /dev/null +++ b/packages/types/src/types/GetExternalIdAttestationsForAgentPayload.ts @@ -0,0 +1,16 @@ +import { AgentPubKey } from "@holochain/client"; +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Input to `get_external_id_attestations_for_agent` + */ +export type GetExternalIdAttestationsForAgentPayload = { + /** + * The agent whose is the object of the attestations you wish to retrieve + */ + agent_pubkey: AgentPubKey; + /** + * The authorities whose attestations you respect. + */ + trusted_authorities: AgentPubKey[]; +}; diff --git a/packages/types/src/types/GetUsernameAttestationForAgentPayload.ts b/packages/types/src/types/GetUsernameAttestationForAgentPayload.ts new file mode 100644 index 0000000..cafdecd --- /dev/null +++ b/packages/types/src/types/GetUsernameAttestationForAgentPayload.ts @@ -0,0 +1,16 @@ +import { AgentPubKey } from "@holochain/client"; +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * The input to `get_username_attestation_for_agent` + */ +export type GetUsernameAttestationForAgentPayload = { + /** + * The agent whose is the object of the attestations you wish to retrieve + */ + agent: AgentPubKey; + /** + * The authorities whose attestations you respect. + */ + trusted_authorities: AgentPubKey[]; +}; diff --git a/packages/types/src/types/SendExternalIdAttestationRequestPayload.ts b/packages/types/src/types/SendExternalIdAttestationRequestPayload.ts index cc9cfc0..05e685c 100644 --- a/packages/types/src/types/SendExternalIdAttestationRequestPayload.ts +++ b/packages/types/src/types/SendExternalIdAttestationRequestPayload.ts @@ -1,7 +1,9 @@ +import { AgentPubKey } from "@holochain/client"; // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. export type SendExternalIdAttestationRequestPayload = { request_id: string; code_verifier: string; code: string; + authority: AgentPubKey; }; diff --git a/packages/types/src/types/SignUsernameAndRequestAttestationInput.ts b/packages/types/src/types/SignUsernameAndRequestAttestationInput.ts new file mode 100644 index 0000000..342d155 --- /dev/null +++ b/packages/types/src/types/SignUsernameAndRequestAttestationInput.ts @@ -0,0 +1,16 @@ +import { AgentPubKey } from "@holochain/client"; +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * The input to `sign_username_and_request_attestation` + */ +export type SignUsernameAndRequestAttestationInput = { + /** + * The username for which you want a corresponding attestation + */ + username: string; + /** + * The authorities whose attestations you respect. + */ + authority: AgentPubKey; +}; diff --git a/packages/types/src/types/index.ts b/packages/types/src/types/index.ts index 9ad428a..b698149 100644 --- a/packages/types/src/types/index.ts +++ b/packages/types/src/types/index.ts @@ -7,7 +7,10 @@ export * from "./EvmSigningOffer"; export * from "./EvmU256Item"; export * from "./ExecuteRecipePayload"; export * from "./ExternalIdAttestation"; +export * from "./GetAttestationForExternalIdPayload"; +export * from "./GetExternalIdAttestationsForAgentPayload"; export * from "./GetMetadataItemValuePayload"; +export * from "./GetUsernameAttestationForAgentPayload"; export * from "./IngestExternalIdAttestationRequestPayload"; export * from "./JqInstructionArgumentNames"; export * from "./LocalHoloomSignal"; @@ -24,6 +27,7 @@ export * from "./RejectExternalIdRequestPayload"; export * from "./RemoteHoloomSignal"; export * from "./ResolveEvmSignatureOverRecipeExecutionRequestPayload"; export * from "./SendExternalIdAttestationRequestPayload"; +export * from "./SignUsernameAndRequestAttestationInput"; export * from "./SignableBytes"; export * from "./SignedEvmSigningOffer"; export * from "./SignedEvmU256Array"; diff --git a/packages/types/src/zome-functions/UsernameRegistryCoordinator.ts b/packages/types/src/zome-functions/UsernameRegistryCoordinator.ts index 63d06c1..c54f0af 100644 --- a/packages/types/src/zome-functions/UsernameRegistryCoordinator.ts +++ b/packages/types/src/zome-functions/UsernameRegistryCoordinator.ts @@ -7,7 +7,10 @@ import { EvmSignatureOverRecipeExecutionRequest, ExecuteRecipePayload, ExternalIdAttestation, + GetAttestationForExternalIdPayload, + GetExternalIdAttestationsForAgentPayload, GetMetadataItemValuePayload, + GetUsernameAttestationForAgentPayload, IngestExternalIdAttestationRequestPayload, OracleDocument, Recipe, @@ -16,6 +19,7 @@ import { RejectExternalIdRequestPayload, ResolveEvmSignatureOverRecipeExecutionRequestPayload, SendExternalIdAttestationRequestPayload, + SignUsernameAndRequestAttestationInput, SignedUsername, UpdateMetadataItemPayload, UsernameAttestation, @@ -145,6 +149,15 @@ export class UsernameRegistryCoordinator { }); } + async evmSignatureProviderSetup(): Promise { + return this.client.callZome({ + role_name: this.roleName, + zome_name: this.zomeName, + fn_name: "evm_signature_provider_setup", + payload: null, + }); + } + async executeRecipe(payload: ExecuteRecipePayload): Promise { return this.client.callZome({ role_name: this.roleName, @@ -154,39 +167,41 @@ export class UsernameRegistryCoordinator { }); } - async getAllExternalIdAhs(): Promise { + async externalIdAuthoritySetup(): Promise { return this.client.callZome({ role_name: this.roleName, zome_name: this.zomeName, - fn_name: "get_all_external_id_ahs", + fn_name: "external_id_authority_setup", payload: null, }); } - async getAllUsernameAttestations(): Promise { + async getAllAuthoredExternalIdAhs(): Promise { return this.client.callZome({ role_name: this.roleName, zome_name: this.zomeName, - fn_name: "get_all_username_attestations", + fn_name: "get_all_authored_external_id_ahs", payload: null, }); } - async getAttestationForExternalId(payload: string): Promise { + async getAllAuthoredUsernameAttestations(): Promise { return this.client.callZome({ role_name: this.roleName, zome_name: this.zomeName, - fn_name: "get_attestation_for_external_id", - payload, + fn_name: "get_all_authored_username_attestations", + payload: null, }); } - async getAuthority(): Promise { + async getAttestationForExternalId( + payload: GetAttestationForExternalIdPayload, + ): Promise { return this.client.callZome({ role_name: this.roleName, zome_name: this.zomeName, - fn_name: "get_authority", - payload: null, + fn_name: "get_attestation_for_external_id", + payload, }); } @@ -209,7 +224,7 @@ export class UsernameRegistryCoordinator { } async getExternalIdAttestationsForAgent( - payload: AgentPubKey, + payload: GetExternalIdAttestationsForAgentPayload, ): Promise { return this.client.callZome({ role_name: this.roleName, @@ -309,7 +324,7 @@ export class UsernameRegistryCoordinator { } async getUsernameAttestationForAgent( - payload: AgentPubKey, + payload: GetUsernameAttestationForAgentPayload, ): Promise { return this.client.callZome({ role_name: this.roleName, @@ -432,11 +447,13 @@ export class UsernameRegistryCoordinator { }); } - async signUsernameToAttest(payload: string): Promise { + async signUsernameAndRequestAttestation( + payload: SignUsernameAndRequestAttestationInput, + ): Promise { return this.client.callZome({ role_name: this.roleName, zome_name: this.zomeName, - fn_name: "sign_username_to_attest", + fn_name: "sign_username_and_request_attestation", payload, }); } @@ -449,4 +466,13 @@ export class UsernameRegistryCoordinator { payload, }); } + + async usernameAuthoritySetup(): Promise { + return this.client.callZome({ + role_name: this.roleName, + zome_name: this.zomeName, + fn_name: "username_authority_setup", + payload: null, + }); + } } diff --git a/scripts/build_dna.sh b/scripts/build_dna.sh index 560d0b8..a0d7daf 100755 --- a/scripts/build_dna.sh +++ b/scripts/build_dna.sh @@ -9,4 +9,4 @@ RUSTFLAGS='' CARGO_TARGET_DIR=target cargo build \ --package '*_integrity' \ --package '*_coordinator' -hc dna pack workdir +hc app pack --recursive workdir