lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

commit ff48c3eff6888f7e0aa677bd63ac33714c88ddfd
parent 5c054aae7fe507850c35792741bc533bf3356a16
Author: triesap <tyson@radroots.org>
Date:   Fri, 31 Jul 2026 20:32:58 +0000

nostr-connect: migrate workspace consumers

- drive SDK remote signing through the final client state machine
- adapt host relay IO behind the canonical transport contract
- migrate examples, tests, and adapter exports to final protocol paths
- verify SDK tests, strict Clippy, and architecture policy

Diffstat:
Mcrates/sdk/examples/sdk_v1_myc_nip46_signer_setup.rs | 16+++++++++-------
Mcrates/sdk/src/adapters/signer.rs | 16++++++++--------
Mcrates/sdk/src/signer_provider.rs | 213++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Mcrates/sdk/tests/source_boundary.rs | 30++++++++++++++++++++++++++++++
Mcrates/sdk/tests/unit/signer_provider_tests.rs | 111+++++++++++++++++++++++++++++++++++++++++++------------------------------------
5 files changed, 231 insertions(+), 155 deletions(-)

diff --git a/crates/sdk/examples/sdk_v1_myc_nip46_signer_setup.rs b/crates/sdk/examples/sdk_v1_myc_nip46_signer_setup.rs @@ -1,8 +1,8 @@ use nostr::Keys as RadrootsNostrKeys; use radroots_event::envelope::kind::KIND_TRADE_PROPOSAL; use radroots_nostr::event::Event as RadrootsNostrEvent; -use radroots_nostr_connect::prelude::{ - RadrootsNostrConnectClientTarget, RadrootsNostrConnectError, +use radroots_nostr_connect::{ + Error as NostrConnectError, client::Target, uri::RelayUrl as ConnectRelayUrl, }; use radroots_sdk::{ RadrootsClient, RadrootsSdkMycNip46Signer, RadrootsSdkNip46ClientKey, @@ -24,7 +24,7 @@ impl RadrootsSdkNip46Transport for ExampleNip46Transport { fn next_response_event<'a>( &'a self, ) -> RadrootsSdkNip46TransportFuture<'a, RadrootsNostrEvent> { - Box::pin(async { Err(RadrootsNostrConnectError::RequestTimedOut) }) + Box::pin(async { Err(NostrConnectError::RequestTimedOut) }) } } @@ -33,10 +33,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> { let client_key = RadrootsSdkNip46ClientKey::generate(); let remote_signer_keys = RadrootsNostrKeys::generate(); let user_keys = RadrootsNostrKeys::generate(); - let target = RadrootsNostrConnectClientTarget::new( - remote_signer_keys.public_key(), - vec![nostr::RelayUrl::parse("wss://relay.example.com")?], - ); + let remote_signer_public_key = + radroots_nostr::key::public_key_from_nostr(remote_signer_keys.public_key())?; + let target = Target::try_new( + remote_signer_public_key, + vec![ConnectRelayUrl::parse("wss://relay.example.com")?], + )?; let signer = RadrootsSdkMycNip46Signer::new( client_key, target, diff --git a/crates/sdk/src/adapters/signer.rs b/crates/sdk/src/adapters/signer.rs @@ -1,11 +1,11 @@ -pub use radroots_nostr_connect::prelude::{ - RADROOTS_NOSTR_CONNECT_PENDING_CONNECTION_ERROR, RADROOTS_NOSTR_CONNECT_RPC_KIND, - RadrootsNostrConnectBunkerUri, RadrootsNostrConnectClientMetadata, - RadrootsNostrConnectClientUri, RadrootsNostrConnectError, RadrootsNostrConnectMethod, - RadrootsNostrConnectPendingConnectionPollOutcome, RadrootsNostrConnectPermission, - RadrootsNostrConnectPermissions, RadrootsNostrConnectRemoteSessionCapability, - RadrootsNostrConnectRequest, RadrootsNostrConnectRequestMessage, RadrootsNostrConnectResponse, - RadrootsNostrConnectResponseEnvelope, RadrootsNostrConnectUri, +pub use radroots_nostr_connect::{ + BunkerUri, ClientUri, Error, Method, Permission, Request, Response, + message::{ + PENDING_CONNECTION_ERROR, PendingConnectionOutcome, RPC_KIND, RemoteSessionCapability, + RequestMessage, ResponseEnvelope, + }, + permission::Permissions, + uri::{ClientMetadata, Uri}, }; pub use radroots_nostr_signer::prelude::{ RadrootsNostrEmbeddedSignerBackend, RadrootsNostrLocalSignerAvailability, diff --git a/crates/sdk/src/signer_provider.rs b/crates/sdk/src/signer_provider.rs @@ -10,12 +10,14 @@ use radroots_event::envelope::kind::{ use radroots_event::wire::Nip01EventWire; use radroots_identity::PublicKey; use radroots_nostr::event::Event as RadrootsNostrEvent; -use radroots_nostr_connect::prelude::{ - RadrootsNostrConnectClientRequest, RadrootsNostrConnectClientTarget, - RadrootsNostrConnectClientTransport, RadrootsNostrConnectClientTransportFuture, - RadrootsNostrConnectError, RadrootsNostrConnectMethod, RadrootsNostrConnectPermission, - RadrootsNostrConnectPermissions, RadrootsNostrConnectRequest, RadrootsNostrConnectResponse, - UnsignedEvent as ConnectUnsignedEvent, execute_request_with_transport, +use radroots_nostr_connect::{ + Error as NostrConnectError, Method, Permission, Request, Response, + client::{ + CancellationToken, Client, ClientEvent, Completion, Progress, Receive, Target, Transport, + TransportFuture, + }, + message::{RequestId, UnsignedEvent as ConnectUnsignedEvent}, + permission::Permissions, }; use radroots_signing::{ Actor, SignReceipt, SignRequest, Signer, @@ -26,7 +28,7 @@ use std::time::Duration; use tokio::time::timeout; use uuid::Uuid; -pub type RadrootsSdkNip46TransportFuture<'a, T> = RadrootsNostrConnectClientTransportFuture<'a, T>; +pub type RadrootsSdkNip46TransportFuture<'a, T> = TransportFuture<'a, T>; pub type RadrootsSdkLocalSignerCapability = dyn Signer; pub const RADROOTS_SDK_MYC_NIP46_PRODUCT_SIGN_EVENT_KINDS: [u32; 7] = [ @@ -381,8 +383,7 @@ impl Default for RadrootsSdkMycNip46RequestPolicy { #[derive(Clone)] pub struct RadrootsSdkMycNip46Signer { - client_keys: RadrootsNostrKeys, - target: RadrootsNostrConnectClientTarget, + client: Arc<Client>, user_pubkey: PublicKey, transport: Arc<dyn RadrootsSdkNip46Transport>, request_policy: RadrootsSdkMycNip46RequestPolicy, @@ -392,7 +393,7 @@ pub struct RadrootsSdkMycNip46Signer { impl RadrootsSdkMycNip46Signer { pub fn new( client_key: RadrootsSdkNip46ClientKey, - target: RadrootsNostrConnectClientTarget, + target: Target, user_pubkey: impl AsRef<str>, transport: Arc<dyn RadrootsSdkNip46Transport>, ) -> Result<Self, RadrootsSdkError> { @@ -407,7 +408,7 @@ impl RadrootsSdkMycNip46Signer { pub fn new_with_request_policy( client_key: RadrootsSdkNip46ClientKey, - target: RadrootsNostrConnectClientTarget, + target: Target, user_pubkey: impl AsRef<str>, transport: Arc<dyn RadrootsSdkNip46Transport>, request_policy: RadrootsSdkMycNip46RequestPolicy, @@ -424,21 +425,23 @@ impl RadrootsSdkMycNip46Signer { fn new_with_request_id_generator( client_key: RadrootsSdkNip46ClientKey, - target: RadrootsNostrConnectClientTarget, + target: Target, user_pubkey: impl AsRef<str>, transport: Arc<dyn RadrootsSdkNip46Transport>, request_policy: RadrootsSdkMycNip46RequestPolicy, request_id_generator: Arc<dyn RadrootsSdkMycNip46RequestIdGenerator>, ) -> Result<Self, RadrootsSdkError> { RadrootsSdkMycNip46RequestPolicy::new(request_policy.request_timeout())?; + let client_secret = client_key.into_keys().secret_key().to_secret_hex(); + let client = Client::from_secret(client_secret.as_str(), target) + .map_err(sdk_error_from_nip46_error)?; let user_pubkey = PublicKey::from_hex(user_pubkey.as_ref()).map_err(|error| { RadrootsSdkError::InvalidRequest { message: format!("myc_nip46 user pubkey is invalid: {error}"), } })?; Ok(Self { - client_keys: client_key.into_keys(), - target, + client: Arc::new(client), user_pubkey, transport, request_policy, @@ -451,8 +454,8 @@ impl RadrootsSdkMycNip46Signer { mode: RadrootsSdkSignerMode::MycNip46, state: RadrootsSdkSignerState::Ready, signer_pubkey: self.user_pubkey.to_hex(), - remote_signer_pubkey: Some(self.target.remote_signer_public_key.to_hex()), - relay_count: self.target.relays.len(), + remote_signer_pubkey: Some(self.client.target().remote_signer_public_key().to_hex()), + relay_count: self.client.target().relays().len(), } } @@ -460,8 +463,14 @@ impl RadrootsSdkMycNip46Signer { RadrootsSdkSignerCapability { mode: RadrootsSdkSignerMode::MycNip46, signer_pubkey: self.user_pubkey.to_hex(), - remote_signer_pubkey: Some(self.target.remote_signer_public_key.to_hex()), - relays: self.target.relays.iter().map(ToString::to_string).collect(), + remote_signer_pubkey: Some(self.client.target().remote_signer_public_key().to_hex()), + relays: self + .client + .target() + .relays() + .iter() + .map(ToString::to_string) + .collect(), can_sign_events: true, nip46_permissions: radroots_sdk_myc_nip46_product_permission_strings(), } @@ -487,42 +496,52 @@ impl RadrootsSdkMycNip46Signer { .into()); } let sign_event_request = sign_event_request_from_frozen_draft(sign_request.draft())?; - let request_id = self.next_request_id(); + let request_id = + RequestId::parse(self.next_request_id()).map_err(sdk_error_from_nip46_error)?; let mut adapter = RadrootsSdkNip46TransportAdapter { transport: self.transport.as_ref(), + request_timeout: self.request_policy.request_timeout(), }; let mut progress_error = None; - let request_future = execute_request_with_transport( - &self.client_keys, - &self.target, - RadrootsNostrConnectClientRequest::new(request_id, sign_event_request), - &mut adapter, - |progress| { - let sdk_progress = match progress { - radroots_nostr_connect::prelude::RadrootsNostrConnectClientProgress::AuthChallenge { - url, - } => RadrootsSdkSignerProgress::AuthChallenge { - mode: RadrootsSdkSignerMode::MycNip46, - url, - }, - }; - if let Err(error) = request.emit_progress(sdk_progress) { - progress_error = Some(error); - return Err(RadrootsNostrConnectError::Transport { - reason: "SDK signer progress sink failed".to_owned(), - }); - } - Ok(()) - }, - ); - let response = timeout(self.request_policy.request_timeout(), request_future) - .await - .map_err(|_| RadrootsNostrConnectError::RequestTimedOut) - .and_then(|response| response); + let cancellation = CancellationToken::new(); + let completion = self + .client + .execute( + request_id, + sign_event_request, + &mut adapter, + &cancellation, + |progress| { + let sdk_progress = match progress { + Progress::AuthChallenge { url } => { + RadrootsSdkSignerProgress::AuthChallenge { + mode: RadrootsSdkSignerMode::MycNip46, + url, + } + } + }; + if let Err(error) = request.emit_progress(sdk_progress) { + progress_error = Some(error); + return Err(NostrConnectError::Transport { + reason: "SDK signer progress sink failed".to_owned(), + }); + } + Ok(()) + }, + ) + .await; if let Some(error) = progress_error { return Err(error); } - let response = response.map_err(sdk_error_from_nip46_error)?; + let response = match completion.map_err(sdk_error_from_nip46_error)? { + Completion::Response(response) => *response, + Completion::Cancelled(phase) => { + return Err(RadrootsSdkError::SignerTransport { + mode: RadrootsSdkSignerMode::MycNip46.as_str().to_owned(), + reason: format!("NIP-46 request cancelled {phase:?}"), + }); + } + }; let signed_event = signed_event_from_nip46_response(operation_kind.as_str(), response)?; let receipt = SignReceipt::from_signed_event(&sign_request, signed_event, unix_time_now()?) .map_err(|error| match error.kind() { @@ -541,7 +560,7 @@ impl RadrootsSdkMycNip46Signer { operation_kind.as_str(), RadrootsSdkSignerMode::MycNip46, self.user_pubkey.to_hex(), - Some(self.target.remote_signer_public_key.to_hex()), + Some(self.client.target().remote_signer_public_key().to_hex()), receipt, )) } @@ -563,15 +582,10 @@ impl RadrootsSdkMycNip46RequestIdGenerator for RadrootsSdkUuidNip46RequestIdGene } } -pub fn radroots_sdk_myc_nip46_product_permissions() -> RadrootsNostrConnectPermissions { +pub fn radroots_sdk_myc_nip46_product_permissions() -> Permissions { RADROOTS_SDK_MYC_NIP46_PRODUCT_SIGN_EVENT_KINDS .iter() - .map(|kind| { - RadrootsNostrConnectPermission::with_parameter( - RadrootsNostrConnectMethod::SignEvent, - kind.to_string(), - ) - }) + .map(|kind| Permission::with_parameter(Method::SignEvent, kind.to_string())) .collect::<Vec<_>>() .into() } @@ -586,26 +600,49 @@ pub fn radroots_sdk_myc_nip46_product_permission_strings() -> Vec<String> { struct RadrootsSdkNip46TransportAdapter<'a> { transport: &'a dyn RadrootsSdkNip46Transport, + request_timeout: Duration, } -impl RadrootsNostrConnectClientTransport for RadrootsSdkNip46TransportAdapter<'_> { - fn publish_request_event<'a>( - &'a mut self, - event: RadrootsNostrEvent, - ) -> RadrootsNostrConnectClientTransportFuture<'a, ()> { +impl Transport for RadrootsSdkNip46TransportAdapter<'_> { + fn publish<'a>(&'a mut self, event: ClientEvent) -> TransportFuture<'a, ()> { + let event = match RadrootsNostrEvent::from_json(event.as_json()) { + Ok(event) => event, + Err(error) => { + return Box::pin(async move { + Err(NostrConnectError::Transport { + reason: format!("invalid SDK NIP-46 publication: {error}"), + }) + }); + } + }; self.transport.publish_request_event(event) } - fn next_response_event<'a>( + fn receive<'a>( &'a mut self, - ) -> RadrootsNostrConnectClientTransportFuture<'a, RadrootsNostrEvent> { - self.transport.next_response_event() + cancellation: &'a CancellationToken, + ) -> TransportFuture<'a, Receive> { + if cancellation.is_cancelled() { + return Box::pin(async { Ok(Receive::Cancelled) }); + } + let next = self.transport.next_response_event(); + let request_timeout = self.request_timeout; + Box::pin(async move { + match timeout(request_timeout, next).await { + Ok(Ok(event)) => { + if cancellation.is_cancelled() { + return Ok(Receive::Cancelled); + } + ClientEvent::from_json(event.as_json().as_str()).map(Receive::event) + } + Ok(Err(error)) => Err(error), + Err(_) => Ok(Receive::TimedOut), + } + }) } } -fn sign_event_request_from_frozen_draft( - draft: &EventDraft, -) -> Result<RadrootsNostrConnectRequest, RadrootsSdkError> { +fn sign_event_request_from_frozen_draft(draft: &EventDraft) -> Result<Request, RadrootsSdkError> { let public_key = nip46_unsigned_event_pubkey(draft)?; let kind = nip46_unsigned_event_kind(draft)?; let tags = nip46_unsigned_event_tags(draft)?; @@ -619,7 +656,7 @@ fn sign_event_request_from_frozen_draft( }; let unsigned_event = ConnectUnsignedEvent::from_json(&unsigned_event.as_json()) .map_err(|error| nip46_sign_event_protocol_error(error.to_string()))?; - Ok(RadrootsNostrConnectRequest::SignEvent(unsigned_event)) + Ok(Request::SignEvent(unsigned_event)) } fn nip46_unsigned_event_pubkey(draft: &EventDraft) -> Result<NostrPublicKey, RadrootsSdkError> { @@ -662,10 +699,10 @@ fn nip46_sign_event_protocol_error(reason: String) -> RadrootsSdkError { fn signed_event_from_nip46_response( operation_kind: &str, - response: RadrootsNostrConnectResponse, + response: Response, ) -> Result<SignedEvent, RadrootsSdkError> { match response { - RadrootsNostrConnectResponse::SignedEvent(event) => { + Response::SignedEvent(event) => { let raw_json = event.as_json(); let wire = Nip01EventWire::parse_json(raw_json.as_str()).map_err(|error| { RadrootsSdkError::SignerProtocol { @@ -687,18 +724,14 @@ fn signed_event_from_nip46_response( reason: format!("remote signed event signature is invalid: {error}"), }) } - RadrootsNostrConnectResponse::Error { error, .. } => { - Err(RadrootsSdkError::SignerRequestRejected { - mode: RadrootsSdkSignerMode::MycNip46.as_str().to_owned(), - reason: error, - }) - } - RadrootsNostrConnectResponse::PendingConnection => { - Err(RadrootsSdkError::SignerAuthChallengePending { - mode: RadrootsSdkSignerMode::MycNip46.as_str().to_owned(), - auth_url: None, - }) - } + Response::Error { error, .. } => Err(RadrootsSdkError::SignerRequestRejected { + mode: RadrootsSdkSignerMode::MycNip46.as_str().to_owned(), + reason: error, + }), + Response::PendingConnection => Err(RadrootsSdkError::SignerAuthChallengePending { + mode: RadrootsSdkSignerMode::MycNip46.as_str().to_owned(), + auth_url: None, + }), other => Err(RadrootsSdkError::SignerProtocol { mode: RadrootsSdkSignerMode::MycNip46.as_str().to_owned(), reason: format!("unexpected NIP-46 response for {operation_kind}: {other:?}"), @@ -706,21 +739,21 @@ fn signed_event_from_nip46_response( } } -fn sdk_error_from_nip46_error(error: RadrootsNostrConnectError) -> RadrootsSdkError { +fn sdk_error_from_nip46_error(error: NostrConnectError) -> RadrootsSdkError { match error { - RadrootsNostrConnectError::RequestTimedOut => RadrootsSdkError::SignerRequestTimedOut { + NostrConnectError::RequestTimedOut => RadrootsSdkError::SignerRequestTimedOut { mode: RadrootsSdkSignerMode::MycNip46.as_str().to_owned(), }, - RadrootsNostrConnectError::Transport { reason } => RadrootsSdkError::SignerTransport { + NostrConnectError::Transport { reason } => RadrootsSdkError::SignerTransport { mode: RadrootsSdkSignerMode::MycNip46.as_str().to_owned(), reason, }, - RadrootsNostrConnectError::Encrypt { reason } - | RadrootsNostrConnectError::Decrypt { reason } - | RadrootsNostrConnectError::Sign { reason } - | RadrootsNostrConnectError::Json(reason) - | RadrootsNostrConnectError::InvalidRequestPayload { reason, .. } - | RadrootsNostrConnectError::InvalidResponsePayload { reason, .. } => { + NostrConnectError::Encrypt { reason } + | NostrConnectError::Decrypt { reason } + | NostrConnectError::Sign { reason } + | NostrConnectError::Json(reason) + | NostrConnectError::InvalidRequestPayload { reason, .. } + | NostrConnectError::InvalidResponsePayload { reason, .. } => { RadrootsSdkError::SignerProtocol { mode: RadrootsSdkSignerMode::MycNip46.as_str().to_owned(), reason, diff --git a/crates/sdk/tests/source_boundary.rs b/crates/sdk/tests/source_boundary.rs @@ -280,3 +280,33 @@ fn signer_transition_surface_is_private_hidden_and_scheduled_for_removal() { assert!(transition_record.contains("oss/cli")); assert!(transition_record.contains("oss/studio_app")); } + +#[test] +fn signer_consumers_use_the_final_nostr_connect_state_machine() { + let manifest = manifest_dir(); + for relative in [ + "src/signer_provider.rs", + "src/adapters/signer.rs", + "examples/sdk_v1_myc_nip46_signer_setup.rs", + ] { + let source = read_source(&manifest.join(relative)); + for retired in [ + "radroots_nostr_connect::prelude", + "RadrootsNostrConnectClient", + "RadrootsNostrConnectMethod", + "RadrootsNostrConnectPermission", + "RadrootsNostrConnectRequest", + "RadrootsNostrConnectResponse", + "RADROOTS_NOSTR_CONNECT_", + ] { + assert!( + !source.contains(retired), + "{relative} retains retired Nostr Connect surface `{retired}`" + ); + } + } + + let provider = read_source(&manifest.join("src/signer_provider.rs")); + assert!(provider.contains("impl Transport for RadrootsSdkNip46TransportAdapter")); + assert!(provider.contains(".client\n .execute(")); +} diff --git a/crates/sdk/tests/unit/signer_provider_tests.rs b/crates/sdk/tests/unit/signer_provider_tests.rs @@ -8,10 +8,11 @@ use radroots_event::envelope::kind::{ }; use radroots_nostr::signing::sign_frozen_draft; use radroots_nostr::{event::Event as RadrootsNostrEvent, signing::LocalSigner}; -use radroots_nostr_connect::prelude::{ - RADROOTS_NOSTR_CONNECT_RPC_KIND, RadrootsNostrConnectClientTarget, RadrootsNostrConnectError, - RadrootsNostrConnectRequest, RadrootsNostrConnectRequestMessage, RadrootsNostrConnectResponse, - SignedEvent as ConnectSignedEvent, +use radroots_nostr_connect::{ + Error as NostrConnectError, Request, Response, + client::Target, + message::{RPC_KIND, RequestMessage, SignedEvent as ConnectSignedEvent}, + uri::RelayUrl as ConnectRelayUrl, }; use radroots_signing::actor::ActorSource; use std::collections::VecDeque; @@ -54,6 +55,16 @@ fn remote_keys() -> RadrootsNostrKeys { REMOTE_KEYS.clone() } +fn nip46_target(remote_public_key: nostr::PublicKey, relays: Vec<nostr::RelayUrl>) -> Target { + let remote_public_key = + radroots_nostr::key::public_key_from_nostr(remote_public_key).expect("identity public key"); + let relays = relays + .into_iter() + .map(|relay| ConnectRelayUrl::parse(relay.to_string().as_str()).expect("connect relay")) + .collect(); + Target::try_new(remote_public_key, relays).expect("NIP-46 target") +} + fn client_keys() -> RadrootsSdkNip46ClientKey { RadrootsSdkNip46ClientKey::generate() } @@ -134,7 +145,7 @@ fn response_event( remote_keys: &RadrootsNostrKeys, client_public_key: nostr::PublicKey, request_id: &str, - response: RadrootsNostrConnectResponse, + response: Response, ) -> RadrootsNostrEvent { let envelope = response .into_envelope(request_id) @@ -147,7 +158,7 @@ fn response_event( Version::V2, ) .expect("ciphertext"); - EventBuilder::new(Kind::Custom(RADROOTS_NOSTR_CONNECT_RPC_KIND), ciphertext) + EventBuilder::new(Kind::Custom(RPC_KIND), ciphertext) .tag(Tag::public_key(client_public_key)) .sign_with_keys(remote_keys) .expect("response event") @@ -158,7 +169,7 @@ fn myc_signer_with_responses( ) -> (RadrootsSdkMycNip46Signer, Arc<MockNip46Transport>) { let remote_keys = remote_keys(); let transport = Arc::new(MockNip46Transport::new(remote_keys.clone(), responses)); - let target = RadrootsNostrConnectClientTarget::new( + let target = nip46_target( remote_keys.public_key(), vec![nostr::RelayUrl::parse("wss://relay.example.com").expect("relay")], ); @@ -176,7 +187,7 @@ struct MockNip46Transport { } enum MockNip46Response { - Respond(RadrootsNostrConnectResponse), + Respond(Response), } impl MockNip46Transport { @@ -193,7 +204,7 @@ impl MockNip46Transport { self.published.lock().expect("published lock").clone() } - fn published_request_messages(&self) -> Vec<RadrootsNostrConnectRequestMessage> { + fn published_request_messages(&self) -> Vec<RequestMessage> { self.published() .iter() .map(|event| request_message_from_event(&self.remote_keys, event)) @@ -204,7 +215,7 @@ impl MockNip46Transport { fn request_message_from_event( remote_keys: &RadrootsNostrKeys, event: &RadrootsNostrEvent, -) -> RadrootsNostrConnectRequestMessage { +) -> RequestMessage { let payload = nip44::decrypt(remote_keys.secret_key(), &event.pubkey, &event.content) .expect("request payload"); serde_json::from_str(payload.as_str()).expect("request message") @@ -239,7 +250,7 @@ impl RadrootsSdkNip46Transport for MockNip46Transport { &'a self, ) -> RadrootsSdkNip46TransportFuture<'a, RadrootsNostrEvent> { let next = self.inbound.lock().expect("inbound lock").pop_front(); - Box::pin(async move { next.ok_or(RadrootsNostrConnectError::RequestTimedOut) }) + Box::pin(async move { next.ok_or(NostrConnectError::RequestTimedOut) }) } } @@ -268,7 +279,7 @@ impl RadrootsSdkNip46Transport for HangingNip46Transport { &'a self, ) -> RadrootsSdkNip46TransportFuture<'a, RadrootsNostrEvent> { Box::pin(future::pending::< - Result<RadrootsNostrEvent, RadrootsNostrConnectError>, + Result<RadrootsNostrEvent, NostrConnectError>, >()) } } @@ -409,7 +420,7 @@ fn signer_provider_reports_myc_status_capability_and_constructor_errors() { nostr::RelayUrl::parse("wss://relay-a.example.com").expect("relay a"), nostr::RelayUrl::parse("wss://relay-b.example.com").expect("relay b"), ]; - let target = RadrootsNostrConnectClientTarget::new(remote_keys.public_key(), relays); + let target = nip46_target(remote_keys.public_key(), relays); let transport = Arc::new(MockNip46Transport::new(remote_keys.clone(), Vec::new())); let signer = RadrootsSdkMycNip46Signer::new(client_keys(), target, user_pubkey(), transport.clone()) @@ -442,7 +453,7 @@ fn signer_provider_reports_myc_status_capability_and_constructor_errors() { } ); - let target = RadrootsNostrConnectClientTarget::new(remote_keys.public_key(), Vec::new()); + let target = nip46_target(remote_keys.public_key(), Vec::new()); let error = match RadrootsSdkMycNip46Signer::new(client_keys(), target, "not-a-pubkey", transport) { Ok(_) => panic!("expected invalid pubkey"), @@ -460,7 +471,7 @@ fn nip46_private_helpers_map_identity_adapter_and_response_edges() { assert!(matches!( signed_event_from_nip46_response( "farm.publish", - RadrootsNostrConnectResponse::Error { + Response::Error { result: None, error: "operator rejected".to_owned(), }, @@ -469,42 +480,42 @@ fn nip46_private_helpers_map_identity_adapter_and_response_edges() { if mode == "myc_nip46" && reason == "operator rejected" )); assert!(matches!( - signed_event_from_nip46_response("farm.publish", RadrootsNostrConnectResponse::PendingConnection), + signed_event_from_nip46_response("farm.publish", Response::PendingConnection), Err(RadrootsSdkError::SignerAuthChallengePending { ref mode, auth_url: None }) if mode == "myc_nip46" )); assert!(matches!( - signed_event_from_nip46_response("farm.publish", RadrootsNostrConnectResponse::Pong), + signed_event_from_nip46_response("farm.publish", Response::Pong), Err(RadrootsSdkError::SignerProtocol { ref mode, ref reason }) if mode == "myc_nip46" && reason.contains("farm.publish") )); assert!(matches!( - sdk_error_from_nip46_error(RadrootsNostrConnectError::Transport { + sdk_error_from_nip46_error(NostrConnectError::Transport { reason: "relay offline".to_owned(), }), RadrootsSdkError::SignerTransport { ref mode, ref reason } if mode == "myc_nip46" && reason == "relay offline" )); assert!(matches!( - sdk_error_from_nip46_error(RadrootsNostrConnectError::Json("bad json".to_owned())), + sdk_error_from_nip46_error(NostrConnectError::Json("bad json".to_owned())), RadrootsSdkError::SignerProtocol { ref mode, ref reason } if mode == "myc_nip46" && reason == "bad json" )); for error in [ - RadrootsNostrConnectError::Encrypt { + NostrConnectError::Encrypt { reason: "encrypt failed".to_owned(), }, - RadrootsNostrConnectError::Decrypt { + NostrConnectError::Decrypt { reason: "decrypt failed".to_owned(), }, - RadrootsNostrConnectError::Sign { + NostrConnectError::Sign { reason: "sign failed".to_owned(), }, - RadrootsNostrConnectError::InvalidRequestPayload { + NostrConnectError::InvalidRequestPayload { method: "sign_event".to_owned(), reason: "request payload failed".to_owned(), }, - RadrootsNostrConnectError::InvalidResponsePayload { + NostrConnectError::InvalidResponsePayload { method: "sign_event".to_owned(), reason: "response payload failed".to_owned(), }, @@ -515,7 +526,7 @@ fn nip46_private_helpers_map_identity_adapter_and_response_edges() { )); } assert!(matches!( - sdk_error_from_nip46_error(RadrootsNostrConnectError::InvalidMethod("ping".to_owned())), + sdk_error_from_nip46_error(NostrConnectError::InvalidMethod("ping".to_owned())), RadrootsSdkError::SignerProtocol { ref mode, ref reason } if mode == "myc_nip46" && reason.contains("invalid NIP-46 method") )); @@ -527,17 +538,18 @@ async fn nip46_transport_adapter_delegates_publish_and_response_poll() { let event = sign_event(&user_keys(), &frozen_draft()); let mut adapter = RadrootsSdkNip46TransportAdapter { transport: transport.as_ref(), + request_timeout: Duration::from_millis(10), }; adapter - .publish_request_event(event) + .publish(ClientEvent::from_json(event.as_json().as_str()).expect("client event")) .await .expect("publish request"); assert_eq!(transport.published().len(), 1); assert!(matches!( - adapter.next_response_event().await, - Err(RadrootsNostrConnectError::RequestTimedOut) + adapter.receive(&CancellationToken::new()).await, + Err(NostrConnectError::RequestTimedOut) )); } @@ -573,11 +585,11 @@ async fn myc_nip46_provider_signs_and_validates_remote_event() { let signed_event = RadrootsNostrEvent::from_json(signed.raw_json()).expect("event"); let transport = Arc::new(MockNip46Transport::new( remote_keys.clone(), - vec![MockNip46Response::Respond( - RadrootsNostrConnectResponse::SignedEvent(connect_signed_event(signed_event)), - )], + vec![MockNip46Response::Respond(Response::SignedEvent( + connect_signed_event(signed_event), + ))], )); - let target = RadrootsNostrConnectClientTarget::new( + let target = nip46_target( remote_keys.public_key(), vec![nostr::RelayUrl::parse("wss://relay.example.com").expect("relay")], ); @@ -614,7 +626,7 @@ async fn myc_nip46_provider_signs_and_validates_remote_event() { assert_eq!(transport.published().len(), 1); let request_messages = transport.published_request_messages(); let sign_event_request = match &request_messages[0].request { - RadrootsNostrConnectRequest::SignEvent(unsigned_event) => unsigned_event, + Request::SignEvent(unsigned_event) => unsigned_event, other => panic!("unexpected NIP-46 request: {other:?}"), }; let sign_event_request: nostr::UnsignedEvent = @@ -708,8 +720,7 @@ async fn myc_nip46_provider_reports_preflight_and_progress_sink_edges() { let remote_keys = remote_keys(); let mismatch_transport = Arc::new(MockNip46Transport::new(remote_keys.clone(), Vec::new())); - let mismatch_target = - RadrootsNostrConnectClientTarget::new(remote_keys.public_key(), Vec::new()); + let mismatch_target = nip46_target(remote_keys.public_key(), Vec::new()); let mismatch_signer = RadrootsSdkMycNip46Signer::new( client_keys(), mismatch_target, @@ -735,7 +746,7 @@ async fn myc_nip46_provider_returns_completion_progress_errors_after_remote_sign let signed = sign_frozen_draft(&user_keys, &draft).expect("signed"); let signed_event = RadrootsNostrEvent::from_json(signed.raw_json()).expect("event"); let (signer, transport) = myc_signer_with_responses(vec![MockNip46Response::Respond( - RadrootsNostrConnectResponse::SignedEvent(connect_signed_event(signed_event)), + Response::SignedEvent(connect_signed_event(signed_event)), )]); let actor = actor(); let mut observed = Vec::new(); @@ -787,11 +798,11 @@ async fn myc_nip46_provider_reports_auth_challenge_progress_and_timeout() { let remote_keys = remote_keys(); let transport = Arc::new(MockNip46Transport::new( remote_keys.clone(), - vec![MockNip46Response::Respond( - RadrootsNostrConnectResponse::AuthUrl("https://auth.example.com/challenge".to_owned()), - )], + vec![MockNip46Response::Respond(Response::AuthUrl( + "https://auth.example.com/challenge".to_owned(), + ))], )); - let target = RadrootsNostrConnectClientTarget::new(remote_keys.public_key(), Vec::new()); + let target = nip46_target(remote_keys.public_key(), Vec::new()); let signer = RadrootsSdkMycNip46Signer::new(client_keys, target, user_pubkey(), transport) .expect("signer"); let mut progress = Vec::new(); @@ -834,11 +845,11 @@ async fn myc_nip46_provider_returns_progress_sink_errors_from_auth_challenge() { let remote_keys = remote_keys(); let transport = Arc::new(MockNip46Transport::new( remote_keys.clone(), - vec![MockNip46Response::Respond( - RadrootsNostrConnectResponse::AuthUrl("https://auth.example.com/challenge".to_owned()), - )], + vec![MockNip46Response::Respond(Response::AuthUrl( + "https://auth.example.com/challenge".to_owned(), + ))], )); - let target = RadrootsNostrConnectClientTarget::new(remote_keys.public_key(), Vec::new()); + let target = nip46_target(remote_keys.public_key(), Vec::new()); let signer = RadrootsSdkMycNip46Signer::new(client_keys, target, user_pubkey(), transport) .expect("signer"); let draft = frozen_draft(); @@ -886,7 +897,7 @@ async fn myc_nip46_provider_rejects_zero_timeout_policy() { if mode == "myc_nip46" && reason.contains("timeout") )); - let target = RadrootsNostrConnectClientTarget::new(remote_keys().public_key(), Vec::new()); + let target = nip46_target(remote_keys().public_key(), Vec::new()); let transport = Arc::new(MockNip46Transport::new(remote_keys(), Vec::new())); let constructor_error = match RadrootsSdkMycNip46Signer::new_with_request_policy( client_keys(), @@ -911,7 +922,7 @@ async fn myc_nip46_provider_rejects_zero_timeout_policy() { async fn myc_nip46_provider_times_out_hanging_transport() { let client_keys = client_keys(); let remote_keys = remote_keys(); - let target = RadrootsNostrConnectClientTarget::new(remote_keys.public_key(), Vec::new()); + let target = nip46_target(remote_keys.public_key(), Vec::new()); let transport = Arc::new(HangingNip46Transport::new()); let policy = RadrootsSdkMycNip46RequestPolicy::new(Duration::from_millis(5)).expect("policy"); let signer = RadrootsSdkMycNip46Signer::new_with_request_policy( @@ -1022,11 +1033,11 @@ async fn myc_nip46_provider_rejects_returned_event_drift() { let signed_event = sign_event(&signing_keys, &drifted_draft); let transport = Arc::new(MockNip46Transport::new( remote_keys.clone(), - vec![MockNip46Response::Respond( - RadrootsNostrConnectResponse::SignedEvent(connect_signed_event(signed_event)), - )], + vec![MockNip46Response::Respond(Response::SignedEvent( + connect_signed_event(signed_event), + ))], )); - let target = RadrootsNostrConnectClientTarget::new(remote_keys.public_key(), Vec::new()); + let target = nip46_target(remote_keys.public_key(), Vec::new()); let signer = RadrootsSdkMycNip46Signer::new(client_keys, target, user_pubkey(), transport) .expect("signer"); let actor = actor();