1uniffi::setup_scaffolding!();
2
3#[derive(uniffi::Error, thiserror::Error, Debug)]
4pub enum Error {
5 #[error("There is already a node for node_id={0}, maybe you want to recover?")]
6 DuplicateNode(String),
7
8 #[error("There is no node with node_id={0}, maybe you need to register first?")]
9 NoSuchNode(String),
10
11 #[error("The provided credentials could not be parsed, please recover.")]
12 UnparseableCreds(),
13
14 #[error("The passphrase you provided fails the checksum")]
15 PhraseCorrupted(),
16
17 #[error("Error calling the rpc: {0}")]
18 Rpc(String),
19
20 #[error("Invalid argument: {0}={1}")]
21 Argument(String, String),
22
23 #[error("Generic error: {0}")]
24 Other(String),
25}
26
27mod config;
28mod credentials;
29mod input;
30mod node;
31mod scheduler;
32mod signer;
33mod util;
34
35pub use crate::{
36 config::Config,
37 credentials::{Credentials, DeveloperCert},
38 node::{
39 ChannelState, FundChannel, FundOutput, GetInfoResponse, Invoice, InvoicePaidEvent,
40 InvoiceStatus, ListFundsResponse, ListIndex, ListInvoicesResponse,
41 ListPaymentsRequest, ListPeerChannelsResponse, ListPaysResponse, ListPeersResponse,
42 Node, NodeEvent, NodeEventStream, OnchainReceiveResponse, OnchainSendResponse,
43 OutputStatus, Pay, PayStatus, Payment, PaymentStatus, PaymentType, PaymentTypeFilter,
44 Peer, PeerChannel, ReceiveResponse, SendResponse,
45 },
46 input::{InputType, ParsedInvoice},
47 scheduler::Scheduler,
48 signer::{Handle, Signer},
49};
50
51enum SchedulerAction {
53 Register { invite_code: Option<String> },
54 Recover,
55}
56
57fn schedule_node(
59 seed: Vec<u8>,
60 config: &config::Config,
61 action: SchedulerAction,
62) -> Result<std::sync::Arc<node::Node>, Error> {
63 use std::sync::Arc;
64
65 let network = config.network;
66 let nobody = config.nobody();
67
68 let seed_for_async = seed.clone();
69 let credentials = util::exec(async move {
70 let signer =
71 gl_client::signer::Signer::new(seed_for_async, network, nobody.clone())
72 .map_err(|e| Error::Other(e.to_string()))?;
73
74 let scheduler = gl_client::scheduler::Scheduler::new(network, nobody)
75 .await
76 .map_err(|e| Error::Other(e.to_string()))?;
77
78 let node_id_hex = hex::encode(signer.node_id());
79
80 let creds_bytes = match action {
81 SchedulerAction::Register { invite_code } => {
82 scheduler
83 .register(&signer, invite_code)
84 .await
85 .map_err(|e| map_scheduler_error(e, &node_id_hex))?
86 .creds
87 }
88 SchedulerAction::Recover => {
89 scheduler
90 .recover(&signer)
91 .await
92 .map_err(|e| map_scheduler_error(e, &node_id_hex))?
93 .creds
94 }
95 };
96
97 credentials::Credentials::load(creds_bytes)
98 })?;
99
100 let authenticated_signer =
101 gl_client::signer::Signer::new(seed, network, credentials.inner.clone())
102 .map_err(|e| Error::Other(e.to_string()))?;
103
104 let handle = signer::Handle::spawn(authenticated_signer);
105 let node = node::Node::with_signer(credentials, handle)?;
106 Ok(Arc::new(node))
107}
108
109fn map_scheduler_error(e: anyhow::Error, node_id_hex: &str) -> Error {
112 for cause in e.chain() {
114 if let Some(status) = cause.downcast_ref::<tonic::Status>() {
115 match status.code() {
116 tonic::Code::AlreadyExists => {
117 return Error::DuplicateNode(node_id_hex.to_string())
118 }
119 tonic::Code::NotFound => return Error::NoSuchNode(node_id_hex.to_string()),
120 _ => {}
124 }
125 }
126 }
127
128 let msg = e.to_string();
130 if msg.contains("NOT_FOUND")
131 || msg.contains("no rows returned")
132 || msg.contains("Recovery failed")
133 {
134 Error::NoSuchNode(node_id_hex.to_string())
135 } else if msg.contains("ALREADY_EXISTS") {
136 Error::DuplicateNode(node_id_hex.to_string())
137 } else {
138 Error::Other(msg)
139 }
140}
141
142fn parse_mnemonic(mnemonic: &str) -> Result<Vec<u8>, Error> {
144 use bip39::Mnemonic;
145 use std::str::FromStr;
146 let phrase = Mnemonic::from_str(mnemonic).map_err(|_e| Error::PhraseCorrupted())?;
147 Ok(phrase.to_seed_normalized("").to_vec())
148}
149
150#[uniffi::export]
155pub fn register(
156 mnemonic: String,
157 invite_code: Option<String>,
158 config: &config::Config,
159) -> Result<std::sync::Arc<node::Node>, Error> {
160 let seed = parse_mnemonic(&mnemonic)?;
161 schedule_node(seed, config, SchedulerAction::Register { invite_code })
162}
163
164#[uniffi::export]
169pub fn recover(
170 mnemonic: String,
171 config: &config::Config,
172) -> Result<std::sync::Arc<node::Node>, Error> {
173 let seed = parse_mnemonic(&mnemonic)?;
174 schedule_node(seed, config, SchedulerAction::Recover)
175}
176
177#[uniffi::export]
179pub fn connect(
180 mnemonic: String,
181 credentials: Vec<u8>,
182 config: &config::Config,
183) -> Result<std::sync::Arc<node::Node>, Error> {
184 use std::sync::Arc;
185
186 let seed = parse_mnemonic(&mnemonic)?;
187 let network = config.network;
188 let creds = credentials::Credentials::load(credentials)?;
189
190 let authenticated_signer =
191 gl_client::signer::Signer::new(seed, network, creds.inner.clone())
192 .map_err(|e| Error::Other(e.to_string()))?;
193
194 let handle = signer::Handle::spawn(authenticated_signer);
195 let node = node::Node::with_signer(creds, handle)?;
196 Ok(Arc::new(node))
197}
198
199#[uniffi::export]
201pub fn register_or_recover(
202 mnemonic: String,
203 invite_code: Option<String>,
204 config: &config::Config,
205) -> Result<std::sync::Arc<node::Node>, Error> {
206 match recover(mnemonic.clone(), config) {
207 Ok(node) => Ok(node),
208 Err(Error::NoSuchNode(_)) => register(mnemonic, invite_code, config),
209 Err(e) => Err(e),
210 }
211}
212
213#[uniffi::export]
218pub fn parse_input(input: String) -> Result<input::InputType, Error> {
219 input::parse_input(input)
220}
221
222#[derive(uniffi::Enum, Debug)]
223pub enum Network {
224 BITCOIN,
225 REGTEST,
226}
227
228impl From<Network> for gl_client::bitcoin::Network {
229 fn from(n: Network) -> gl_client::bitcoin::Network {
230 match n {
231 Network::BITCOIN => gl_client::bitcoin::Network::Bitcoin,
232 Network::REGTEST => gl_client::bitcoin::Network::Regtest,
233 }
234 }
235}