WASM: prevent filesystem access (#792)

* WASM: prevent filesystem access

* Exclude logger module on wasm

* Drop use of conditional compilation in `LiquidOnchainWallet`

* Expose `LiquidSdkBuilder` and configure build for wasm

* Move working_dir setup and log header to connect with signer

* Add in memory persister constructor

* Drop builder connect method

* Remove `empty_wallet_cache` from WASM interface

* Impose custom persister on wasm
This commit is contained in:
Daniel Granhão
2025-03-24 13:31:22 +00:00
committed by GitHub
parent 2952b6133e
commit a06c6d522e
13 changed files with 277 additions and 128 deletions

2
lib/Cargo.lock generated
View File

@@ -812,6 +812,7 @@ dependencies = [
"env_logger 0.11.5",
"flutter_rust_bridge",
"futures-util",
"getrandom 0.2.14",
"glob",
"hex",
"lazy_static",
@@ -823,6 +824,7 @@ dependencies = [
"mockall",
"paste",
"prost 0.13.4",
"rand 0.8.5",
"reqwest 0.12.13",
"rusqlite",
"rusqlite_migration",

View File

@@ -85,11 +85,16 @@ uuid = { version = "1.8.0", features = ["v4", "js"] }
[dev-dependencies]
sdk-common = { workspace = true, features = ["test-utils"] }
paste = "1.0.15"
# Non-WASM dev dependencies
[target.'cfg(not(all(target_family = "wasm", target_os = "unknown")))'.dev-dependencies]
tempdir = "0.3.7"
# WASM dev dependencies
[target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dev-dependencies]
wasm-bindgen-test = "0.3.33"
rand = "0.8"
getrandom = { version = "0.2", features = ["js"] }
[build-dependencies]
anyhow = { version = "1.0.79", features = ["backtrace"] }

View File

@@ -174,6 +174,7 @@ pub(crate) mod event;
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
pub(crate) mod frb_generated;
pub(crate) mod lnurl;
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
pub mod logger;
pub mod model;
pub mod persist;
@@ -186,7 +187,7 @@ pub(crate) mod swapper;
pub(crate) mod sync;
pub(crate) mod test_utils;
pub(crate) mod utils;
pub(crate) mod wallet;
pub mod wallet;
pub use sdk_common::prelude::*;

View File

@@ -189,6 +189,10 @@ impl Config {
LiquidNetwork::Regtest => BOLTZ_REGTEST,
}
}
pub fn sync_enabled(&self) -> bool {
self.sync_service_url.is_some()
}
}
/// Network chosen for this Liquid SDK instance. Note that it represents both the Liquid and the
@@ -821,7 +825,7 @@ pub(crate) trait BlockListener: Send + Sync {
// A swap enum variant
#[derive(Clone, Debug)]
pub(crate) enum Swap {
pub enum Swap {
Chain(ChainSwap),
Send(SendSwap),
Receive(ReceiveSwap),
@@ -946,7 +950,7 @@ pub(crate) struct SwapMetadata {
/// See <https://docs.boltz.exchange/v/api/lifecycle#chain-swaps>
#[derive(Clone, Debug, Derivative)]
#[derivative(PartialEq)]
pub(crate) struct ChainSwap {
pub struct ChainSwap {
pub(crate) id: String,
pub(crate) direction: Direction,
/// The Bitcoin claim address is only set for Outgoing Chain Swaps
@@ -1113,7 +1117,7 @@ pub(crate) struct ChainSwapUpdate {
/// A submarine swap, used for Send
#[derive(Clone, Debug, Derivative)]
#[derivative(PartialEq)]
pub(crate) struct SendSwap {
pub struct SendSwap {
pub(crate) id: String,
/// Bolt11 or Bolt12 invoice. This is determined by whether `bolt12_offer` is set or not.
pub(crate) invoice: String,
@@ -1206,7 +1210,7 @@ impl SendSwap {
/// A reverse swap, used for Receive
#[derive(Clone, Debug, Derivative)]
#[derivative(PartialEq)]
pub(crate) struct ReceiveSwap {
pub struct ReceiveSwap {
pub(crate) id: String,
pub(crate) preimage: String,
/// JSON representation of [crate::persist::receive::InternalCreateReverseResponse]

View File

@@ -44,10 +44,7 @@ mod tests {
test_utils::persist::{create_persister, new_receive_swap, new_send_swap},
};
#[cfg(all(target_family = "wasm", target_os = "unknown"))]
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
#[sdk_macros::test_all]
#[sdk_macros::test_not_wasm]
fn test_backup_and_restore() -> Result<()> {
create_persister!(local);

View File

@@ -11,7 +11,7 @@ pub(crate) mod sync;
use std::collections::{HashMap, HashSet};
use std::ops::Not;
use std::{fs::create_dir_all, path::PathBuf, str::FromStr};
use std::{path::PathBuf, str::FromStr};
use crate::lightning_invoice::{Bolt11Invoice, Bolt11InvoiceDescription};
use crate::model::*;
@@ -32,7 +32,7 @@ use tokio::sync::broadcast::{self, Sender};
const DEFAULT_DB_FILENAME: &str = "storage.sql";
pub(crate) struct Persister {
pub struct Persister {
main_db_dir: PathBuf,
network: LiquidNetwork,
pub(crate) sync_trigger: Option<Sender<()>>,
@@ -60,21 +60,58 @@ fn where_clauses_to_string(where_clauses: Vec<String>) -> String {
}
impl Persister {
pub fn new(working_dir: &str, network: LiquidNetwork, sync_enabled: bool) -> Result<Self> {
/// Creates a new Persister that stores data on the provided `working_dir`.
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
pub fn new_using_fs(
working_dir: &str,
network: LiquidNetwork,
sync_enabled: bool,
asset_metadata: Option<Vec<AssetMetadata>>,
) -> Result<Self> {
let main_db_dir = PathBuf::from_str(working_dir)?;
if !main_db_dir.exists() {
create_dir_all(&main_db_dir)?;
std::fs::create_dir_all(&main_db_dir)?;
}
Self::new_inner(main_db_dir, network, sync_enabled, asset_metadata)
}
/// Creates a new Persister that only keeps data in memory.
///
/// Multiple persisters accessing the same in-memory data can be created by providing the
/// same `database_id`.
#[cfg(all(target_family = "wasm", target_os = "unknown"))]
pub fn new_in_memory(
database_id: &str,
network: LiquidNetwork,
sync_enabled: bool,
asset_metadata: Option<Vec<AssetMetadata>>,
) -> Result<Self> {
let main_db_dir = PathBuf::from_str(database_id)?;
Self::new_inner(main_db_dir, network, sync_enabled, asset_metadata)
}
fn new_inner(
main_db_dir: PathBuf,
network: LiquidNetwork,
sync_enabled: bool,
asset_metadata: Option<Vec<AssetMetadata>>,
) -> Result<Self> {
let mut sync_trigger = None;
if sync_enabled {
let (events_notifier, _) = broadcast::channel::<()>(1);
sync_trigger = Some(events_notifier);
}
Ok(Persister {
let persister = Persister {
main_db_dir,
network,
sync_trigger,
})
};
persister.init()?;
persister.replace_asset_metadata(asset_metadata)?;
Ok(persister)
}
pub(crate) fn get_connection(&self) -> Result<Connection> {

View File

@@ -27,7 +27,7 @@ use crate::{
const LIQUID_TIP_LEEWAY: u32 = 3;
pub(crate) struct Recoverer {
pub struct Recoverer {
master_blinding_key: MasterBlindingKey,
swapper: Arc<dyn Swapper>,
onchain_wallet: Arc<dyn OnchainWallet>,

View File

@@ -1,6 +1,6 @@
use std::collections::{BTreeMap, HashMap, HashSet};
use std::ops::Not as _;
use std::{fs, path::PathBuf, str::FromStr, sync::Arc, time::Duration};
use std::{path::PathBuf, str::FromStr, sync::Arc, time::Duration};
use anyhow::{anyhow, ensure, Result};
use boltz_client::{swaps::boltz::*, util::secrets::Preimage};
@@ -72,7 +72,7 @@ pub const DEFAULT_EXTERNAL_INPUT_PARSERS: &[(&str, &str, &str)] = &[(
pub(crate) const NETWORK_PROPAGATION_GRACE_PERIOD: Duration = Duration::from_secs(30);
pub(crate) struct LiquidSdkBuilder {
pub struct LiquidSdkBuilder {
config: Config,
signer: Arc<Box<dyn Signer>>,
breez_server: Arc<BreezServer>,
@@ -162,17 +162,20 @@ impl LiquidSdkBuilder {
self
}
fn get_working_dir(&self) -> Result<String> {
let fingerprint_hex: String =
Xpub::decode(self.signer.xpub()?.as_slice())?.identifier()[0..4].to_hex();
self.config
.get_wallet_dir(&self.config.working_dir, &fingerprint_hex)
}
pub fn build(&self) -> Result<Arc<LiquidSdk>> {
if let Some(breez_api_key) = &self.config.breez_api_key {
LiquidSdk::validate_breez_api_key(breez_api_key)?
}
fs::create_dir_all(&self.config.working_dir)?;
let fingerprint_hex: String =
Xpub::decode(self.signer.xpub()?.as_slice())?.identifier()[0..4].to_hex();
let working_dir = self
.config
.get_wallet_dir(&self.config.working_dir, &fingerprint_hex)?;
let cache_dir = self.config.get_wallet_dir(
self.config
.cache_dir
@@ -181,24 +184,20 @@ impl LiquidSdkBuilder {
&fingerprint_hex,
)?;
let sync_enabled = self
.config
.sync_service_url
.clone()
.map(|_| true)
.unwrap_or(false);
let persister = match self.persister.clone() {
Some(persister) => persister,
None => {
let persister = Arc::new(Persister::new(
&working_dir,
#[cfg(all(target_family = "wasm", target_os = "unknown"))]
return Err(anyhow!(
"Must provide a WASM-compatible persister on WASM builds"
));
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
Arc::new(Persister::new_using_fs(
&self.get_working_dir()?,
self.config.network,
sync_enabled,
)?);
persister.init()?;
persister.replace_asset_metadata(self.config.asset_metadata.clone())?;
persister
self.config.sync_enabled(),
self.config.asset_metadata.clone(),
)?)
}
};
@@ -226,7 +225,7 @@ impl LiquidSdkBuilder {
Some(onchain_wallet) => onchain_wallet,
None => Arc::new(LiquidOnchainWallet::new(
self.config.clone(),
&cache_dir,
cache_dir,
persister.clone(),
self.signer.clone(),
)?),
@@ -383,43 +382,49 @@ impl LiquidSdk {
/// * `passphrase` - the optional passphrase for the mnemonic
/// * `seed` - the optional Liquid wallet seed
pub async fn connect(req: ConnectRequest) -> Result<Arc<LiquidSdk>> {
let start_ts = Instant::now();
let is_mainnet = req.config.network == LiquidNetwork::Mainnet;
let signer = Self::default_signer(&req)?;
let signer = match (req.mnemonic, req.seed) {
(None, Some(seed)) => Box::new(SdkSigner::new_with_seed(seed, is_mainnet)?),
(Some(mnemonic), None) => Box::new(SdkSigner::new(
&mnemonic,
req.passphrase.unwrap_or("".to_string()).as_ref(),
Self::connect_with_signer(
ConnectWithSignerRequest { config: req.config },
Box::new(signer),
)
.inspect_err(|e| error!("Failed to connect: {:?}", e))
.await
}
pub fn default_signer(req: &ConnectRequest) -> Result<SdkSigner> {
let is_mainnet = req.config.network == LiquidNetwork::Mainnet;
match (&req.mnemonic, &req.seed) {
(None, Some(seed)) => Ok(SdkSigner::new_with_seed(seed.clone(), is_mainnet)?),
(Some(mnemonic), None) => Ok(SdkSigner::new(
mnemonic,
req.passphrase.as_ref().unwrap_or(&"".to_string()).as_ref(),
is_mainnet,
)?),
_ => return Err(anyhow!("Either `mnemonic` or `seed` must be set")),
};
let sdk =
Self::connect_with_signer(ConnectWithSignerRequest { config: req.config }, signer)
.inspect_err(|e| error!("Failed to connect: {:?}", e))
.await;
let init_time = Instant::now().duration_since(start_ts);
utils::log_print_header(init_time);
sdk
_ => Err(anyhow!("Either `mnemonic` or `seed` must be set")),
}
}
pub async fn connect_with_signer(
req: ConnectWithSignerRequest,
signer: Box<dyn Signer>,
) -> Result<Arc<LiquidSdk>> {
let start_ts = Instant::now();
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
std::fs::create_dir_all(&req.config.working_dir)?;
let sdk = LiquidSdkBuilder::new(
req.config,
PRODUCTION_BREEZSERVER_URL.into(),
Arc::new(signer),
)?
.build()?;
sdk.start()
.inspect_err(|e| error!("Failed to start an SDK instance: {:?}", e))
.await?;
sdk.start().await?;
let init_time = Instant::now().duration_since(start_ts);
utils::log_print_header(init_time);
Ok(sdk)
}
@@ -450,9 +455,8 @@ impl LiquidSdk {
/// Starts an SDK instance.
///
/// Internal method. Should only be called once per instance.
/// Should only be called as part of [LiquidSdk::connect].
async fn start(self: &Arc<LiquidSdk>) -> SdkResult<()> {
/// Should only be called once per instance.
pub async fn start(self: &Arc<LiquidSdk>) -> SdkResult<()> {
let mut is_started = self.is_started.write().await;
self.persister
.update_send_swaps_by_state(Created, TimedOut, Some(true))
@@ -3305,13 +3309,14 @@ impl LiquidSdk {
}
/// Empties the Liquid Wallet cache for the [Config::network].
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
pub fn empty_wallet_cache(&self) -> Result<()> {
let mut path = PathBuf::from(self.config.working_dir.clone());
path.push(Into::<ElementsNetwork>::into(self.config.network).as_str());
path.push("enc_cache");
fs::remove_dir_all(&path)?;
fs::create_dir_all(path)?;
std::fs::remove_dir_all(&path)?;
std::fs::create_dir_all(path)?;
Ok(())
}
@@ -3827,6 +3832,7 @@ impl LiquidSdk {
/// An error is thrown if the log file cannot be created in the working directory.
///
/// An error is thrown if a global logger is already configured.
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
pub fn init_logging(log_dir: &str, app_logger: Option<Box<dyn log::Log>>) -> Result<()> {
crate::logger::init_logging(log_dir, app_logger)
}

View File

@@ -39,7 +39,7 @@ pub(crate) struct SyncCompletedData {
pub(crate) pushed_records_count: u32,
}
pub(crate) struct SyncService {
pub struct SyncService {
remote_url: String,
client_id: String,
persister: Arc<Persister>,

View File

@@ -123,16 +123,38 @@ pub(crate) fn new_receive_swap(
macro_rules! create_persister {
($name:ident) => {
let temp_dir = tempdir::TempDir::new("liquid-sdk")?;
let $name = std::sync::Arc::new(crate::persist::Persister::new(
temp_dir
#[cfg(all(target_family = "wasm", target_os = "unknown"))]
let $name = {
let db_id = {
use rand::Rng;
let res: String = rand::thread_rng()
.sample_iter(&rand::distributions::Alphanumeric)
.take(16)
.map(char::from)
.collect();
res
};
std::sync::Arc::new(crate::persist::Persister::new_in_memory(
&db_id,
crate::model::LiquidNetwork::Testnet,
true,
None,
)?)
};
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
let $name = {
let temp_dir_path = tempdir::TempDir::new("liquid-sdk")?
.path()
.to_str()
.ok_or(anyhow::anyhow!("Could not create temporary directory"))?,
crate::model::LiquidNetwork::Testnet,
true,
)?);
$name.init()?;
.ok_or(anyhow::anyhow!("Could not create temporary directory"))?
.to_string();
std::sync::Arc::new(crate::persist::Persister::new_using_fs(
&temp_dir_path,
crate::model::LiquidNetwork::Testnet,
true,
None,
)?)
};
};
}
pub(crate) use create_persister;

View File

@@ -1,8 +1,6 @@
use std::collections::HashMap;
use std::fs::{self, create_dir_all};
use std::io::Write;
use std::path::PathBuf;
use std::{path::Path, str::FromStr, sync::Arc};
use std::{str::FromStr, sync::Arc};
use anyhow::{anyhow, Result};
use boltz_client::ElementsAddress;
@@ -10,10 +8,10 @@ use log::{debug, info, warn};
use lwk_common::Signer as LwkSigner;
use lwk_common::{singlesig_desc, Singlesig};
use lwk_wollet::elements::{AssetId, Txid};
use lwk_wollet::ElectrumOptions;
use lwk_wollet::{
elements::{hex::ToHex, Address, Transaction},
ElectrumClient, ElectrumUrl, ElementsNetwork, FsPersister, WalletTx, Wollet, WolletDescriptor,
ElectrumClient, ElectrumOptions, ElectrumUrl, ElementsNetwork, FsPersister, NoPersist,
WalletTx, Wollet, WolletDescriptor,
};
use sdk_common::bitcoin::hashes::{sha256, Hash};
use sdk_common::bitcoin::secp256k1::PublicKey;
@@ -99,28 +97,29 @@ pub trait OnchainWallet: Send + Sync {
async fn full_scan(&self) -> Result<(), PaymentError>;
}
pub(crate) struct LiquidOnchainWallet {
pub struct LiquidOnchainWallet {
config: Config,
persister: Arc<Persister>,
wallet: Arc<Mutex<Wollet>>,
electrum_client: Mutex<Option<ElectrumClient>>,
working_dir: String,
working_dir: Option<String>,
pub(crate) signer: SdkLwkSigner,
}
impl LiquidOnchainWallet {
/// Creates a new LiquidOnchainWallet that caches data on the provided `working_dir`.
pub(crate) fn new(
config: Config,
working_dir: &String,
working_dir: String,
persister: Arc<Persister>,
user_signer: Arc<Box<dyn Signer>>,
) -> Result<Self> {
let signer = crate::signer::SdkLwkSigner::new(user_signer.clone())?;
let wollet = Self::create_wallet(&config, working_dir, &signer)?;
let signer = SdkLwkSigner::new(user_signer.clone())?;
let wollet = Self::create_wallet(&config, Some(&working_dir), &signer)?;
let working_dir_buf = PathBuf::from_str(working_dir)?;
let working_dir_buf = std::path::PathBuf::from_str(&working_dir)?;
if !working_dir_buf.exists() {
create_dir_all(&working_dir_buf)?;
std::fs::create_dir_all(&working_dir_buf)?;
}
Ok(Self {
@@ -128,40 +127,70 @@ impl LiquidOnchainWallet {
persister,
wallet: Arc::new(Mutex::new(wollet)),
electrum_client: Mutex::new(None),
working_dir: working_dir.clone(),
working_dir: Some(working_dir),
signer,
})
}
fn create_wallet<P: AsRef<Path>>(
/// Creates a new LiquidOnchainWallet that caches data in memory
pub fn new_in_memory(
config: Config,
persister: Arc<Persister>,
user_signer: Arc<Box<dyn Signer>>,
) -> Result<Self> {
let signer = SdkLwkSigner::new(user_signer.clone())?;
let wollet = Self::create_wallet(&config, None, &signer)?;
Ok(Self {
config,
persister,
wallet: Arc::new(Mutex::new(wollet)),
electrum_client: Mutex::new(None),
working_dir: None,
signer,
})
}
fn create_wallet(
config: &Config,
working_dir: P,
working_dir: Option<&str>,
signer: &SdkLwkSigner,
) -> Result<Wollet> {
let elements_network: ElementsNetwork = config.network.into();
let descriptor = LiquidOnchainWallet::get_descriptor(signer, config.network)?;
let mut lwk_persister =
FsPersister::new(working_dir.as_ref(), elements_network, &descriptor)?;
let wollet_res = Wollet::new(elements_network, lwk_persister, descriptor.clone());
let wollet_res = match &working_dir {
Some(working_dir) => Wollet::new(
elements_network,
FsPersister::new(working_dir, elements_network, &descriptor)?,
descriptor.clone(),
),
None => Wollet::new(elements_network, NoPersist::new(), descriptor.clone()),
};
match wollet_res {
Ok(wollet) => Ok(wollet),
Err(
res @ Err(
lwk_wollet::Error::PersistError(_)
| lwk_wollet::Error::UpdateHeightTooOld { .. }
| lwk_wollet::Error::UpdateOnDifferentStatus { .. },
) => {
warn!("Update error initialising wollet, wipping storage and retrying: {wollet_res:?}");
let mut path = working_dir.as_ref().to_path_buf();
path.push(elements_network.as_str());
fs::remove_dir_all(&path)?;
warn!("Wiping wallet in path: {:?}", path);
lwk_persister = FsPersister::new(working_dir, elements_network, &descriptor)?;
Ok(Wollet::new(
elements_network,
lwk_persister,
descriptor.clone(),
)?)
}
) => match working_dir {
Some(working_dir) => {
warn!(
"Update error initialising wollet, wipping storage and retrying: {res:?}"
);
let mut path = std::path::PathBuf::from(working_dir);
path.push(elements_network.as_str());
std::fs::remove_dir_all(&path)?;
warn!("Wiping wallet in path: {:?}", path);
let lwk_persister =
FsPersister::new(working_dir, elements_network, &descriptor)?;
Ok(Wollet::new(
elements_network,
lwk_persister,
descriptor.clone(),
)?)
}
None => res.map_err(Into::into),
},
Err(e) => Err(e.into()),
}
}
@@ -397,7 +426,7 @@ impl OnchainWallet for LiquidOnchainWallet {
Err(lwk_wollet::Error::UpdateHeightTooOld { .. }) => {
warn!("Full scan failed with update height too old, wiping storage and retrying");
let mut new_wallet =
Self::create_wallet(&self.config, &self.working_dir, &self.signer)?;
Self::create_wallet(&self.config, self.working_dir.as_deref(), &self.signer)?;
lwk_wollet::full_scan_to_index_with_electrum_client(
&mut new_wallet,
index_with_buffer,
@@ -443,7 +472,6 @@ mod tests {
use crate::test_utils::persist::create_persister;
use crate::wallet::LiquidOnchainWallet;
use anyhow::Result;
use tempdir::TempDir;
#[cfg(all(target_family = "wasm", target_os = "unknown"))]
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
@@ -456,15 +484,27 @@ mod tests {
let config = Config::testnet(None);
// Create a temporary directory for working_dir
let temp_dir = TempDir::new("").unwrap();
let working_dir = temp_dir.path().to_str().unwrap().to_string();
create_persister!(storage);
let wallet: Arc<dyn OnchainWallet> = Arc::new(
LiquidOnchainWallet::new(config, &working_dir, storage, sdk_signer.clone()).unwrap(),
);
let wallet: Arc<dyn OnchainWallet> =
if cfg!(not(all(target_family = "wasm", target_os = "unknown"))) {
// Create a temporary directory for working_dir
let working_dir = tempdir::TempDir::new("")
.unwrap()
.path()
.to_str()
.unwrap()
.to_string();
Arc::new(
LiquidOnchainWallet::new(config, working_dir, storage, sdk_signer.clone())
.unwrap(),
)
} else {
Arc::new(
LiquidOnchainWallet::new_in_memory(config, storage, sdk_signer.clone())
.unwrap(),
)
};
// Test message
let message = "Hello, Liquid!";

View File

@@ -6,15 +6,17 @@ mod signer;
use std::str::FromStr;
use std::sync::Arc;
use crate::event::{EventListener, WasmEventListener};
use crate::model::*;
use anyhow::anyhow;
use breez_sdk_liquid::sdk::LiquidSdk;
use breez_sdk_liquid::persist::Persister;
use breez_sdk_liquid::sdk::{LiquidSdk, LiquidSdkBuilder};
use breez_sdk_liquid::wallet::LiquidOnchainWallet;
use breez_sdk_liquid::PRODUCTION_BREEZSERVER_URL;
use log::Level;
use signer::{Signer, WasmSigner};
use wasm_bindgen::prelude::*;
use crate::event::{EventListener, WasmEventListener};
use crate::model::*;
#[wasm_bindgen]
pub struct BindingLiquidSdk {
sdk: Arc<LiquidSdk>,
@@ -22,8 +24,8 @@ pub struct BindingLiquidSdk {
#[wasm_bindgen(js_name = "connect")]
pub async fn connect(req: ConnectRequest) -> WasmResult<BindingLiquidSdk> {
let sdk = LiquidSdk::connect(req.into()).await?;
Ok(BindingLiquidSdk { sdk })
let signer = Box::new(LiquidSdk::default_signer(&req.clone().into())?);
connect_inner(req.config, signer).await
}
#[wasm_bindgen(js_name = "connectWithSigner")]
@@ -31,8 +33,42 @@ pub async fn connect_with_signer(
req: ConnectWithSignerRequest,
signer: Signer,
) -> WasmResult<BindingLiquidSdk> {
let wasm_signer = Box::new(WasmSigner { signer });
let sdk = LiquidSdk::connect_with_signer(req.into(), wasm_signer).await?;
let signer: Box<dyn breez_sdk_liquid::model::Signer> = Box::new(WasmSigner { signer });
connect_inner(req.config, signer).await
}
async fn connect_inner(
config: Config,
signer: Box<dyn breez_sdk_liquid::model::Signer>,
) -> WasmResult<BindingLiquidSdk> {
let config: breez_sdk_liquid::model::Config = config.into();
let signer = Arc::new(signer);
let mut sdk_builder = LiquidSdkBuilder::new(
config.clone(),
PRODUCTION_BREEZSERVER_URL.to_string(),
Arc::clone(&signer),
)?;
let persister = Arc::new(Persister::new_in_memory(
&config.working_dir,
config.network,
config.sync_enabled(),
config.asset_metadata.clone(),
)?);
let onchain_wallet = Arc::new(LiquidOnchainWallet::new_in_memory(
config,
Arc::clone(&persister),
signer,
)?);
sdk_builder.persister(persister);
sdk_builder.onchain_wallet(onchain_wallet);
let sdk = sdk_builder.build()?;
sdk.start().await?;
Ok(BindingLiquidSdk { sdk })
}
@@ -294,12 +330,6 @@ impl BindingLiquidSdk {
Ok(self.sdk.recommended_fees().await?.into())
}
#[wasm_bindgen(js_name = "emptyWalletCache")]
pub fn empty_wallet_cache(&self) -> WasmResult<()> {
self.sdk.empty_wallet_cache()?;
Ok(())
}
#[wasm_bindgen(js_name = "backup")]
pub fn backup(&self, req: BackupRequest) -> WasmResult<()> {
self.sdk.backup(req.into())?;

View File

@@ -10,6 +10,7 @@ pub enum Network {
Regtest,
}
#[derive(Clone)]
#[sdk_macros::extern_wasm_bindgen(breez_sdk_liquid::prelude::ExternalInputParser)]
pub struct ExternalInputParser {
pub provider_id: String,
@@ -292,6 +293,7 @@ pub struct Symbol {
pub position: Option<u32>,
}
#[derive(Clone)]
#[sdk_macros::extern_wasm_bindgen(breez_sdk_liquid::prelude::Config)]
pub struct Config {
pub liquid_electrum_url: String,
@@ -310,6 +312,7 @@ pub struct Config {
pub asset_metadata: Option<Vec<AssetMetadata>>,
}
#[derive(Clone)]
#[sdk_macros::extern_wasm_bindgen(breez_sdk_liquid::prelude::LiquidNetwork)]
pub enum LiquidNetwork {
Mainnet,
@@ -330,6 +333,7 @@ pub enum SdkEvent {
Synced,
}
#[derive(Clone)]
#[sdk_macros::extern_wasm_bindgen(breez_sdk_liquid::prelude::ConnectRequest)]
pub struct ConnectRequest {
pub config: Config,
@@ -639,6 +643,7 @@ pub struct LnUrlInfo {
pub lnurl_withdraw_endpoint: Option<String>,
}
#[derive(Clone)]
#[sdk_macros::extern_wasm_bindgen(breez_sdk_liquid::prelude::AssetMetadata)]
pub struct AssetMetadata {
pub asset_id: String,