lwk/
amp0.rs

1use std::fmt::Display;
2use std::sync::{Arc, Mutex};
3
4use lwk_common::Amp0Signer;
5
6// use crate::{LwkError, Pset, WolletDescriptor};
7use crate::{AddressResult, LwkError, Network, Pset, Signer, Transaction, WolletDescriptor};
8
9/// Context for actions related to an AMP0 (sub)account
10#[derive(uniffi::Object)]
11pub struct Amp0 {
12    inner: Mutex<lwk_wollet::amp0::blocking::Amp0>,
13}
14
15#[uniffi::export]
16impl Amp0 {
17    /// Construct an AMP0 context
18    #[uniffi::constructor]
19    pub fn new(
20        network: &Network,
21        username: &str,
22        password: &str,
23        amp_id: &str,
24    ) -> Result<Self, LwkError> {
25        let inner =
26            lwk_wollet::amp0::blocking::Amp0::new(network.into(), username, password, amp_id)?;
27        Ok(Self {
28            inner: Mutex::new(inner),
29        })
30    }
31
32    /// Index of the last returned address
33    pub fn last_index(&self) -> Result<u32, LwkError> {
34        Ok(self.inner.lock()?.last_index())
35    }
36
37    /// AMP ID
38    pub fn amp_id(&self) -> Result<String, LwkError> {
39        Ok(self.inner.lock()?.amp_id().into())
40    }
41
42    /// Wollet descriptor
43    pub fn wollet_descriptor(&self) -> Result<Arc<WolletDescriptor>, LwkError> {
44        Ok(Arc::new(self.inner.lock()?.wollet_descriptor().into()))
45    }
46
47    /// Get an address
48    ///
49    /// If `index` is None, a new address is returned.
50    pub fn address(&self, index: Option<u32>) -> Result<Arc<AddressResult>, LwkError> {
51        Ok(Arc::new(self.inner.lock()?.address(index)?.into()))
52    }
53
54    /// Ask AMP0 server to cosign
55    pub fn sign(&self, pset: &Amp0Pset) -> Result<Arc<Transaction>, LwkError> {
56        Ok(Arc::new(self.inner.lock()?.sign(pset.as_ref())?.into()))
57    }
58}
59
60/// A PSET to use with AMP0
61#[derive(uniffi::Object)]
62pub struct Amp0Pset {
63    inner: lwk_wollet::amp0::Amp0Pset,
64}
65
66impl From<lwk_wollet::amp0::Amp0Pset> for Amp0Pset {
67    fn from(inner: lwk_wollet::amp0::Amp0Pset) -> Self {
68        Self { inner }
69    }
70}
71
72impl AsRef<lwk_wollet::amp0::Amp0Pset> for Amp0Pset {
73    fn as_ref(&self) -> &lwk_wollet::amp0::Amp0Pset {
74        &self.inner
75    }
76}
77
78#[uniffi::export]
79impl Amp0Pset {
80    /// Construct a PSET to use with AMP0
81    #[uniffi::constructor]
82    pub fn new(pset: &Pset, blinding_nonces: Vec<String>) -> Result<Arc<Self>, LwkError> {
83        let pset = pset.as_ref().clone();
84        let inner = lwk_wollet::amp0::Amp0Pset::new(pset, blinding_nonces)?;
85        Ok(Arc::new(Self { inner }))
86    }
87
88    /// Get the PSET
89    pub fn pset(&self) -> Result<Pset, LwkError> {
90        let pset = self.inner.pset().clone();
91        Ok(pset.into())
92    }
93
94    /// Get blinding nonces
95    pub fn blinding_nonces(&self) -> Result<Vec<String>, LwkError> {
96        Ok(self.inner.blinding_nonces().to_vec())
97    }
98}
99
100/// Signer information necessary for full login to AMP0
101#[derive(uniffi::Object, Clone)]
102#[uniffi::export(Display)]
103pub struct Amp0SignerData {
104    inner: lwk_common::Amp0SignerData,
105}
106
107impl Display for Amp0SignerData {
108    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109        write!(f, "{}", self.inner)
110    }
111}
112
113impl From<lwk_common::Amp0SignerData> for Amp0SignerData {
114    fn from(inner: lwk_common::Amp0SignerData) -> Self {
115        Self { inner }
116    }
117}
118
119impl AsRef<lwk_common::Amp0SignerData> for Amp0SignerData {
120    fn as_ref(&self) -> &lwk_common::Amp0SignerData {
121        &self.inner
122    }
123}
124
125#[uniffi::export]
126impl Signer {
127    /// AMP0 signer data for login
128    pub fn amp0_signer_data(&self) -> Result<Amp0SignerData, LwkError> {
129        Ok(self.inner.amp0_signer_data()?.into())
130    }
131
132    /// AMP0 sign login challenge
133    fn amp0_sign_challenge(&self, challenge: &str) -> Result<String, LwkError> {
134        Ok(self.inner.amp0_sign_challenge(challenge)?)
135    }
136
137    /// AMP0 account xpub
138    fn amp0_account_xpub(&self, account: u32) -> Result<String, LwkError> {
139        Ok(self.inner.amp0_account_xpub(account)?.to_string())
140    }
141}
142
143/// Session connecting to AMP0
144#[derive(uniffi::Object)]
145pub struct Amp0Connected {
146    /// Uniffi doesn't allow to accept self and consume the parameter (everything is behind Arc)
147    /// So, inside the Mutex we have an option that allow to consume the inner builder and also
148    /// to emulate the consumption of this object after login.
149    /// (same approach used for TxBuilder)
150    inner: Mutex<Option<lwk_wollet::amp0::blocking::Amp0Connected>>,
151}
152
153/// Session logged in AMP0
154#[derive(uniffi::Object)]
155pub struct Amp0LoggedIn {
156    inner: Mutex<lwk_wollet::amp0::blocking::Amp0LoggedIn>,
157}
158
159fn amp0_err() -> LwkError {
160    "AMP0 session already logged in or it errored".into()
161}
162
163#[uniffi::export]
164impl Amp0Connected {
165    /// Connect and register to AMP0
166    #[uniffi::constructor]
167    pub fn new(network: &Network, signer_data: &Amp0SignerData) -> Result<Self, LwkError> {
168        let inner = lwk_wollet::amp0::blocking::Amp0Connected::new(
169            network.into(),
170            signer_data.inner.clone(),
171        )?;
172        Ok(Amp0Connected {
173            inner: Mutex::new(Some(inner)),
174        })
175    }
176
177    /// Obtain a login challenge
178    ///
179    /// This must be signed with [`Signer::amp0_sign_challenge()`].
180    pub fn get_challenge(&self) -> Result<String, LwkError> {
181        Ok(self
182            .inner
183            .lock()?
184            .as_ref()
185            .ok_or_else(amp0_err)?
186            .get_challenge()?)
187    }
188
189    /// Log in
190    ///
191    /// `sig` must be obtained from [`Signer::amp0_sign_challenge()`] called with the value returned
192    /// by [`Amp0Connected::get_challenge()`]
193    pub fn login(self: Arc<Self>, sig: &str) -> Result<Arc<Amp0LoggedIn>, LwkError> {
194        let mut lock = self.inner.lock()?;
195        let amp0 = lock.take().ok_or_else(amp0_err)?;
196        let amp0 = amp0.login(sig)?;
197        Ok(Arc::new(Amp0LoggedIn {
198            inner: Mutex::new(amp0),
199        }))
200    }
201}
202
203#[uniffi::export]
204impl Amp0LoggedIn {
205    /// List of AMP IDs.
206    pub fn get_amp_ids(&self) -> Result<Vec<String>, LwkError> {
207        Ok(self.inner.lock()?.get_amp_ids()?)
208    }
209
210    /// Get the next account for AMP0 account creation
211    ///
212    /// This must be given to [`Signer::amp0_account_xpub()`] to obtain the xpub to pass to
213    /// [`Amp0LoggedIn::create_amp0_account()`]
214    pub fn next_account(&self) -> Result<u32, LwkError> {
215        Ok(self.inner.lock()?.next_account()?)
216    }
217
218    /// Create a new AMP0 account
219    ///
220    /// `account_xpub` must be obtained from [`Signer::amp0_account_xpub()`] called with the value obtained from
221    /// [`Amp0LoggedIn::next_account()`]
222    pub fn create_amp0_account(
223        &self,
224        pointer: u32,
225        account_xpub: &str,
226    ) -> Result<String, LwkError> {
227        use elements::bitcoin::bip32::Xpub;
228        use std::str::FromStr;
229        let account_xpub = Xpub::from_str(account_xpub)?;
230        Ok(self
231            .inner
232            .lock()?
233            .create_amp0_account(pointer, &account_xpub)?)
234    }
235
236    /// Create a new Watch-Only entry for this wallet
237    pub fn create_watch_only(&self, username: &str, password: &str) -> Result<(), LwkError> {
238        Ok(self.inner.lock()?.create_watch_only(username, password)?)
239    }
240}