Merge #1671: Refac services module

7085542066006d1d0debbe6fff0b3463ace834f1 Move lianalite to services::connect module (edouardparis)
f3ae84f91c2028d8e53da0d096867c8cf13a54c4 Move keys service to its own submodule (edouardparis)

Pull request description:

  Move the current client implementation for https://keys.wizardsardine.com from `services` to `services::keys`.
  Move the liana-connect client implementation to `services::connect`

ACKs for top commit:
  jp1ac4:
    ACK 7085542066006d1d0debbe6fff0b3463ace834f1.

Tree-SHA512: 853f8cde9de768f0b087125b7ae9a5aae3fec9a5d6085ccf0ae72e6b75c7833424ddf482bcc9eefeea05f35ef59b65727af8f6e0af1efcf0a95217f3d9b74b2c
This commit is contained in:
edouardparis 2025-04-24 14:13:16 +02:00
commit b2f28b94d1
No known key found for this signature in database
GPG Key ID: E65F7A089C20DC8F
23 changed files with 132 additions and 128 deletions

View File

@ -12,8 +12,7 @@ use serde::{Deserialize, Serialize};
use crate::{
backup::{Key, KeyRole, KeyType},
hw::HardwareWalletConfig,
lianalite::client::backend,
services,
services::{self, connect::client::backend},
};
pub const DEFAULT_FILE_NAME: &str = "settings.json";
@ -143,8 +142,8 @@ impl From<backend::api::Provider> for Provider {
}
}
impl From<services::api::Provider> for Provider {
fn from(provider: services::api::Provider) -> Self {
impl From<services::keys::api::Provider> for Provider {
fn from(provider: services::keys::api::Provider) -> Self {
Self {
uuid: provider.uuid,
name: provider.name,

View File

@ -26,7 +26,7 @@ use crate::{
extract_daemon_config, extract_local_gui_settings, extract_remote_gui_settings, Context,
RemoteBackend,
},
lianalite::client::backend::api::DEFAULT_LIMIT,
services::connect::client::backend::api::DEFAULT_LIMIT,
VERSION,
};

View File

@ -44,8 +44,8 @@ use crate::{
model::{HistoryTransaction, Labelled},
Daemon, DaemonBackend, DaemonError,
},
lianalite::client::backend::api::DEFAULT_LIMIT,
node::bitcoind::Bitcoind,
services::connect::client::backend::api::DEFAULT_LIMIT,
};
const DUMP_LABELS_LIMIT: u32 = 100;

View File

@ -6,8 +6,8 @@ use std::time::Duration;
use crate::{
app::settings::KeySetting,
backup::Backup,
lianalite::client::backend::{BackendClient, BackendWalletClient},
node::bitcoind::{Bitcoind, InternalBitcoindConfig},
services::connect::client::backend::{BackendClient, BackendWalletClient},
signer::Signer,
};
use async_hwi::DeviceKind;

View File

@ -2,7 +2,7 @@ use async_hwi::{DeviceKind, Version};
use liana::miniscript::{bitcoin::bip32::Fingerprint, descriptor::DescriptorPublicKey};
use crate::{
app::settings::ProviderKey, hw::is_compatible_with_tapminiscript, services::api::KeyKind,
app::settings::ProviderKey, hw::is_compatible_with_tapminiscript, services::keys::api::KeyKind,
};
/// Whether to enable cosigner keys on all paths (excluding safety net paths).

View File

@ -15,12 +15,14 @@ use crate::{
export::ImportExportMessage,
hw::HardwareWalletMessage,
installer::descriptor::{Key, PathKind},
lianalite::client::{auth::AuthClient, backend::api},
node::{
bitcoind::{Bitcoind, ConfigField, RpcAuthType},
electrum, NodeType,
},
services,
services::{
self,
connect::client::{auth::AuthClient, backend::api},
},
};
#[derive(Debug, Clone)]
@ -52,7 +54,7 @@ pub enum Message {
MnemonicWord(usize, String),
ImportMnemonic(bool),
RedeemNextKey,
KeyRedeemed(ProviderKey, Result<(), services::Error>),
KeyRedeemed(ProviderKey, Result<(), services::keys::Error>),
AllKeysRedeemed,
BackupWallet,
ExportWallet(Result<String, backup::Error>),
@ -176,7 +178,7 @@ pub enum ImportKeyModal {
NameEdited(String),
ManuallyImportXpub,
ConfirmXpub,
UseToken(services::api::KeyKind),
UseToken(services::keys::api::KeyKind),
TokenEdited(String),
ConfirmToken,
SelectKey(usize),

View File

@ -29,14 +29,16 @@ use crate::{
daemon::DaemonError,
datadir::create_directory,
hw::{HardwareWalletConfig, HardwareWallets},
lianalite::client::{
auth::AuthError,
backend::{
api::payload::{Provider, ProviderKey},
BackendClient, BackendWalletClient,
services::{
self,
connect::client::{
auth::AuthError,
backend::{
api::payload::{Provider, ProviderKey},
BackendClient, BackendWalletClient,
},
},
},
services,
signer::Signer,
};
@ -701,7 +703,7 @@ pub enum Error {
// DaemonError does not implement Clone.
// TODO: maybe Arc is overkill
Backend(Arc<DaemonError>),
Services(services::Error),
Services(services::keys::Error),
Settings(SettingsError),
Bitcoind(String),
Electrum(String),

View File

@ -14,7 +14,7 @@ use crate::{
step::Step,
view, Error,
},
lianalite::client::{
services::connect::client::{
self,
auth::{AuthClient, AuthError},
backend::{api, BackendClient},

View File

@ -406,7 +406,7 @@ impl super::DescriptorEditModal for EditXpubModal {
message::ImportKeyModal::ConfirmToken => {
// We have checked that the token has not already been fetched and saved.
let token = self.form_token.value.clone();
let client = services::Client::new();
let client = services::keys::Client::new();
return Task::perform(
async move { (token.clone(), client.get_key_by_token(token).await) },
|(token, res)| {

View File

@ -30,7 +30,7 @@ use crate::{
step::{Context, Step},
view,
},
services::api::KeyKind,
services::keys::api::KeyKind,
signer::Signer,
};

View File

@ -68,7 +68,7 @@ pub struct Final {
internal_bitcoind: Option<Bitcoind>,
warning: Option<String>,
config_path: Option<PathBuf>,
key_redemptions: HashMap<ProviderKey, Option<Result<(), services::Error>>>,
key_redemptions: HashMap<ProviderKey, Option<Result<(), services::keys::Error>>>,
}
impl Final {
@ -109,7 +109,7 @@ impl Step for Final {
match message {
Message::RedeemNextKey => {
if let Some((pk, _)) = self.key_redemptions.iter().find(|(_, v)| v.is_none()) {
let client = services::Client::new();
let client = services::keys::Client::new();
let pk = pk.clone();
return Task::perform(
async move { (pk.clone(), client.redeem_key(pk.uuid, pk.token).await) },

View File

@ -265,7 +265,7 @@ fn maybe_key_from_token<'a>(
has_chosen_signer: bool,
form_token: &form::Value<String>,
form_token_warning: Option<&'a String>,
key_kind: services::api::KeyKind,
key_kind: services::keys::api::KeyKind,
) -> Option<Element<'a, Message>> {
if !path_kind.can_choose_key_source_kind(&KeySourceKind::Token(key_kind)) {
None
@ -448,8 +448,8 @@ pub fn edit_key_modal<'a>(
))
}
)
.push_maybe(maybe_key_from_token(path_kind, form_key_source_kind, chosen_signer.is_some(), form_token, form_token_warning, services::api::KeyKind::SafetyNet))
.push_maybe(maybe_key_from_token(path_kind, form_key_source_kind, chosen_signer.is_some(), form_token, form_token_warning, services::api::KeyKind::Cosigner))
.push_maybe(maybe_key_from_token(path_kind, form_key_source_kind, chosen_signer.is_some(), form_token, form_token_warning, services::keys::api::KeyKind::SafetyNet))
.push_maybe(maybe_key_from_token(path_kind, form_key_source_kind, chosen_signer.is_some(), form_token, form_token_warning, services::keys::api::KeyKind::Cosigner))
.width(Length::Fill),
)
.push_maybe(

View File

@ -7,7 +7,6 @@ pub mod export;
pub mod hw;
pub mod installer;
pub mod launcher;
pub mod lianalite;
pub mod loader;
pub mod logger;
pub mod node;

View File

@ -28,12 +28,12 @@ use liana_gui::{
hw::HardwareWalletConfig,
installer::{self, Installer},
launcher::{self, Launcher},
lianalite::{
loader::{self, Loader},
logger::Logger,
services::connect::{
client::backend::{api, BackendWalletClient},
login,
},
loader::{self, Loader},
logger::Logger,
VERSION,
};

View File

@ -0,0 +1,97 @@
pub mod api;
use reqwest::{self, IntoUrl, Method, RequestBuilder};
use serde_json::json;
const KEYS_API_URL: &str = "https://keys.wizardsardine.com";
#[derive(Debug, Clone)]
pub enum Error {
Http(Option<u16>, String),
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::Http(kind, e) => write!(f, "Http error: [{:?}] {}", kind, e),
}
}
}
impl From<reqwest::Error> for Error {
fn from(error: reqwest::Error) -> Self {
Self::Http(None, error.to_string())
}
}
async fn check_response_status(response: reqwest::Response) -> Result<reqwest::Response, Error> {
if !response.status().is_success() {
return Err(Error::Http(
Some(response.status().into()),
response.text().await?,
));
}
Ok(response)
}
fn request<U: reqwest::IntoUrl>(
http: &reqwest::Client,
method: reqwest::Method,
url: U,
) -> reqwest::RequestBuilder {
let req = http
.request(method, url)
.header("Content-Type", "application/json")
.header("API-Version", "0.1");
tracing::debug!("Sending http request: {:?}", req);
req
}
#[derive(Debug, Clone)]
pub struct Client(reqwest::Client);
impl Default for Client {
fn default() -> Self {
Self::new()
}
}
impl Client {
pub fn new() -> Self {
let http = reqwest::Client::new();
Client(http)
}
async fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
request(&self.0, method, url)
}
pub async fn get_key_by_token(&self, token: String) -> Result<api::Key, Error> {
let response = self
.request(Method::GET, &format!("{}/v1/keys", KEYS_API_URL))
.await
.query(&[("token", token)])
.send()
.await?;
let response = check_response_status(response).await?;
let key = response.json().await?;
Ok(key)
}
pub async fn redeem_key(&self, uuid: String, token: String) -> Result<api::Key, Error> {
let response = self
.request(
Method::POST,
&format!("{}/v1/keys/{}/redeem", KEYS_API_URL, uuid),
)
.await
.json(&json!({
"token": token,
}))
.send()
.await?;
let response = check_response_status(response).await?;
let key = response.json().await?;
Ok(key)
}
}

View File

@ -1,97 +1,2 @@
pub mod api;
use reqwest::{self, IntoUrl, Method, RequestBuilder};
use serde_json::json;
const KEYS_API_URL: &str = "https://keys.wizardsardine.com";
#[derive(Debug, Clone)]
pub enum Error {
Http(Option<u16>, String),
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::Http(kind, e) => write!(f, "Http error: [{:?}] {}", kind, e),
}
}
}
impl From<reqwest::Error> for Error {
fn from(error: reqwest::Error) -> Self {
Self::Http(None, error.to_string())
}
}
async fn check_response_status(response: reqwest::Response) -> Result<reqwest::Response, Error> {
if !response.status().is_success() {
return Err(Error::Http(
Some(response.status().into()),
response.text().await?,
));
}
Ok(response)
}
fn request<U: reqwest::IntoUrl>(
http: &reqwest::Client,
method: reqwest::Method,
url: U,
) -> reqwest::RequestBuilder {
let req = http
.request(method, url)
.header("Content-Type", "application/json")
.header("API-Version", "0.1");
tracing::debug!("Sending http request: {:?}", req);
req
}
#[derive(Debug, Clone)]
pub struct Client(reqwest::Client);
impl Default for Client {
fn default() -> Self {
Self::new()
}
}
impl Client {
pub fn new() -> Self {
let http = reqwest::Client::new();
Client(http)
}
async fn request<U: IntoUrl>(&self, method: Method, url: U) -> RequestBuilder {
request(&self.0, method, url)
}
pub async fn get_key_by_token(&self, token: String) -> Result<api::Key, Error> {
let response = self
.request(Method::GET, &format!("{}/v1/keys", KEYS_API_URL))
.await
.query(&[("token", token)])
.send()
.await?;
let response = check_response_status(response).await?;
let key = response.json().await?;
Ok(key)
}
pub async fn redeem_key(&self, uuid: String, token: String) -> Result<api::Key, Error> {
let response = self
.request(
Method::POST,
&format!("{}/v1/keys/{}/redeem", KEYS_API_URL, uuid),
)
.await
.json(&json!({
"token": token,
}))
.send()
.await?;
let response = check_response_status(response).await?;
let key = response.json().await?;
Ok(key)
}
}
pub mod connect;
pub mod keys;