1pub mod cwi_logic;
13pub mod types;
15pub mod ump_token;
17
18use std::sync::Arc;
19
20use async_trait::async_trait;
21use tokio::sync::{Mutex, RwLock};
22
23use bsv::primitives::big_number::BigNumber;
24use bsv::primitives::private_key::PrivateKey;
25use bsv::script::templates::r_puzzle::{RPuzzle, RPuzzleType};
26use bsv::wallet::interfaces::{
27 AbortActionArgs, AbortActionResult, AcquireCertificateArgs, AuthenticatedResult, Certificate,
28 CreateActionArgs, CreateActionResult, CreateHmacArgs, CreateHmacResult, CreateSignatureArgs,
29 CreateSignatureResult, DecryptArgs, DecryptResult, DiscoverByAttributesArgs,
30 DiscoverByIdentityKeyArgs, DiscoverCertificatesResult, EncryptArgs, EncryptResult,
31 GetHeaderArgs, GetHeaderResult, GetHeightResult, GetNetworkResult, GetPublicKeyArgs,
32 GetPublicKeyResult, GetVersionResult, InternalizeActionArgs, InternalizeActionResult,
33 ListActionsArgs, ListActionsResult, ListCertificatesArgs, ListCertificatesResult,
34 ListOutputsArgs, ListOutputsResult, ProveCertificateArgs, ProveCertificateResult,
35 RelinquishCertificateArgs, RelinquishCertificateResult, RelinquishOutputArgs,
36 RelinquishOutputResult, RevealCounterpartyKeyLinkageArgs, RevealCounterpartyKeyLinkageResult,
37 RevealSpecificKeyLinkageArgs, RevealSpecificKeyLinkageResult, SignActionArgs, SignActionResult,
38 VerifyHmacArgs, VerifyHmacResult, VerifySignatureArgs, VerifySignatureResult, WalletInterface,
39};
40
41use crate::wab_client::interactors::AuthMethodInteractor;
42use crate::wab_client::types::{FaucetResponse, StartAuthResponse};
43use crate::wab_client::WABClient;
44use crate::WalletError;
45
46use types::{AuthState, StateSnapshot, WalletBuilderFn};
47
48macro_rules! delegate_or_not_active {
51 ($self:ident, $method:ident, $args:expr, $originator:expr) => {{
52 let guard = $self.inner.read().await;
53 match guard.as_ref() {
54 Some(w) => w.$method($args, $originator).await,
55 None => Err(bsv::wallet::error::WalletError::Internal(
56 "WERR_NOT_ACTIVE: Wallet is not authenticated".into(),
57 )),
58 }
59 }};
60 ($self:ident, $method:ident, $originator:expr) => {{
62 let guard = $self.inner.read().await;
63 match guard.as_ref() {
64 Some(w) => w.$method($originator).await,
65 None => Err(bsv::wallet::error::WalletError::Internal(
66 "WERR_NOT_ACTIVE: Wallet is not authenticated".into(),
67 )),
68 }
69 }};
70}
71
72pub struct WalletAuthenticationManager {
88 inner: RwLock<Option<Arc<dyn WalletInterface + Send + Sync>>>,
90 auth_tx: tokio::sync::watch::Sender<bool>,
92 auth_rx: tokio::sync::watch::Receiver<bool>,
94 wab_client: WABClient,
96 wallet_builder: WalletBuilderFn,
98 admin_originator: String,
100 state: Mutex<AuthState>,
102 presentation_key: Mutex<Option<String>>,
104}
105
106impl WalletAuthenticationManager {
107 pub fn new(
115 wab_client: WABClient,
116 wallet_builder: WalletBuilderFn,
117 admin_originator: String,
118 state_snapshot: Option<Vec<u8>>,
119 ) -> Result<Self, WalletError> {
120 let (auth_tx, auth_rx) = tokio::sync::watch::channel(false);
121
122 let (initial_state, initial_key) = if let Some(snapshot_bytes) = state_snapshot {
123 let snapshot: StateSnapshot = serde_json::from_slice(&snapshot_bytes)?;
124 let key = snapshot.presentation_key.clone();
125 (snapshot.auth_state, key)
126 } else {
127 (AuthState::Unauthenticated, None)
128 };
129
130 Ok(Self {
131 inner: RwLock::new(None),
132 auth_tx,
133 auth_rx,
134 wab_client,
135 wallet_builder,
136 admin_originator,
137 state: Mutex::new(initial_state),
138 presentation_key: Mutex::new(initial_key),
139 })
140 }
141
142 pub async fn start_auth(
155 &self,
156 interactor: &dyn AuthMethodInteractor,
157 payload: serde_json::Value,
158 ) -> Result<StartAuthResponse, WalletError> {
159 {
161 let mut pk = self.presentation_key.lock().await;
162 if pk.is_none() {
163 *pk = Some(WABClient::generate_random_presentation_key()?);
164 }
165 }
166
167 let pk = self.presentation_key.lock().await;
168 let presentation_key = pk
169 .as_ref()
170 .ok_or_else(|| WalletError::Internal("Presentation key missing".to_string()))?;
171
172 {
174 let mut state = self.state.lock().await;
175 *state = AuthState::Authenticating;
176 }
177
178 let method_type = interactor.method_type();
179 self.wab_client
180 .start_auth_method(presentation_key, method_type, payload)
181 .await
182 }
183
184 pub async fn complete_auth(
198 &self,
199 interactor: &dyn AuthMethodInteractor,
200 payload: serde_json::Value,
201 ) -> Result<(), WalletError> {
202 let temp_key = {
203 let mut pk = self.presentation_key.lock().await;
204 pk.take().ok_or_else(|| {
205 WalletError::Internal("No presentation key; call start_auth first".to_string())
206 })?
207 };
208
209 let method_type = interactor.method_type();
210 let result = self
211 .wab_client
212 .complete_auth_method(&temp_key, method_type, payload)
213 .await?;
214
215 if !result.success {
216 let msg = result
217 .message
218 .unwrap_or_else(|| "Failed to complete WAB auth".to_string());
219 let mut state = self.state.lock().await;
220 *state = AuthState::Failed(msg.clone());
221 return Err(WalletError::Internal(msg));
222 }
223
224 let presentation_key_hex = result.presentation_key.ok_or_else(|| {
225 WalletError::Internal("No presentation key returned from WAB".to_string())
226 })?;
227
228 {
230 let mut pk = self.presentation_key.lock().await;
231 *pk = Some(presentation_key_hex.clone());
232 }
233
234 let presentation_key_bytes = hex_to_bytes(&presentation_key_hex)?;
237 let _is_new_user = {
238 let guard = self.inner.read().await;
239 if let Some(ref _w) = *guard {
240 false
242 } else {
243 true
245 }
246 };
247
248 let root_key = if presentation_key_bytes.len() >= 32 {
252 presentation_key_bytes.clone()
253 } else {
254 return Err(WalletError::Internal(
255 "Presentation key too short for root key derivation".to_string(),
256 ));
257 };
258
259 let priv_key_manager = Arc::new(crate::wallet::privileged::NoOpPrivilegedKeyManager);
261
262 let wallet = (self.wallet_builder)(root_key, priv_key_manager).await?;
264
265 {
267 let mut inner = self.inner.write().await;
268 *inner = Some(wallet);
269 }
270
271 {
273 let mut state = self.state.lock().await;
274 *state = AuthState::Authenticated;
275 }
276
277 let _ = self.auth_tx.send(true);
279
280 Ok(())
281 }
282
283 pub async fn get_state_snapshot(&self) -> Result<Vec<u8>, WalletError> {
288 let state = self.state.lock().await.clone();
289 let pk = self.presentation_key.lock().await.clone();
290 let snapshot = StateSnapshot {
291 presentation_key: pk,
292 auth_state: state,
293 profile: None,
294 is_new_user: None,
295 };
296 serde_json::to_vec(&snapshot).map_err(|e| {
297 WalletError::Internal(format!("Failed to serialize state snapshot: {}", e))
298 })
299 }
300
301 #[allow(dead_code)]
316 async fn redeem_faucet(
317 &self,
318 wallet: &(dyn WalletInterface + Send + Sync),
319 faucet_response: &FaucetResponse,
320 ) -> Result<(), WalletError> {
321 let k_hex = faucet_response
322 .k
323 .as_ref()
324 .ok_or_else(|| WalletError::Internal("Faucet response missing k value".to_string()))?;
325 let txid = faucet_response
326 .txid
327 .as_ref()
328 .ok_or_else(|| WalletError::Internal("Faucet response missing txid".to_string()))?;
329 let tx_hex = faucet_response
330 .tx
331 .as_ref()
332 .ok_or_else(|| WalletError::Internal("Faucet response missing tx data".to_string()))?;
333
334 let tx_bytes = hex_to_bytes(tx_hex)?;
336
337 let create_args = CreateActionArgs {
339 input_beef: Some(tx_bytes),
340 inputs: vec![bsv::wallet::interfaces::CreateActionInput {
341 outpoint: format!("{}.0", txid),
342 unlocking_script_length: Some(108),
343 input_description: "Fund from faucet".to_string(),
344 unlocking_script: None,
345 sequence_number: None,
346 }],
347 description: "Fund wallet".to_string(),
348 outputs: vec![],
349 labels: vec![],
350 lock_time: None,
351 version: None,
352 options: Some(bsv::wallet::interfaces::CreateActionOptions {
353 accept_delayed_broadcast: bsv::wallet::types::BooleanDefaultTrue(Some(false)),
354 ..Default::default()
355 }),
356 reference: None,
357 };
358
359 let create_result = wallet
360 .create_action(create_args, Some(&self.admin_originator))
361 .await
362 .map_err(|e| WalletError::Internal(format!("Faucet create_action failed: {}", e)))?;
363
364 let signable = create_result.signable_transaction.ok_or_else(|| {
365 WalletError::Internal("No signable transaction from faucet create_action".to_string())
366 })?;
367
368 let k = BigNumber::from_hex(k_hex)
370 .map_err(|e| WalletError::Internal(format!("Failed to parse faucet k value: {}", e)))?;
371 let random_key = PrivateKey::from_random().map_err(|e| {
372 WalletError::Internal(format!("Failed to generate random key for RPuzzle: {}", e))
373 })?;
374
375 let puzzle = RPuzzle::from_k(RPuzzleType::Raw, vec![], k, random_key);
378
379 let tx_hex_str: String = signable.tx.iter().map(|b| format!("{:02x}", b)).collect();
381
382 let tx = bsv::transaction::Transaction::from_beef(&tx_hex_str).map_err(|e| {
384 WalletError::Internal(format!("Failed to parse signable transaction BEEF: {}", e))
385 })?;
386
387 let (source_sats, source_script) = tx
390 .inputs
391 .first()
392 .and_then(|inp| {
393 inp.source_transaction.as_ref().map(|stx| {
394 let vout = inp.source_output_index as usize;
395 let output = &stx.outputs[vout];
396 (output.satoshis, output.locking_script.clone())
397 })
398 })
399 .ok_or_else(|| {
400 WalletError::Internal(
401 "Faucet signable tx missing source transaction on input 0".to_string(),
402 )
403 })?;
404
405 let preimage = tx
407 .sighash_preimage(
408 0,
409 bsv::primitives::transaction_signature::SIGHASH_ALL
410 | bsv::primitives::transaction_signature::SIGHASH_FORKID,
411 source_sats.unwrap_or(0),
412 &source_script,
413 )
414 .map_err(|e| {
415 WalletError::Internal(format!("Failed to compute sighash preimage: {}", e))
416 })?;
417
418 let unlocking_script = puzzle
420 .unlock(&preimage)
421 .map_err(|e| WalletError::Internal(format!("RPuzzle unlock failed: {}", e)))?;
422
423 let mut spends = std::collections::HashMap::new();
425 spends.insert(
426 0u32,
427 bsv::wallet::interfaces::SignActionSpend {
428 unlocking_script: unlocking_script.to_binary(),
429 sequence_number: None,
430 },
431 );
432
433 let _sign_result = wallet
434 .sign_action(
435 SignActionArgs {
436 reference: signable.reference,
437 spends,
438 options: None,
439 },
440 Some(&self.admin_originator),
441 )
442 .await
443 .map_err(|e| WalletError::Internal(format!("Faucet sign_action failed: {}", e)))?;
444
445 Ok(())
446 }
447}
448
449fn hex_to_bytes(hex: &str) -> Result<Vec<u8>, WalletError> {
451 if hex.len() % 2 != 0 {
452 return Err(WalletError::Internal(format!(
453 "Invalid hex string length: {}",
454 hex.len()
455 )));
456 }
457 (0..hex.len())
458 .step_by(2)
459 .map(|i| {
460 u8::from_str_radix(&hex[i..i + 2], 16)
461 .map_err(|e| WalletError::Internal(format!("Invalid hex at position {}: {}", i, e)))
462 })
463 .collect()
464}
465
466#[async_trait]
471impl WalletInterface for WalletAuthenticationManager {
472 async fn is_authenticated(
473 &self,
474 _originator: Option<&str>,
475 ) -> Result<AuthenticatedResult, bsv::wallet::error::WalletError> {
476 let guard = self.inner.read().await;
477 Ok(AuthenticatedResult {
478 authenticated: guard.is_some(),
479 })
480 }
481
482 async fn wait_for_authentication(
483 &self,
484 _originator: Option<&str>,
485 ) -> Result<AuthenticatedResult, bsv::wallet::error::WalletError> {
486 let mut rx = self.auth_rx.clone();
487 if *rx.borrow() {
489 return Ok(AuthenticatedResult {
490 authenticated: true,
491 });
492 }
493 while rx.changed().await.is_ok() {
495 if *rx.borrow() {
496 return Ok(AuthenticatedResult {
497 authenticated: true,
498 });
499 }
500 }
501 Ok(AuthenticatedResult {
503 authenticated: false,
504 })
505 }
506
507 async fn get_height(
508 &self,
509 originator: Option<&str>,
510 ) -> Result<GetHeightResult, bsv::wallet::error::WalletError> {
511 delegate_or_not_active!(self, get_height, originator)
512 }
513
514 async fn get_header_for_height(
515 &self,
516 args: GetHeaderArgs,
517 originator: Option<&str>,
518 ) -> Result<GetHeaderResult, bsv::wallet::error::WalletError> {
519 delegate_or_not_active!(self, get_header_for_height, args, originator)
520 }
521
522 async fn get_network(
523 &self,
524 originator: Option<&str>,
525 ) -> Result<GetNetworkResult, bsv::wallet::error::WalletError> {
526 delegate_or_not_active!(self, get_network, originator)
527 }
528
529 async fn get_version(
530 &self,
531 originator: Option<&str>,
532 ) -> Result<GetVersionResult, bsv::wallet::error::WalletError> {
533 delegate_or_not_active!(self, get_version, originator)
534 }
535
536 async fn create_action(
537 &self,
538 args: CreateActionArgs,
539 originator: Option<&str>,
540 ) -> Result<CreateActionResult, bsv::wallet::error::WalletError> {
541 delegate_or_not_active!(self, create_action, args, originator)
542 }
543
544 async fn sign_action(
545 &self,
546 args: SignActionArgs,
547 originator: Option<&str>,
548 ) -> Result<SignActionResult, bsv::wallet::error::WalletError> {
549 delegate_or_not_active!(self, sign_action, args, originator)
550 }
551
552 async fn abort_action(
553 &self,
554 args: AbortActionArgs,
555 originator: Option<&str>,
556 ) -> Result<AbortActionResult, bsv::wallet::error::WalletError> {
557 delegate_or_not_active!(self, abort_action, args, originator)
558 }
559
560 async fn list_actions(
561 &self,
562 args: ListActionsArgs,
563 originator: Option<&str>,
564 ) -> Result<ListActionsResult, bsv::wallet::error::WalletError> {
565 delegate_or_not_active!(self, list_actions, args, originator)
566 }
567
568 async fn internalize_action(
569 &self,
570 args: InternalizeActionArgs,
571 originator: Option<&str>,
572 ) -> Result<InternalizeActionResult, bsv::wallet::error::WalletError> {
573 delegate_or_not_active!(self, internalize_action, args, originator)
574 }
575
576 async fn list_outputs(
577 &self,
578 args: ListOutputsArgs,
579 originator: Option<&str>,
580 ) -> Result<ListOutputsResult, bsv::wallet::error::WalletError> {
581 delegate_or_not_active!(self, list_outputs, args, originator)
582 }
583
584 async fn relinquish_output(
585 &self,
586 args: RelinquishOutputArgs,
587 originator: Option<&str>,
588 ) -> Result<RelinquishOutputResult, bsv::wallet::error::WalletError> {
589 delegate_or_not_active!(self, relinquish_output, args, originator)
590 }
591
592 async fn get_public_key(
593 &self,
594 args: GetPublicKeyArgs,
595 originator: Option<&str>,
596 ) -> Result<GetPublicKeyResult, bsv::wallet::error::WalletError> {
597 delegate_or_not_active!(self, get_public_key, args, originator)
598 }
599
600 async fn reveal_counterparty_key_linkage(
601 &self,
602 args: RevealCounterpartyKeyLinkageArgs,
603 originator: Option<&str>,
604 ) -> Result<RevealCounterpartyKeyLinkageResult, bsv::wallet::error::WalletError> {
605 delegate_or_not_active!(self, reveal_counterparty_key_linkage, args, originator)
606 }
607
608 async fn reveal_specific_key_linkage(
609 &self,
610 args: RevealSpecificKeyLinkageArgs,
611 originator: Option<&str>,
612 ) -> Result<RevealSpecificKeyLinkageResult, bsv::wallet::error::WalletError> {
613 delegate_or_not_active!(self, reveal_specific_key_linkage, args, originator)
614 }
615
616 async fn encrypt(
617 &self,
618 args: EncryptArgs,
619 originator: Option<&str>,
620 ) -> Result<EncryptResult, bsv::wallet::error::WalletError> {
621 delegate_or_not_active!(self, encrypt, args, originator)
622 }
623
624 async fn decrypt(
625 &self,
626 args: DecryptArgs,
627 originator: Option<&str>,
628 ) -> Result<DecryptResult, bsv::wallet::error::WalletError> {
629 delegate_or_not_active!(self, decrypt, args, originator)
630 }
631
632 async fn create_hmac(
633 &self,
634 args: CreateHmacArgs,
635 originator: Option<&str>,
636 ) -> Result<CreateHmacResult, bsv::wallet::error::WalletError> {
637 delegate_or_not_active!(self, create_hmac, args, originator)
638 }
639
640 async fn verify_hmac(
641 &self,
642 args: VerifyHmacArgs,
643 originator: Option<&str>,
644 ) -> Result<VerifyHmacResult, bsv::wallet::error::WalletError> {
645 delegate_or_not_active!(self, verify_hmac, args, originator)
646 }
647
648 async fn create_signature(
649 &self,
650 args: CreateSignatureArgs,
651 originator: Option<&str>,
652 ) -> Result<CreateSignatureResult, bsv::wallet::error::WalletError> {
653 delegate_or_not_active!(self, create_signature, args, originator)
654 }
655
656 async fn verify_signature(
657 &self,
658 args: VerifySignatureArgs,
659 originator: Option<&str>,
660 ) -> Result<VerifySignatureResult, bsv::wallet::error::WalletError> {
661 delegate_or_not_active!(self, verify_signature, args, originator)
662 }
663
664 async fn acquire_certificate(
665 &self,
666 args: AcquireCertificateArgs,
667 originator: Option<&str>,
668 ) -> Result<Certificate, bsv::wallet::error::WalletError> {
669 delegate_or_not_active!(self, acquire_certificate, args, originator)
670 }
671
672 async fn list_certificates(
673 &self,
674 args: ListCertificatesArgs,
675 originator: Option<&str>,
676 ) -> Result<ListCertificatesResult, bsv::wallet::error::WalletError> {
677 delegate_or_not_active!(self, list_certificates, args, originator)
678 }
679
680 async fn prove_certificate(
681 &self,
682 args: ProveCertificateArgs,
683 originator: Option<&str>,
684 ) -> Result<ProveCertificateResult, bsv::wallet::error::WalletError> {
685 delegate_or_not_active!(self, prove_certificate, args, originator)
686 }
687
688 async fn relinquish_certificate(
689 &self,
690 args: RelinquishCertificateArgs,
691 originator: Option<&str>,
692 ) -> Result<RelinquishCertificateResult, bsv::wallet::error::WalletError> {
693 delegate_or_not_active!(self, relinquish_certificate, args, originator)
694 }
695
696 async fn discover_by_identity_key(
697 &self,
698 args: DiscoverByIdentityKeyArgs,
699 originator: Option<&str>,
700 ) -> Result<DiscoverCertificatesResult, bsv::wallet::error::WalletError> {
701 delegate_or_not_active!(self, discover_by_identity_key, args, originator)
702 }
703
704 async fn discover_by_attributes(
705 &self,
706 args: DiscoverByAttributesArgs,
707 originator: Option<&str>,
708 ) -> Result<DiscoverCertificatesResult, bsv::wallet::error::WalletError> {
709 delegate_or_not_active!(self, discover_by_attributes, args, originator)
710 }
711}
712
713#[cfg(test)]
714mod tests {
715 use super::*;
716
717 struct MockWallet;
720
721 #[async_trait]
722 impl WalletInterface for MockWallet {
723 async fn is_authenticated(
724 &self,
725 _originator: Option<&str>,
726 ) -> Result<AuthenticatedResult, bsv::wallet::error::WalletError> {
727 Ok(AuthenticatedResult {
728 authenticated: true,
729 })
730 }
731
732 async fn wait_for_authentication(
733 &self,
734 _originator: Option<&str>,
735 ) -> Result<AuthenticatedResult, bsv::wallet::error::WalletError> {
736 Ok(AuthenticatedResult {
737 authenticated: true,
738 })
739 }
740
741 async fn get_height(
742 &self,
743 _o: Option<&str>,
744 ) -> Result<GetHeightResult, bsv::wallet::error::WalletError> {
745 Err(bsv::wallet::error::WalletError::NotImplemented(
746 "mock".into(),
747 ))
748 }
749 async fn get_header_for_height(
750 &self,
751 _a: GetHeaderArgs,
752 _o: Option<&str>,
753 ) -> Result<GetHeaderResult, bsv::wallet::error::WalletError> {
754 Err(bsv::wallet::error::WalletError::NotImplemented(
755 "mock".into(),
756 ))
757 }
758 async fn get_network(
759 &self,
760 _o: Option<&str>,
761 ) -> Result<GetNetworkResult, bsv::wallet::error::WalletError> {
762 Err(bsv::wallet::error::WalletError::NotImplemented(
763 "mock".into(),
764 ))
765 }
766 async fn get_version(
767 &self,
768 _o: Option<&str>,
769 ) -> Result<GetVersionResult, bsv::wallet::error::WalletError> {
770 Err(bsv::wallet::error::WalletError::NotImplemented(
771 "mock".into(),
772 ))
773 }
774 async fn create_action(
775 &self,
776 _a: CreateActionArgs,
777 _o: Option<&str>,
778 ) -> Result<CreateActionResult, bsv::wallet::error::WalletError> {
779 Err(bsv::wallet::error::WalletError::NotImplemented(
780 "mock".into(),
781 ))
782 }
783 async fn sign_action(
784 &self,
785 _a: SignActionArgs,
786 _o: Option<&str>,
787 ) -> Result<SignActionResult, bsv::wallet::error::WalletError> {
788 Err(bsv::wallet::error::WalletError::NotImplemented(
789 "mock".into(),
790 ))
791 }
792 async fn abort_action(
793 &self,
794 _a: AbortActionArgs,
795 _o: Option<&str>,
796 ) -> Result<AbortActionResult, bsv::wallet::error::WalletError> {
797 Err(bsv::wallet::error::WalletError::NotImplemented(
798 "mock".into(),
799 ))
800 }
801 async fn list_actions(
802 &self,
803 _a: ListActionsArgs,
804 _o: Option<&str>,
805 ) -> Result<ListActionsResult, bsv::wallet::error::WalletError> {
806 Err(bsv::wallet::error::WalletError::NotImplemented(
807 "mock".into(),
808 ))
809 }
810 async fn internalize_action(
811 &self,
812 _a: InternalizeActionArgs,
813 _o: Option<&str>,
814 ) -> Result<InternalizeActionResult, bsv::wallet::error::WalletError> {
815 Err(bsv::wallet::error::WalletError::NotImplemented(
816 "mock".into(),
817 ))
818 }
819 async fn list_outputs(
820 &self,
821 _a: ListOutputsArgs,
822 _o: Option<&str>,
823 ) -> Result<ListOutputsResult, bsv::wallet::error::WalletError> {
824 Err(bsv::wallet::error::WalletError::NotImplemented(
825 "mock".into(),
826 ))
827 }
828 async fn relinquish_output(
829 &self,
830 _a: RelinquishOutputArgs,
831 _o: Option<&str>,
832 ) -> Result<RelinquishOutputResult, bsv::wallet::error::WalletError> {
833 Err(bsv::wallet::error::WalletError::NotImplemented(
834 "mock".into(),
835 ))
836 }
837 async fn get_public_key(
838 &self,
839 _a: GetPublicKeyArgs,
840 _o: Option<&str>,
841 ) -> Result<GetPublicKeyResult, bsv::wallet::error::WalletError> {
842 Err(bsv::wallet::error::WalletError::NotImplemented(
843 "mock".into(),
844 ))
845 }
846 async fn reveal_counterparty_key_linkage(
847 &self,
848 _a: RevealCounterpartyKeyLinkageArgs,
849 _o: Option<&str>,
850 ) -> Result<RevealCounterpartyKeyLinkageResult, bsv::wallet::error::WalletError> {
851 Err(bsv::wallet::error::WalletError::NotImplemented(
852 "mock".into(),
853 ))
854 }
855 async fn reveal_specific_key_linkage(
856 &self,
857 _a: RevealSpecificKeyLinkageArgs,
858 _o: Option<&str>,
859 ) -> Result<RevealSpecificKeyLinkageResult, bsv::wallet::error::WalletError> {
860 Err(bsv::wallet::error::WalletError::NotImplemented(
861 "mock".into(),
862 ))
863 }
864 async fn encrypt(
865 &self,
866 _a: EncryptArgs,
867 _o: Option<&str>,
868 ) -> Result<EncryptResult, bsv::wallet::error::WalletError> {
869 Err(bsv::wallet::error::WalletError::NotImplemented(
870 "mock".into(),
871 ))
872 }
873 async fn decrypt(
874 &self,
875 _a: DecryptArgs,
876 _o: Option<&str>,
877 ) -> Result<DecryptResult, bsv::wallet::error::WalletError> {
878 Err(bsv::wallet::error::WalletError::NotImplemented(
879 "mock".into(),
880 ))
881 }
882 async fn create_hmac(
883 &self,
884 _a: CreateHmacArgs,
885 _o: Option<&str>,
886 ) -> Result<CreateHmacResult, bsv::wallet::error::WalletError> {
887 Err(bsv::wallet::error::WalletError::NotImplemented(
888 "mock".into(),
889 ))
890 }
891 async fn verify_hmac(
892 &self,
893 _a: VerifyHmacArgs,
894 _o: Option<&str>,
895 ) -> Result<VerifyHmacResult, bsv::wallet::error::WalletError> {
896 Err(bsv::wallet::error::WalletError::NotImplemented(
897 "mock".into(),
898 ))
899 }
900 async fn create_signature(
901 &self,
902 _a: CreateSignatureArgs,
903 _o: Option<&str>,
904 ) -> Result<CreateSignatureResult, bsv::wallet::error::WalletError> {
905 Err(bsv::wallet::error::WalletError::NotImplemented(
906 "mock".into(),
907 ))
908 }
909 async fn verify_signature(
910 &self,
911 _a: VerifySignatureArgs,
912 _o: Option<&str>,
913 ) -> Result<VerifySignatureResult, bsv::wallet::error::WalletError> {
914 Err(bsv::wallet::error::WalletError::NotImplemented(
915 "mock".into(),
916 ))
917 }
918 async fn acquire_certificate(
919 &self,
920 _a: AcquireCertificateArgs,
921 _o: Option<&str>,
922 ) -> Result<Certificate, bsv::wallet::error::WalletError> {
923 Err(bsv::wallet::error::WalletError::NotImplemented(
924 "mock".into(),
925 ))
926 }
927 async fn list_certificates(
928 &self,
929 _a: ListCertificatesArgs,
930 _o: Option<&str>,
931 ) -> Result<ListCertificatesResult, bsv::wallet::error::WalletError> {
932 Err(bsv::wallet::error::WalletError::NotImplemented(
933 "mock".into(),
934 ))
935 }
936 async fn prove_certificate(
937 &self,
938 _a: ProveCertificateArgs,
939 _o: Option<&str>,
940 ) -> Result<ProveCertificateResult, bsv::wallet::error::WalletError> {
941 Err(bsv::wallet::error::WalletError::NotImplemented(
942 "mock".into(),
943 ))
944 }
945 async fn relinquish_certificate(
946 &self,
947 _a: RelinquishCertificateArgs,
948 _o: Option<&str>,
949 ) -> Result<RelinquishCertificateResult, bsv::wallet::error::WalletError> {
950 Err(bsv::wallet::error::WalletError::NotImplemented(
951 "mock".into(),
952 ))
953 }
954 async fn discover_by_identity_key(
955 &self,
956 _a: DiscoverByIdentityKeyArgs,
957 _o: Option<&str>,
958 ) -> Result<DiscoverCertificatesResult, bsv::wallet::error::WalletError> {
959 Err(bsv::wallet::error::WalletError::NotImplemented(
960 "mock".into(),
961 ))
962 }
963 async fn discover_by_attributes(
964 &self,
965 _a: DiscoverByAttributesArgs,
966 _o: Option<&str>,
967 ) -> Result<DiscoverCertificatesResult, bsv::wallet::error::WalletError> {
968 Err(bsv::wallet::error::WalletError::NotImplemented(
969 "mock".into(),
970 ))
971 }
972 }
973
974 fn make_builder() -> WalletBuilderFn {
975 Box::new(|_root_key, _pkm| {
976 Box::pin(async move {
977 let mock: Arc<dyn WalletInterface + Send + Sync> = Arc::new(MockWallet);
978 Ok(mock)
979 })
980 })
981 }
982
983 #[tokio::test]
984 async fn test_auth_manager_not_active_before_auth() {
985 let wab = WABClient::new("http://localhost:3000");
986 let mgr = WalletAuthenticationManager::new(
987 wab,
988 make_builder(),
989 "admin.example.com".to_string(),
990 None,
991 )
992 .unwrap();
993
994 let result = mgr
996 .create_action(
997 CreateActionArgs {
998 description: "test action".to_string(),
999 inputs: vec![],
1000 outputs: vec![],
1001 labels: vec![],
1002 lock_time: None,
1003 version: None,
1004 options: None,
1005 input_beef: None,
1006 reference: None,
1007 },
1008 Some("test.com"),
1009 )
1010 .await;
1011
1012 assert!(result.is_err());
1013 let err = result.unwrap_err();
1014 let err_str = err.to_string();
1015 assert!(
1016 err_str.contains("not authenticated") || err_str.contains("NOT_ACTIVE"),
1017 "Expected NOT_ACTIVE error, got: {}",
1018 err_str
1019 );
1020 }
1021
1022 #[tokio::test]
1023 async fn test_is_authenticated_before_and_after() {
1024 let wab = WABClient::new("http://localhost:3000");
1025 let mgr = WalletAuthenticationManager::new(
1026 wab,
1027 make_builder(),
1028 "admin.example.com".to_string(),
1029 None,
1030 )
1031 .unwrap();
1032
1033 let auth = mgr.is_authenticated(None).await.unwrap();
1035 assert!(!auth.authenticated, "Should not be authenticated initially");
1036
1037 {
1039 let mock: Arc<dyn WalletInterface + Send + Sync> = Arc::new(MockWallet);
1040 let mut inner = mgr.inner.write().await;
1041 *inner = Some(mock);
1042 }
1043
1044 let auth = mgr.is_authenticated(None).await.unwrap();
1046 assert!(
1047 auth.authenticated,
1048 "Should be authenticated after setting inner"
1049 );
1050 }
1051}