bp_util/
opts.rs

1// Modern, minimalistic & standard-compliant cold wallet library.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Written in 2020-2023 by
6//     Dr Maxim Orlovsky <orlovsky@lnp-bp.org>
7//
8// Copyright (C) 2020-2023 LNP/BP Standards Association. All rights reserved.
9// Copyright (C) 2020-2023 Dr Maxim Orlovsky. All rights reserved.
10//
11// Licensed under the Apache License, Version 2.0 (the "License");
12// you may not use this file except in compliance with the License.
13// You may obtain a copy of the License at
14//
15//     http://www.apache.org/licenses/LICENSE-2.0
16//
17// Unless required by applicable law or agreed to in writing, software
18// distributed under the License is distributed on an "AS IS" BASIS,
19// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20// See the License for the specific language governing permissions and
21// limitations under the License.
22
23use std::fmt::Debug;
24use std::path::{Path, PathBuf};
25
26use bpstd::{Network, XpubDerivable};
27use clap::ValueHint;
28use descriptors::{Descriptor, StdDescr, TrKey, Wpkh};
29use strict_encoding::Ident;
30
31pub const DATA_DIR_ENV: &str = "LNPBP_DATA_DIR";
32#[cfg(any(target_os = "linux"))]
33pub const DATA_DIR: &str = "~/.lnp-bp";
34#[cfg(any(target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))]
35pub const DATA_DIR: &str = "~/.lnp-bp";
36#[cfg(target_os = "macos")]
37pub const DATA_DIR: &str = "~/Library/Application Support/LNP-BP Suite";
38#[cfg(target_os = "windows")]
39pub const DATA_DIR: &str = "~\\AppData\\Local\\LNP-BP Suite";
40#[cfg(target_os = "ios")]
41pub const DATA_DIR: &str = "~/Documents";
42#[cfg(target_os = "android")]
43pub const DATA_DIR: &str = ".";
44
45pub const DEFAULT_ELECTRUM: &str = "example.com:50001";
46pub const DEFAULT_ESPLORA: &str = "https://blockstream.info/testnet/api";
47
48#[derive(Args, Clone, PartialEq, Eq, Debug)]
49pub struct ResolverOpt {
50    /// Electrum server to use.
51    #[arg(
52        conflicts_with = "esplora",
53        long,
54        global = true,
55        default_value = DEFAULT_ELECTRUM,
56        env = "ELECRTUM_SERVER",
57        value_hint = ValueHint::Url,
58        value_name = "URL"
59    )]
60    pub electrum: String,
61
62    /// Esplora server to use.
63    #[arg(
64        conflicts_with = "electrum",
65        long,
66        global = true,
67        default_value = DEFAULT_ESPLORA,
68        env = "ESPLORA_SERVER",
69        value_hint = ValueHint::Url,
70        value_name = "URL"
71    )]
72    pub esplora: String,
73
74    #[clap(long, global = true)]
75    pub sync: bool,
76}
77
78pub trait DescriptorOpts: clap::Args + Clone + Eq + Debug {
79    type Descr: Descriptor + serde::Serialize + for<'de> serde::Deserialize<'de>;
80    fn is_some(&self) -> bool;
81    fn descriptor(&self) -> Option<Self::Descr>;
82}
83
84#[derive(Args, Clone, PartialEq, Eq, Debug)]
85#[group(multiple = false)]
86pub struct DescrStdOpts {
87    /// Use wpkh(KEY) descriptor as wallet
88    #[arg(long, global = true)]
89    pub wpkh: Option<XpubDerivable>,
90
91    /// Use tr(KEY) descriptor as wallet
92    #[arg(long, global = true)]
93    pub tr_key_only: Option<XpubDerivable>,
94}
95
96impl DescriptorOpts for DescrStdOpts {
97    type Descr = StdDescr;
98
99    fn is_some(&self) -> bool { self.tr_key_only.is_some() | self.wpkh.is_some() }
100    fn descriptor(&self) -> Option<Self::Descr> {
101        if let Some(ref x) = self.tr_key_only {
102            Some(TrKey::from(x.clone()).into())
103        } else if let Some(ref x) = self.wpkh {
104            Some(Wpkh::from(x.clone()).into())
105        } else {
106            None
107        }
108    }
109}
110
111#[derive(Args, Clone, PartialEq, Eq, Debug)]
112#[group(multiple = false)]
113pub struct WalletOpts<O: DescriptorOpts = DescrStdOpts> {
114    #[arg(short = 'w', long = "wallet", global = true)]
115    pub name: Option<Ident>,
116
117    /// Path to wallet directory.
118    #[arg(
119        short = 'W',
120        long,
121        global = true,
122        value_hint = ValueHint::DirPath,
123    )]
124    pub wallet_path: Option<PathBuf>,
125
126    #[clap(flatten)]
127    pub descriptor_opts: O,
128}
129
130#[derive(Args, Clone, PartialEq, Eq, Debug)]
131pub struct GeneralOpts {
132    /// Data directory path.
133    ///
134    /// Path to the directory that contains RGB stored data.
135    #[arg(
136        short,
137        long,
138        global = true,
139        default_value = DATA_DIR,
140        env = DATA_DIR_ENV,
141        value_hint = ValueHint::DirPath
142    )]
143    pub data_dir: PathBuf,
144
145    /// Network to use.
146    #[arg(short, long, global = true, default_value = "testnet", env = "LNPBP_NETWORK")]
147    pub network: Network,
148}
149
150impl GeneralOpts {
151    pub fn process(&mut self) {
152        self.data_dir =
153            PathBuf::from(shellexpand::tilde(&self.data_dir.display().to_string()).to_string());
154    }
155
156    pub fn base_dir(&self) -> PathBuf {
157        let mut dir = self.data_dir.clone();
158        dir.push(self.network.to_string());
159        dir
160    }
161
162    pub fn wallet_dir(&self, wallet_name: impl AsRef<Path>) -> PathBuf {
163        let mut dir = self.base_dir();
164        dir.push(wallet_name);
165        dir
166    }
167}