sdk

Radroots SDK and bindings
git clone https://radroots.dev/git/sdk.git
Log | Files | Refs | README

signer_provider.rs (22086B)


      1 use crate::RadrootsSdkError;
      2 #[cfg(feature = "local-signer")]
      3 use radroots_authority::RadrootsLocalEventSigner;
      4 use radroots_authority::{
      5     RadrootsActorContext, RadrootsEventSigner, RadrootsSignerError, authorize_actor_for_draft,
      6     authorize_signer_for_draft, sign_authorized_draft, validate_signed_event_matches_draft,
      7 };
      8 use radroots_events::draft::{RadrootsFrozenEventDraft, RadrootsSignedNostrEvent};
      9 use radroots_events::ids::RadrootsPublicKey;
     10 use radroots_events::kinds::{
     11     KIND_FARM, KIND_LISTING, KIND_ORDER_CANCELLATION, KIND_ORDER_DECISION, KIND_ORDER_REQUEST,
     12     KIND_ORDER_REVISION_DECISION, KIND_ORDER_REVISION_PROPOSAL,
     13 };
     14 use radroots_nostr::prelude::{RadrootsNostrEvent, RadrootsNostrKeys, radroots_event_from_nostr};
     15 use radroots_nostr_connect::prelude::{
     16     RadrootsNostrConnectClientRequest, RadrootsNostrConnectClientTarget,
     17     RadrootsNostrConnectClientTransport, RadrootsNostrConnectClientTransportFuture,
     18     RadrootsNostrConnectError, RadrootsNostrConnectMethod, RadrootsNostrConnectPermission,
     19     RadrootsNostrConnectPermissions, RadrootsNostrConnectRequest, RadrootsNostrConnectResponse,
     20     execute_request_with_transport,
     21 };
     22 use serde_json::json;
     23 use std::sync::Arc;
     24 use std::time::Duration;
     25 use tokio::time::timeout;
     26 use uuid::Uuid;
     27 
     28 pub type RadrootsSdkNip46TransportFuture<'a, T> = RadrootsNostrConnectClientTransportFuture<'a, T>;
     29 
     30 pub const RADROOTS_SDK_MYC_NIP46_PRODUCT_SIGN_EVENT_KINDS: [u32; 7] = [
     31     KIND_FARM,
     32     KIND_LISTING,
     33     KIND_ORDER_REQUEST,
     34     KIND_ORDER_DECISION,
     35     KIND_ORDER_REVISION_PROPOSAL,
     36     KIND_ORDER_REVISION_DECISION,
     37     KIND_ORDER_CANCELLATION,
     38 ];
     39 pub const RADROOTS_SDK_MYC_NIP46_DEFAULT_REQUEST_TIMEOUT_MS: u64 = 30_000;
     40 
     41 #[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize)]
     42 #[serde(rename_all = "snake_case")]
     43 #[non_exhaustive]
     44 pub enum RadrootsSdkSignerMode {
     45     #[cfg(feature = "local-signer")]
     46     LocalKey,
     47     MycNip46,
     48 }
     49 
     50 impl RadrootsSdkSignerMode {
     51     pub fn as_str(self) -> &'static str {
     52         match self {
     53             #[cfg(feature = "local-signer")]
     54             Self::LocalKey => "local_key",
     55             Self::MycNip46 => "myc_nip46",
     56         }
     57     }
     58 }
     59 
     60 #[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize)]
     61 #[serde(rename_all = "snake_case")]
     62 #[non_exhaustive]
     63 pub enum RadrootsSdkSignerState {
     64     Ready,
     65 }
     66 
     67 #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
     68 pub struct RadrootsSdkSignerStatus {
     69     pub mode: RadrootsSdkSignerMode,
     70     pub state: RadrootsSdkSignerState,
     71     pub signer_pubkey: String,
     72     pub remote_signer_pubkey: Option<String>,
     73     pub relay_count: usize,
     74 }
     75 
     76 #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
     77 pub struct RadrootsSdkSignerCapability {
     78     pub mode: RadrootsSdkSignerMode,
     79     pub signer_pubkey: String,
     80     pub remote_signer_pubkey: Option<String>,
     81     pub relays: Vec<String>,
     82     pub can_sign_events: bool,
     83     pub nip46_permissions: Vec<String>,
     84 }
     85 
     86 #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
     87 #[serde(rename_all = "snake_case", tag = "kind")]
     88 pub enum RadrootsSdkSignerProgress {
     89     RequestStarted {
     90         mode: RadrootsSdkSignerMode,
     91     },
     92     AuthChallenge {
     93         mode: RadrootsSdkSignerMode,
     94         url: String,
     95     },
     96     RequestCompleted {
     97         mode: RadrootsSdkSignerMode,
     98     },
     99 }
    100 
    101 pub trait RadrootsSdkSignerProgressSink {
    102     fn on_signer_progress(
    103         &mut self,
    104         progress: RadrootsSdkSignerProgress,
    105     ) -> Result<(), RadrootsSdkError>;
    106 }
    107 
    108 impl<F> RadrootsSdkSignerProgressSink for F
    109 where
    110     F: FnMut(RadrootsSdkSignerProgress) -> Result<(), RadrootsSdkError>,
    111 {
    112     fn on_signer_progress(
    113         &mut self,
    114         progress: RadrootsSdkSignerProgress,
    115     ) -> Result<(), RadrootsSdkError> {
    116         self(progress)
    117     }
    118 }
    119 
    120 pub struct RadrootsSdkSignRequest<'a> {
    121     pub operation_kind: &'a str,
    122     pub actor: &'a RadrootsActorContext,
    123     pub frozen_draft: &'a RadrootsFrozenEventDraft,
    124     progress_sink: Option<&'a mut dyn RadrootsSdkSignerProgressSink>,
    125 }
    126 
    127 impl<'a> RadrootsSdkSignRequest<'a> {
    128     pub fn new(
    129         operation_kind: &'a str,
    130         actor: &'a RadrootsActorContext,
    131         frozen_draft: &'a RadrootsFrozenEventDraft,
    132     ) -> Self {
    133         Self {
    134             operation_kind,
    135             actor,
    136             frozen_draft,
    137             progress_sink: None,
    138         }
    139     }
    140 
    141     pub fn with_progress_sink(
    142         mut self,
    143         progress_sink: &'a mut dyn RadrootsSdkSignerProgressSink,
    144     ) -> Self {
    145         self.progress_sink = Some(progress_sink);
    146         self
    147     }
    148 
    149     fn emit_progress(
    150         &mut self,
    151         progress: RadrootsSdkSignerProgress,
    152     ) -> Result<(), RadrootsSdkError> {
    153         match self.progress_sink.as_deref_mut() {
    154             Some(progress_sink) => progress_sink.on_signer_progress(progress),
    155             None => Ok(()),
    156         }
    157     }
    158 }
    159 
    160 #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
    161 pub struct RadrootsSdkSignReceipt {
    162     pub operation_kind: String,
    163     pub mode: RadrootsSdkSignerMode,
    164     pub signer_pubkey: String,
    165     pub remote_signer_pubkey: Option<String>,
    166     pub signed_event_id: String,
    167     pub signed_event: RadrootsSignedNostrEvent,
    168 }
    169 
    170 #[derive(Clone)]
    171 pub enum RadrootsSdkSignerProvider {
    172     #[cfg(feature = "local-signer")]
    173     LocalKey(RadrootsSdkLocalKeySigner),
    174     MycNip46(RadrootsSdkMycNip46Signer),
    175 }
    176 
    177 impl RadrootsSdkSignerProvider {
    178     pub fn mode(&self) -> RadrootsSdkSignerMode {
    179         match self {
    180             #[cfg(feature = "local-signer")]
    181             Self::LocalKey(_) => RadrootsSdkSignerMode::LocalKey,
    182             Self::MycNip46(_) => RadrootsSdkSignerMode::MycNip46,
    183         }
    184     }
    185 
    186     pub fn status(&self) -> RadrootsSdkSignerStatus {
    187         match self {
    188             #[cfg(feature = "local-signer")]
    189             Self::LocalKey(signer) => signer.status(),
    190             Self::MycNip46(signer) => signer.status(),
    191         }
    192     }
    193 
    194     pub fn capability(&self) -> RadrootsSdkSignerCapability {
    195         match self {
    196             #[cfg(feature = "local-signer")]
    197             Self::LocalKey(signer) => signer.capability(),
    198             Self::MycNip46(signer) => signer.capability(),
    199         }
    200     }
    201 
    202     pub async fn sign(
    203         &self,
    204         request: RadrootsSdkSignRequest<'_>,
    205     ) -> Result<RadrootsSdkSignReceipt, RadrootsSdkError> {
    206         match self {
    207             #[cfg(feature = "local-signer")]
    208             Self::LocalKey(signer) => signer.sign(request).await,
    209             Self::MycNip46(signer) => signer.sign(request).await,
    210         }
    211     }
    212 }
    213 
    214 #[cfg(feature = "local-signer")]
    215 #[derive(Clone)]
    216 pub struct RadrootsSdkLocalKeySigner {
    217     signer: Arc<RadrootsLocalEventSigner>,
    218     signer_pubkey: String,
    219 }
    220 
    221 #[cfg(feature = "local-signer")]
    222 impl RadrootsSdkLocalKeySigner {
    223     pub fn new(keys: RadrootsNostrKeys) -> Result<Self, RadrootsSdkError> {
    224         let signer = RadrootsLocalEventSigner::new(keys)?;
    225         let signer_pubkey = signer.pubkey().as_str().to_owned();
    226         Ok(Self {
    227             signer: Arc::new(signer),
    228             signer_pubkey,
    229         })
    230     }
    231 
    232     pub fn status(&self) -> RadrootsSdkSignerStatus {
    233         RadrootsSdkSignerStatus {
    234             mode: RadrootsSdkSignerMode::LocalKey,
    235             state: RadrootsSdkSignerState::Ready,
    236             signer_pubkey: self.signer_pubkey.clone(),
    237             remote_signer_pubkey: None,
    238             relay_count: 0,
    239         }
    240     }
    241 
    242     pub fn capability(&self) -> RadrootsSdkSignerCapability {
    243         RadrootsSdkSignerCapability {
    244             mode: RadrootsSdkSignerMode::LocalKey,
    245             signer_pubkey: self.signer_pubkey.clone(),
    246             remote_signer_pubkey: None,
    247             relays: Vec::new(),
    248             can_sign_events: true,
    249             nip46_permissions: Vec::new(),
    250         }
    251     }
    252 
    253     pub async fn sign(
    254         &self,
    255         mut request: RadrootsSdkSignRequest<'_>,
    256     ) -> Result<RadrootsSdkSignReceipt, RadrootsSdkError> {
    257         request.emit_progress(RadrootsSdkSignerProgress::RequestStarted {
    258             mode: RadrootsSdkSignerMode::LocalKey,
    259         })?;
    260         let signed_event =
    261             sign_authorized_draft(request.actor, self.signer.as_ref(), request.frozen_draft)?;
    262         request.emit_progress(RadrootsSdkSignerProgress::RequestCompleted {
    263             mode: RadrootsSdkSignerMode::LocalKey,
    264         })?;
    265         Ok(sign_receipt(
    266             request.operation_kind,
    267             RadrootsSdkSignerMode::LocalKey,
    268             self.signer_pubkey.clone(),
    269             None,
    270             signed_event,
    271         ))
    272     }
    273 }
    274 
    275 pub trait RadrootsSdkNip46Transport: Send + Sync {
    276     fn publish_request_event<'a>(
    277         &'a self,
    278         event: RadrootsNostrEvent,
    279     ) -> RadrootsSdkNip46TransportFuture<'a, ()>;
    280 
    281     fn next_response_event<'a>(&'a self)
    282     -> RadrootsSdkNip46TransportFuture<'a, RadrootsNostrEvent>;
    283 }
    284 
    285 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    286 pub struct RadrootsSdkMycNip46RequestPolicy {
    287     request_timeout: Duration,
    288 }
    289 
    290 impl RadrootsSdkMycNip46RequestPolicy {
    291     pub fn new(request_timeout: Duration) -> Result<Self, RadrootsSdkError> {
    292         if request_timeout.is_zero() {
    293             return Err(RadrootsSdkError::SignerUnavailable {
    294                 mode: RadrootsSdkSignerMode::MycNip46.as_str().to_owned(),
    295                 reason: "myc_nip46 request timeout must be greater than zero".to_owned(),
    296             });
    297         }
    298         Ok(Self { request_timeout })
    299     }
    300 
    301     pub fn request_timeout(self) -> Duration {
    302         self.request_timeout
    303     }
    304 }
    305 
    306 impl Default for RadrootsSdkMycNip46RequestPolicy {
    307     fn default() -> Self {
    308         Self {
    309             request_timeout: Duration::from_millis(
    310                 RADROOTS_SDK_MYC_NIP46_DEFAULT_REQUEST_TIMEOUT_MS,
    311             ),
    312         }
    313     }
    314 }
    315 
    316 #[derive(Clone)]
    317 pub struct RadrootsSdkMycNip46Signer {
    318     client_keys: RadrootsNostrKeys,
    319     target: RadrootsNostrConnectClientTarget,
    320     user_pubkey: RadrootsPublicKey,
    321     transport: Arc<dyn RadrootsSdkNip46Transport>,
    322     request_policy: RadrootsSdkMycNip46RequestPolicy,
    323     request_id_generator: Arc<dyn RadrootsSdkMycNip46RequestIdGenerator>,
    324 }
    325 
    326 impl RadrootsSdkMycNip46Signer {
    327     pub fn new(
    328         client_keys: RadrootsNostrKeys,
    329         target: RadrootsNostrConnectClientTarget,
    330         user_pubkey: impl AsRef<str>,
    331         transport: Arc<dyn RadrootsSdkNip46Transport>,
    332     ) -> Result<Self, RadrootsSdkError> {
    333         Self::new_with_request_policy(
    334             client_keys,
    335             target,
    336             user_pubkey,
    337             transport,
    338             RadrootsSdkMycNip46RequestPolicy::default(),
    339         )
    340     }
    341 
    342     pub fn new_with_request_policy(
    343         client_keys: RadrootsNostrKeys,
    344         target: RadrootsNostrConnectClientTarget,
    345         user_pubkey: impl AsRef<str>,
    346         transport: Arc<dyn RadrootsSdkNip46Transport>,
    347         request_policy: RadrootsSdkMycNip46RequestPolicy,
    348     ) -> Result<Self, RadrootsSdkError> {
    349         Self::new_with_request_id_generator(
    350             client_keys,
    351             target,
    352             user_pubkey,
    353             transport,
    354             request_policy,
    355             Arc::new(RadrootsSdkUuidNip46RequestIdGenerator),
    356         )
    357     }
    358 
    359     fn new_with_request_id_generator(
    360         client_keys: RadrootsNostrKeys,
    361         target: RadrootsNostrConnectClientTarget,
    362         user_pubkey: impl AsRef<str>,
    363         transport: Arc<dyn RadrootsSdkNip46Transport>,
    364         request_policy: RadrootsSdkMycNip46RequestPolicy,
    365         request_id_generator: Arc<dyn RadrootsSdkMycNip46RequestIdGenerator>,
    366     ) -> Result<Self, RadrootsSdkError> {
    367         RadrootsSdkMycNip46RequestPolicy::new(request_policy.request_timeout())?;
    368         let user_pubkey = RadrootsPublicKey::parse(user_pubkey.as_ref()).map_err(|error| {
    369             RadrootsSdkError::InvalidRequest {
    370                 message: format!("myc_nip46 user pubkey is invalid: {error}"),
    371             }
    372         })?;
    373         Ok(Self {
    374             client_keys,
    375             target,
    376             user_pubkey,
    377             transport,
    378             request_policy,
    379             request_id_generator,
    380         })
    381     }
    382 
    383     pub fn status(&self) -> RadrootsSdkSignerStatus {
    384         RadrootsSdkSignerStatus {
    385             mode: RadrootsSdkSignerMode::MycNip46,
    386             state: RadrootsSdkSignerState::Ready,
    387             signer_pubkey: self.user_pubkey.as_str().to_owned(),
    388             remote_signer_pubkey: Some(self.target.remote_signer_public_key.to_hex()),
    389             relay_count: self.target.relays.len(),
    390         }
    391     }
    392 
    393     pub fn capability(&self) -> RadrootsSdkSignerCapability {
    394         RadrootsSdkSignerCapability {
    395             mode: RadrootsSdkSignerMode::MycNip46,
    396             signer_pubkey: self.user_pubkey.as_str().to_owned(),
    397             remote_signer_pubkey: Some(self.target.remote_signer_public_key.to_hex()),
    398             relays: self.target.relays.iter().map(ToString::to_string).collect(),
    399             can_sign_events: true,
    400             nip46_permissions: radroots_sdk_myc_nip46_product_permission_strings(),
    401         }
    402     }
    403 
    404     pub async fn sign(
    405         &self,
    406         mut request: RadrootsSdkSignRequest<'_>,
    407     ) -> Result<RadrootsSdkSignReceipt, RadrootsSdkError> {
    408         request.emit_progress(RadrootsSdkSignerProgress::RequestStarted {
    409             mode: RadrootsSdkSignerMode::MycNip46,
    410         })?;
    411         authorize_actor_for_draft(request.actor, request.frozen_draft)?;
    412         let signer_identity = RadrootsSdkSignerIdentityOnly {
    413             pubkey: self.user_pubkey.clone(),
    414         };
    415         authorize_signer_for_draft(&signer_identity, request.frozen_draft)?;
    416         let sign_event_request = sign_event_request_from_frozen_draft(request.frozen_draft)?;
    417         let request_id = self.next_request_id();
    418         let mut adapter = RadrootsSdkNip46TransportAdapter {
    419             transport: self.transport.as_ref(),
    420         };
    421         let mut progress_error = None;
    422         let request_future = execute_request_with_transport(
    423             &self.client_keys,
    424             &self.target,
    425             RadrootsNostrConnectClientRequest::new(request_id, sign_event_request),
    426             &mut adapter,
    427             |progress| {
    428                 let sdk_progress = match progress {
    429                     radroots_nostr_connect::prelude::RadrootsNostrConnectClientProgress::AuthChallenge {
    430                         url,
    431                     } => RadrootsSdkSignerProgress::AuthChallenge {
    432                         mode: RadrootsSdkSignerMode::MycNip46,
    433                         url,
    434                     },
    435                 };
    436                 if let Err(error) = request.emit_progress(sdk_progress) {
    437                     progress_error = Some(error);
    438                     return Err(RadrootsNostrConnectError::Transport {
    439                         reason: "SDK signer progress sink failed".to_owned(),
    440                     });
    441                 }
    442                 Ok(())
    443             },
    444         );
    445         let response = timeout(self.request_policy.request_timeout(), request_future)
    446             .await
    447             .map_err(|_| RadrootsNostrConnectError::RequestTimedOut)
    448             .and_then(|response| response);
    449         if let Some(error) = progress_error {
    450             return Err(error);
    451         }
    452         let response = response.map_err(sdk_error_from_nip46_error)?;
    453         let signed_event = signed_event_from_nip46_response(request.operation_kind, response)?;
    454         validate_signed_event_matches_draft(&signed_event, request.frozen_draft).map_err(
    455             |error| RadrootsSdkError::SignerReturnedEventDrift {
    456                 operation: request.operation_kind.to_owned(),
    457                 reason: error.to_string(),
    458             },
    459         )?;
    460         request.emit_progress(RadrootsSdkSignerProgress::RequestCompleted {
    461             mode: RadrootsSdkSignerMode::MycNip46,
    462         })?;
    463         Ok(sign_receipt(
    464             request.operation_kind,
    465             RadrootsSdkSignerMode::MycNip46,
    466             self.user_pubkey.as_str().to_owned(),
    467             Some(self.target.remote_signer_public_key.to_hex()),
    468             signed_event,
    469         ))
    470     }
    471 
    472     fn next_request_id(&self) -> String {
    473         self.request_id_generator.next_request_id()
    474     }
    475 }
    476 
    477 trait RadrootsSdkMycNip46RequestIdGenerator: Send + Sync {
    478     fn next_request_id(&self) -> String;
    479 }
    480 
    481 struct RadrootsSdkUuidNip46RequestIdGenerator;
    482 
    483 impl RadrootsSdkMycNip46RequestIdGenerator for RadrootsSdkUuidNip46RequestIdGenerator {
    484     fn next_request_id(&self) -> String {
    485         format!("radroots-sdk-myc-nip46-sign-{}", Uuid::new_v4())
    486     }
    487 }
    488 
    489 pub fn radroots_sdk_myc_nip46_product_permissions() -> RadrootsNostrConnectPermissions {
    490     RADROOTS_SDK_MYC_NIP46_PRODUCT_SIGN_EVENT_KINDS
    491         .iter()
    492         .map(|kind| {
    493             RadrootsNostrConnectPermission::with_parameter(
    494                 RadrootsNostrConnectMethod::SignEvent,
    495                 kind.to_string(),
    496             )
    497         })
    498         .collect::<Vec<_>>()
    499         .into()
    500 }
    501 
    502 pub fn radroots_sdk_myc_nip46_product_permission_strings() -> Vec<String> {
    503     radroots_sdk_myc_nip46_product_permissions()
    504         .as_slice()
    505         .iter()
    506         .map(ToString::to_string)
    507         .collect()
    508 }
    509 
    510 struct RadrootsSdkSignerIdentityOnly {
    511     pubkey: RadrootsPublicKey,
    512 }
    513 
    514 impl RadrootsEventSigner for RadrootsSdkSignerIdentityOnly {
    515     fn pubkey(&self) -> &RadrootsPublicKey {
    516         &self.pubkey
    517     }
    518 
    519     fn sign_frozen_draft(
    520         &self,
    521         _draft: &RadrootsFrozenEventDraft,
    522     ) -> Result<RadrootsSignedNostrEvent, RadrootsSignerError> {
    523         Err(RadrootsSignerError::Unavailable)
    524     }
    525 }
    526 
    527 struct RadrootsSdkNip46TransportAdapter<'a> {
    528     transport: &'a dyn RadrootsSdkNip46Transport,
    529 }
    530 
    531 impl RadrootsNostrConnectClientTransport for RadrootsSdkNip46TransportAdapter<'_> {
    532     fn publish_request_event<'a>(
    533         &'a mut self,
    534         event: RadrootsNostrEvent,
    535     ) -> RadrootsNostrConnectClientTransportFuture<'a, ()> {
    536         self.transport.publish_request_event(event)
    537     }
    538 
    539     fn next_response_event<'a>(
    540         &'a mut self,
    541     ) -> RadrootsNostrConnectClientTransportFuture<'a, RadrootsNostrEvent> {
    542         self.transport.next_response_event()
    543     }
    544 }
    545 
    546 fn sign_event_request_from_frozen_draft(
    547     draft: &RadrootsFrozenEventDraft,
    548 ) -> Result<RadrootsNostrConnectRequest, RadrootsSdkError> {
    549     let unsigned_event = serde_json::from_value(json!({
    550         "pubkey": draft.expected_pubkey,
    551         "created_at": draft.created_at,
    552         "kind": draft.kind,
    553         "tags": draft.tags,
    554         "content": draft.content,
    555     }))
    556     .map_err(|error| RadrootsSdkError::SignerProtocol {
    557         mode: RadrootsSdkSignerMode::MycNip46.as_str().to_owned(),
    558         reason: format!("failed to convert frozen draft to NIP-46 unsigned event: {error}"),
    559     })?;
    560     Ok(RadrootsNostrConnectRequest::SignEvent(unsigned_event))
    561 }
    562 
    563 fn signed_event_from_nip46_response(
    564     operation_kind: &str,
    565     response: RadrootsNostrConnectResponse,
    566 ) -> Result<RadrootsSignedNostrEvent, RadrootsSdkError> {
    567     match response {
    568         RadrootsNostrConnectResponse::SignedEvent(event) => {
    569             let raw_json = serde_json::to_string(&event).map_err(|error| {
    570                 RadrootsSdkError::SignerProtocol {
    571                     mode: RadrootsSdkSignerMode::MycNip46.as_str().to_owned(),
    572                     reason: format!("failed to serialize remote signed event: {error}"),
    573                 }
    574             })?;
    575             RadrootsSignedNostrEvent::from_event(radroots_event_from_nostr(&event), raw_json)
    576                 .map_err(|error| RadrootsSdkError::SignerProtocol {
    577                     mode: RadrootsSdkSignerMode::MycNip46.as_str().to_owned(),
    578                     reason: format!("remote signed event is invalid: {error}"),
    579                 })
    580         }
    581         RadrootsNostrConnectResponse::Error { error, .. } => {
    582             Err(RadrootsSdkError::SignerRequestRejected {
    583                 mode: RadrootsSdkSignerMode::MycNip46.as_str().to_owned(),
    584                 reason: error,
    585             })
    586         }
    587         RadrootsNostrConnectResponse::PendingConnection => {
    588             Err(RadrootsSdkError::SignerAuthChallengePending {
    589                 mode: RadrootsSdkSignerMode::MycNip46.as_str().to_owned(),
    590                 auth_url: None,
    591             })
    592         }
    593         other => Err(RadrootsSdkError::SignerProtocol {
    594             mode: RadrootsSdkSignerMode::MycNip46.as_str().to_owned(),
    595             reason: format!("unexpected NIP-46 response for {operation_kind}: {other:?}"),
    596         }),
    597     }
    598 }
    599 
    600 fn sdk_error_from_nip46_error(error: RadrootsNostrConnectError) -> RadrootsSdkError {
    601     match error {
    602         RadrootsNostrConnectError::RequestTimedOut => RadrootsSdkError::SignerRequestTimedOut {
    603             mode: RadrootsSdkSignerMode::MycNip46.as_str().to_owned(),
    604         },
    605         RadrootsNostrConnectError::Transport { reason } => RadrootsSdkError::SignerTransport {
    606             mode: RadrootsSdkSignerMode::MycNip46.as_str().to_owned(),
    607             reason,
    608         },
    609         RadrootsNostrConnectError::Encrypt { reason }
    610         | RadrootsNostrConnectError::Decrypt { reason }
    611         | RadrootsNostrConnectError::Sign { reason }
    612         | RadrootsNostrConnectError::Json(reason)
    613         | RadrootsNostrConnectError::InvalidRequestPayload { reason, .. }
    614         | RadrootsNostrConnectError::InvalidResponsePayload { reason, .. } => {
    615             RadrootsSdkError::SignerProtocol {
    616                 mode: RadrootsSdkSignerMode::MycNip46.as_str().to_owned(),
    617                 reason,
    618             }
    619         }
    620         error => RadrootsSdkError::SignerProtocol {
    621             mode: RadrootsSdkSignerMode::MycNip46.as_str().to_owned(),
    622             reason: error.to_string(),
    623         },
    624     }
    625 }
    626 
    627 fn sign_receipt(
    628     operation_kind: &str,
    629     mode: RadrootsSdkSignerMode,
    630     signer_pubkey: String,
    631     remote_signer_pubkey: Option<String>,
    632     signed_event: RadrootsSignedNostrEvent,
    633 ) -> RadrootsSdkSignReceipt {
    634     RadrootsSdkSignReceipt {
    635         operation_kind: operation_kind.to_owned(),
    636         mode,
    637         signer_pubkey,
    638         remote_signer_pubkey,
    639         signed_event_id: signed_event.id.clone(),
    640         signed_event,
    641     }
    642 }
    643 
    644 #[cfg(test)]
    645 #[path = "../tests/unit/signer_provider_tests.rs"]
    646 mod tests;