From a3c8809a83539cbd7e49bd3200ae5dd1e1d49856 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Granh=C3=A3o?= Date: Wed, 19 Nov 2025 12:14:49 +0000 Subject: [PATCH 1/2] Account-aware storage dir and rtsync signer --- crates/breez-sdk/cli/src/main.rs | 27 +++-- crates/breez-sdk/core/src/lib.rs | 1 + crates/breez-sdk/core/src/models/mod.rs | 23 +++- crates/breez-sdk/core/src/persist/path.rs | 31 ++--- .../breez-sdk/core/src/realtime_sync/init.rs | 5 +- .../core/src/realtime_sync/signer.rs | 14 ++- crates/breez-sdk/core/src/sdk_builder.rs | 107 ++++++++---------- crates/breez-sdk/wasm/src/models/mod.rs | 1 + crates/breez-sdk/wasm/src/sdk_builder.rs | 66 +++++++++-- crates/spark/src/signer/default_signer.rs | 61 +++++----- 10 files changed, 198 insertions(+), 138 deletions(-) diff --git a/crates/breez-sdk/cli/src/main.rs b/crates/breez-sdk/cli/src/main.rs index 6b39653f4..5a9d24bf6 100644 --- a/crates/breez-sdk/cli/src/main.rs +++ b/crates/breez-sdk/cli/src/main.rs @@ -5,7 +5,7 @@ use crate::command::CliHelper; use crate::persist::CliPersistence; use anyhow::{Result, anyhow}; use breez_sdk_spark::{ - ConnectRequest, EventListener, Network, SdkEvent, Seed, connect, default_config, + EventListener, KeySetType, Network, SdkBuilder, SdkEvent, Seed, default_config, }; use clap::Parser; use command::{Command, execute_command}; @@ -26,6 +26,10 @@ struct Cli { /// Network to use (mainnet, regtest) #[arg(long, default_value = "regtest")] network: String, + + /// Account number to use for the Spark signer + #[arg(long)] + account_number: Option, } fn expand_path(path: &str) -> PathBuf { @@ -72,7 +76,11 @@ impl EventListener for CliEventListener { } } -async fn run_interactive_mode(data_dir: PathBuf, network: Network) -> Result<()> { +async fn run_interactive_mode( + data_dir: PathBuf, + network: Network, + account_number: Option, +) -> Result<()> { breez_sdk_spark::init_logging(Some(data_dir.to_string_lossy().into()), None, None)?; let persistence = CliPersistence { data_dir: data_dir.clone(), @@ -101,12 +109,13 @@ async fn run_interactive_mode(data_dir: PathBuf, network: Network) -> Result<()> passphrase: None, }; - let sdk = connect(ConnectRequest { - config, - seed, - storage_dir: data_dir.to_string_lossy().to_string(), - }) - .await?; + let mut sdk_builder = + SdkBuilder::new(config, seed).with_default_storage(data_dir.to_string_lossy().to_string()); + if let Some(account_number) = account_number { + sdk_builder = sdk_builder.with_key_set(KeySetType::Default, false, Some(account_number)); + } + + let sdk = sdk_builder.build().await?; let listener = Box::new(CliEventListener {}); sdk.add_event_listener(listener).await; @@ -187,7 +196,7 @@ async fn main() -> Result<(), anyhow::Error> { _ => return Err(anyhow!("Invalid network. Use 'regtest' or 'mainnet'")), }; - Box::pin(run_interactive_mode(data_dir, network)).await?; + Box::pin(run_interactive_mode(data_dir, network, cli.account_number)).await?; Ok(()) } diff --git a/crates/breez-sdk/core/src/lib.rs b/crates/breez-sdk/core/src/lib.rs index cb7e88ab3..4fb36fc8e 100644 --- a/crates/breez-sdk/core/src/lib.rs +++ b/crates/breez-sdk/core/src/lib.rs @@ -29,6 +29,7 @@ pub use persist::{ }; pub use sdk::{BreezSdk, default_config, init_logging, parse_input}; pub use sdk_builder::SdkBuilder; +pub use spark_wallet::KeySet; #[cfg(not(all(target_family = "wasm", target_os = "unknown")))] pub use {persist::sqlite::SqliteStorage, sdk::connect}; diff --git a/crates/breez-sdk/core/src/models/mod.rs b/crates/breez-sdk/core/src/models/mod.rs index 1186d4052..ffa0251ba 100644 --- a/crates/breez-sdk/core/src/models/mod.rs +++ b/crates/breez-sdk/core/src/models/mod.rs @@ -10,8 +10,8 @@ use std::{collections::HashMap, fmt::Display, str::FromStr}; use crate::{ BitcoinAddressDetails, BitcoinNetwork, Bolt11InvoiceDetails, ExternalInputParser, FiatCurrency, - LnurlPayRequestDetails, LnurlWithdrawRequestDetails, Rate, SparkInvoiceDetails, SuccessAction, - SuccessActionProcessed, error::DepositClaimError, + LnurlPayRequestDetails, LnurlWithdrawRequestDetails, Rate, SdkError, SparkInvoiceDetails, + SuccessAction, SuccessActionProcessed, error::DepositClaimError, }; /// A list of external input parsers that are used by default. @@ -45,6 +45,25 @@ pub enum Seed { Entropy(Vec), } +impl Seed { + pub fn to_bytes(&self) -> Result, SdkError> { + match self { + Seed::Mnemonic { + mnemonic, + passphrase, + } => { + let mnemonic = bip39::Mnemonic::parse(mnemonic) + .map_err(|e| SdkError::Generic(e.to_string()))?; + + Ok(mnemonic + .to_seed(passphrase.as_deref().unwrap_or("")) + .to_vec()) + } + Seed::Entropy(entropy) => Ok(entropy.clone()), + } + } +} + #[cfg_attr(feature = "uniffi", derive(uniffi::Record))] pub struct ConnectRequest { pub config: Config, diff --git a/crates/breez-sdk/core/src/persist/path.rs b/crates/breez-sdk/core/src/persist/path.rs index 74cae723d..58e8886ac 100644 --- a/crates/breez-sdk/core/src/persist/path.rs +++ b/crates/breez-sdk/core/src/persist/path.rs @@ -1,36 +1,21 @@ use std::{path::PathBuf, str::FromStr}; use bitcoin::hashes::{Hash, sha256}; +use spark_wallet::PublicKey; -use crate::{Network, SdkError, Seed}; +use crate::{Network, SdkError}; pub fn default_storage_path( data_dir: &str, network: &Network, - seed: &Seed, + identity_pub_key: &PublicKey, ) -> Result { let storage_dir = std::path::PathBuf::from_str(data_dir)?; - let path_suffix: String = match seed { - crate::Seed::Mnemonic { - mnemonic, - passphrase, - } => { - // Ensure mnemonic is valid before proceeding - bip39::Mnemonic::parse(mnemonic) - .map_err(|e| SdkError::InvalidInput(format!("Invalid mnemonic: {e}")))?; - let str = format!("{mnemonic}:{passphrase:?}"); - sha256::Hash::hash(str.as_bytes()) - .to_string() - .chars() - .take(8) - .collect() - } - crate::Seed::Entropy(vec) => sha256::Hash::hash(vec.as_slice()) - .to_string() - .chars() - .take(8) - .collect(), - }; + let path_suffix = sha256::Hash::hash(&identity_pub_key.serialize()) + .to_string() + .chars() + .take(8) + .collect::(); Ok(storage_dir .join(network.to_string().to_lowercase()) diff --git a/crates/breez-sdk/core/src/realtime_sync/init.rs b/crates/breez-sdk/core/src/realtime_sync/init.rs index 7816c460e..c9b11f626 100644 --- a/crates/breez-sdk/core/src/realtime_sync/init.rs +++ b/crates/breez-sdk/core/src/realtime_sync/init.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use bitcoin::bip32::Xpriv; use breez_sdk_common::sync::{BreezSyncerClient, SigningClient, SyncProcessor, SyncService}; use tracing::debug; use uuid::Uuid; @@ -16,7 +17,7 @@ pub struct RealTimeSyncParams { pub server_url: String, pub api_key: Option, pub network: Network, - pub seed: Vec, + pub identity_master_key: Xpriv, pub storage: Arc, pub sync_storage: Arc, pub shutdown_receiver: tokio::sync::watch::Receiver<()>, @@ -42,7 +43,7 @@ pub async fn init_and_start_real_time_sync( .map_err(|e| SdkError::Generic(e.to_string()))?; let sync_signer = Arc::new( - DefaultSyncSigner::new(¶ms.seed, params.network) + DefaultSyncSigner::new(¶ms.identity_master_key, params.network) .map_err(|e| SdkError::Generic(e.to_string()))?, ); let signing_sync_client = SigningClient::new( diff --git a/crates/breez-sdk/core/src/realtime_sync/signer.rs b/crates/breez-sdk/core/src/realtime_sync/signer.rs index 8a31709e3..c00a02ef0 100644 --- a/crates/breez-sdk/core/src/realtime_sync/signer.rs +++ b/crates/breez-sdk/core/src/realtime_sync/signer.rs @@ -21,10 +21,12 @@ pub struct DefaultSyncSigner { } impl DefaultSyncSigner { - pub fn new(seed: &[u8], network: Network) -> Result { - let bitcoin_network: bitcoin::Network = network.into(); - let xpriv = Xpriv::new_master(bitcoin_network, seed)?; + pub fn new( + identity_master_key: &Xpriv, + network: Network, + ) -> Result { let secp = Secp256k1::new(); + let signing_derivation_path: DerivationPath = match network { Network::Mainnet => SIGNING_DERIVATION_PATH, Network::Regtest => SIGNING_DERIVATION_PATH_TEST, @@ -35,12 +37,14 @@ impl DefaultSyncSigner { Network::Regtest => ENCRYPTION_DERIVATION_PATH_TEST, } .parse()?; - let signing_key = xpriv + + let signing_key = identity_master_key .derive_priv(&secp, &signing_derivation_path)? .private_key; - let encryption_key = xpriv + let encryption_key = identity_master_key .derive_priv(&secp, &encryption_derivation_path)? .private_key; + Ok(Self { signing_key, encryption_key, diff --git a/crates/breez-sdk/core/src/sdk_builder.rs b/crates/breez-sdk/core/src/sdk_builder.rs index d61f92f23..bb24011b6 100644 --- a/crates/breez-sdk/core/src/sdk_builder.rs +++ b/crates/breez-sdk/core/src/sdk_builder.rs @@ -8,7 +8,7 @@ use breez_sdk_common::{ breez_server::{BreezServer, PRODUCTION_BREEZSERVER_URL}, rest::ReqwestRestClient as CommonRequestRestClient, }; -use spark_wallet::{DefaultSigner, Signer}; +use spark_wallet::{DefaultSigner, KeySet, Signer}; use tokio::sync::watch; use tracing::{debug, info}; @@ -187,60 +187,18 @@ impl SdkBuilder { /// Builds the `BreezSdk` instance with the configured components. #[allow(clippy::too_many_lines)] pub async fn build(self) -> Result { - let (storage, sync_storage) = match (self.storage, self.storage_dir) { - // Use provided storages directly - (Some(storage), _) => (storage, self.sync_storage), - // Initialize default storages based on provided directory - #[cfg(not(all(target_family = "wasm", target_os = "unknown")))] - (None, Some(storage_dir)) => { - let storage = default_storage(&storage_dir, self.config.network, &self.seed)?; - let sync_storage = match (self.sync_storage, &self.config.real_time_sync_server_url) - { - // Use provided sync storage directly - (Some(sync_storage), _) => Some(sync_storage), - // Initialize default sync storage based on provided directory - // if real-time sync is enabled - (None, Some(_)) => Some(default_sync_storage( - &storage_dir, - self.config.network, - &self.seed, - )?), - _ => None, - }; - (storage, sync_storage) - } - _ => { - return Err(SdkError::Generic( - "Either storage or storage_dir must be set before building the SDK".to_string(), - )); - } - }; // Create the signer from seed - let seed = match self.seed { - Seed::Mnemonic { - mnemonic, - passphrase, - } => { - let mnemonic = bip39::Mnemonic::parse(&mnemonic) - .map_err(|e| SdkError::Generic(e.to_string()))?; - - mnemonic - .to_seed(passphrase.as_deref().unwrap_or("")) - .to_vec() - } - Seed::Entropy(entropy) => entropy, - }; + let seed_bytes = self.seed.to_bytes()?; + let key_set = KeySet::new( + &seed_bytes, + self.config.network.into(), + self.key_set_type.into(), + self.use_address_index, + self.account_number, + ) + .map_err(|e| SdkError::Generic(e.to_string()))?; + let signer: Arc = Arc::new(DefaultSigner::from_key_set(key_set.clone())); - let signer: Arc = Arc::new( - DefaultSigner::with_keyset_type( - &seed, - self.config.network.into(), - self.key_set_type.into(), - self.use_address_index, - self.account_number, - ) - .map_err(|e| SdkError::Generic(e.to_string()))?, - ); let chain_service = if let Some(service) = self.chain_service { service } else { @@ -275,6 +233,39 @@ impl SdkBuilder { } }; + let (storage, sync_storage) = match (self.storage, self.storage_dir) { + // Use provided storages directly + (Some(storage), _) => (storage, self.sync_storage), + // Initialize default storages based on provided directory + #[cfg(not(all(target_family = "wasm", target_os = "unknown")))] + (None, Some(storage_dir)) => { + let identity_pub_key = signer + .get_identity_public_key() + .map_err(|e| SdkError::Generic(e.to_string()))?; + let storage = + default_storage(&storage_dir, self.config.network, &identity_pub_key)?; + let sync_storage = match (self.sync_storage, &self.config.real_time_sync_server_url) + { + // Use provided sync storage directly + (Some(sync_storage), _) => Some(sync_storage), + // Initialize default sync storage based on provided directory + // if real-time sync is enabled + (None, Some(_)) => Some(default_sync_storage( + &storage_dir, + self.config.network, + &identity_pub_key, + )?), + _ => None, + }; + (storage, sync_storage) + } + _ => { + return Err(SdkError::Generic( + "Either storage or storage_dir must be set before building the SDK".to_string(), + )); + } + }; + let fiat_service: Arc = match self.fiat_service { Some(service) => Arc::new(FiatServiceWrapper::new(service)), None => Arc::new( @@ -339,7 +330,7 @@ impl SdkBuilder { server_url: server_url.clone(), api_key: self.config.api_key.clone(), network: self.config.network, - seed, + identity_master_key: key_set.identity_master_key, storage: Arc::clone(&storage), sync_storage, shutdown_receiver: shutdown_sender.subscribe(), @@ -372,9 +363,9 @@ impl SdkBuilder { fn default_storage( data_dir: &str, network: Network, - seed: &Seed, + identity_pub_key: &spark_wallet::PublicKey, ) -> Result, SdkError> { - let db_path = crate::default_storage_path(data_dir, &network, seed)?; + let db_path = crate::default_storage_path(data_dir, &network, identity_pub_key)?; let storage = Arc::new(crate::SqliteStorage::new(&db_path)?); Ok(storage) } @@ -383,9 +374,9 @@ fn default_storage( fn default_sync_storage( data_dir: &str, network: Network, - seed: &Seed, + identity_pub_key: &spark_wallet::PublicKey, ) -> Result, SdkError> { - let db_path = crate::default_storage_path(data_dir, &network, seed)?; + let db_path = crate::default_storage_path(data_dir, &network, identity_pub_key)?; let storage = Arc::new(crate::SqliteStorage::new(&db_path)?); Ok(storage) } diff --git a/crates/breez-sdk/wasm/src/models/mod.rs b/crates/breez-sdk/wasm/src/models/mod.rs index 2c4ad760e..ffccb23ae 100644 --- a/crates/breez-sdk/wasm/src/models/mod.rs +++ b/crates/breez-sdk/wasm/src/models/mod.rs @@ -80,6 +80,7 @@ pub enum SdkEvent { }, } +#[derive(Clone)] #[macros::extern_wasm_bindgen(breez_sdk_spark::KeySetType)] pub enum KeySetType { Default, diff --git a/crates/breez-sdk/wasm/src/sdk_builder.rs b/crates/breez-sdk/wasm/src/sdk_builder.rs index 0a645029f..c007965b9 100644 --- a/crates/breez-sdk/wasm/src/sdk_builder.rs +++ b/crates/breez-sdk/wasm/src/sdk_builder.rs @@ -1,7 +1,7 @@ use std::{rc::Rc, sync::Arc}; use crate::{ - error::WasmResult, + error::{WasmError, WasmResult}, logger::{Logger, WASM_LOGGER}, models::{ Config, Credentials, KeySetType, Seed, @@ -13,6 +13,8 @@ use crate::{ persist::{Storage, WasmStorage}, sdk::BreezSdk, }; +use bitcoin::secp256k1::PublicKey; +use breez_sdk_spark::KeySet; use wasm_bindgen::prelude::*; #[wasm_bindgen] @@ -20,6 +22,11 @@ pub struct SdkBuilder { builder: breez_sdk_spark::SdkBuilder, network: breez_sdk_spark::Network, seed: breez_sdk_spark::Seed, + default_storage_dir: Option, + storage: Option, + key_set_type: breez_sdk_spark::KeySetType, + use_address_index: bool, + account_number: Option, } #[wasm_bindgen] @@ -33,24 +40,23 @@ impl SdkBuilder { network: config.network, seed: seed.clone(), builder: breez_sdk_spark::SdkBuilder::new(config, seed), + default_storage_dir: None, + storage: None, + key_set_type: breez_sdk_spark::KeySetType::Default, + use_address_index: false, + account_number: None, } } #[wasm_bindgen(js_name = "withDefaultStorage")] pub async fn with_default_storage(mut self, storage_dir: String) -> WasmResult { - let storage = Arc::new(WasmStorage { - storage: default_storage(&storage_dir, &self.network, &self.seed).await?, - }); - self.builder = self.builder.with_storage(storage.clone()); - self.builder = self.builder.with_real_time_sync_storage(storage); + self.default_storage_dir = Some(storage_dir); Ok(self) } #[wasm_bindgen(js_name = "withStorage")] pub fn with_storage(mut self, storage: Storage) -> Self { - let storage_arc = Arc::new(WasmStorage { storage }); - self.builder = self.builder.with_storage(storage_arc.clone()); - self.builder = self.builder.with_real_time_sync_storage(storage_arc); + self.storage = Some(storage); self } @@ -61,6 +67,9 @@ impl SdkBuilder { use_address_index: bool, account_number: Option, ) -> Self { + self.key_set_type = key_set_type.clone().into(); + self.use_address_index = use_address_index; + self.account_number = account_number; self.builder = self.builder .with_key_set(key_set_type.into(), use_address_index, account_number); @@ -117,7 +126,40 @@ impl SdkBuilder { } #[wasm_bindgen(js_name = "build")] - pub async fn build(self) -> WasmResult { + pub async fn build(mut self) -> WasmResult { + match (self.default_storage_dir, self.storage) { + (Some(storage_dir), None) => { + // Create key set to get identity_pub_key for WASM-compatible storage + let key_set = KeySet::new( + &self.seed.to_bytes()?, + self.network.into(), + self.key_set_type.into(), + self.use_address_index, + self.account_number, + ) + .map_err(WasmError::new)?; + + let identity_pub_key = key_set.identity_key_pair.public_key(); + + let storage = Arc::new(WasmStorage { + storage: default_storage(&storage_dir, &self.network, &identity_pub_key) + .await?, + }); + self.builder = self.builder.with_storage(storage.clone()); + self.builder = self.builder.with_real_time_sync_storage(storage); + } + (None, Some(storage)) => { + let storage_arc = Arc::new(WasmStorage { storage }); + self.builder = self.builder.with_storage(storage_arc.clone()); + self.builder = self.builder.with_real_time_sync_storage(storage_arc); + } + _ => { + return Err(WasmError::new( + "One and only one of default storage directory or storage must be set", + )); + } + } + let sdk = self.builder.build().await?; Ok(BreezSdk { sdk: Rc::new(sdk) }) } @@ -126,9 +168,9 @@ impl SdkBuilder { async fn default_storage( data_dir: &str, network: &breez_sdk_spark::Network, - seed: &breez_sdk_spark::Seed, + identity_pub_key: &PublicKey, ) -> WasmResult { - let db_path = breez_sdk_spark::default_storage_path(data_dir, network, seed)?; + let db_path = breez_sdk_spark::default_storage_path(data_dir, network, identity_pub_key)?; // SAFETY: In WASM, thread-local storage is stable and the logger reference // will remain valid for the duration of this async function call. // The WASM environment is single-threaded, so there's no risk of the diff --git a/crates/spark/src/signer/default_signer.rs b/crates/spark/src/signer/default_signer.rs index f1b9c34c7..ac295bd42 100644 --- a/crates/spark/src/signer/default_signer.rs +++ b/crates/spark/src/signer/default_signer.rs @@ -133,6 +133,7 @@ impl DerivedKeySet { } Ok(KeySet { identity_key_pair: identity_master_key.private_key.keypair(&secp), + identity_master_key, encryption_master_key, signing_master_key, static_deposit_master_key, @@ -143,12 +144,38 @@ impl DerivedKeySet { #[derive(Clone)] pub struct KeySet { pub identity_key_pair: Keypair, + pub identity_master_key: Xpriv, pub encryption_master_key: Xpriv, pub signing_master_key: Xpriv, pub static_deposit_master_key: Xpriv, } impl KeySet { + pub fn new( + seed: &[u8], + network: Network, + key_type: KeySetType, + use_address_index: bool, + account_no: Option, + ) -> Result { + let account_number = account_no.unwrap_or_else(|| account_number(network)); + match key_type { + KeySetType::Default => Self::default_keys(seed, network, account_number), + KeySetType::Taproot => { + Self::taproot_keys(seed, network, use_address_index, account_number) + } + KeySetType::NativeSegwit => { + Self::native_segwit_keys(seed, network, use_address_index, account_number) + } + KeySetType::WrappedSegwit => { + Self::wrapped_segwit_keys(seed, network, use_address_index, account_number) + } + KeySetType::Legacy => { + Self::legacy_bitcoin_keys(seed, network, use_address_index, account_number) + } + } + } + fn default_keys( seed: &[u8], network: Network, @@ -271,33 +298,13 @@ impl From for DefaultSignerError { impl DefaultSigner { pub fn new(seed: &[u8], network: Network) -> Result { - Self::with_keyset_type(seed, network, KeySetType::Default, false, None) - } - - pub fn with_keyset_type( - seed: &[u8], - network: Network, - key_type: KeySetType, - use_address_index: bool, - account_no: Option, - ) -> Result { - let account_number = account_no.unwrap_or_else(|| account_number(network)); - let key_set = match key_type { - KeySetType::Default => KeySet::default_keys(seed, network, account_number), - KeySetType::Taproot => { - KeySet::taproot_keys(seed, network, use_address_index, account_number) - } - KeySetType::NativeSegwit => { - KeySet::native_segwit_keys(seed, network, use_address_index, account_number) - } - KeySetType::WrappedSegwit => { - KeySet::wrapped_segwit_keys(seed, network, use_address_index, account_number) - } - KeySetType::Legacy => { - KeySet::legacy_bitcoin_keys(seed, network, use_address_index, account_number) - } - }?; - Ok(Self::from_key_set(key_set)) + Ok(Self::from_key_set(KeySet::new( + seed, + network, + KeySetType::Default, + false, + None, + )?)) } pub fn from_key_set(key_set: KeySet) -> Self { From 22b37314e0ca8ca4335a2b99c538262b884d24b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Granh=C3=A3o?= Date: Wed, 19 Nov 2025 12:34:07 +0000 Subject: [PATCH 2/2] Add rtsync backwards compatibility --- crates/breez-sdk/core/src/realtime_sync/init.rs | 4 ++-- crates/breez-sdk/core/src/realtime_sync/signer.rs | 9 +++------ crates/breez-sdk/core/src/sdk_builder.rs | 14 +++++++++++++- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/crates/breez-sdk/core/src/realtime_sync/init.rs b/crates/breez-sdk/core/src/realtime_sync/init.rs index c9b11f626..5dce5ae4b 100644 --- a/crates/breez-sdk/core/src/realtime_sync/init.rs +++ b/crates/breez-sdk/core/src/realtime_sync/init.rs @@ -17,7 +17,7 @@ pub struct RealTimeSyncParams { pub server_url: String, pub api_key: Option, pub network: Network, - pub identity_master_key: Xpriv, + pub master_key: Xpriv, pub storage: Arc, pub sync_storage: Arc, pub shutdown_receiver: tokio::sync::watch::Receiver<()>, @@ -43,7 +43,7 @@ pub async fn init_and_start_real_time_sync( .map_err(|e| SdkError::Generic(e.to_string()))?; let sync_signer = Arc::new( - DefaultSyncSigner::new(¶ms.identity_master_key, params.network) + DefaultSyncSigner::new(¶ms.master_key, params.network) .map_err(|e| SdkError::Generic(e.to_string()))?, ); let signing_sync_client = SigningClient::new( diff --git a/crates/breez-sdk/core/src/realtime_sync/signer.rs b/crates/breez-sdk/core/src/realtime_sync/signer.rs index c00a02ef0..8500059cb 100644 --- a/crates/breez-sdk/core/src/realtime_sync/signer.rs +++ b/crates/breez-sdk/core/src/realtime_sync/signer.rs @@ -21,10 +21,7 @@ pub struct DefaultSyncSigner { } impl DefaultSyncSigner { - pub fn new( - identity_master_key: &Xpriv, - network: Network, - ) -> Result { + pub fn new(master_key: &Xpriv, network: Network) -> Result { let secp = Secp256k1::new(); let signing_derivation_path: DerivationPath = match network { @@ -38,10 +35,10 @@ impl DefaultSyncSigner { } .parse()?; - let signing_key = identity_master_key + let signing_key = master_key .derive_priv(&secp, &signing_derivation_path)? .private_key; - let encryption_key = identity_master_key + let encryption_key = master_key .derive_priv(&secp, &encryption_derivation_path)? .private_key; diff --git a/crates/breez-sdk/core/src/sdk_builder.rs b/crates/breez-sdk/core/src/sdk_builder.rs index bb24011b6..1d841b87c 100644 --- a/crates/breez-sdk/core/src/sdk_builder.rs +++ b/crates/breez-sdk/core/src/sdk_builder.rs @@ -4,6 +4,7 @@ )] use std::sync::Arc; +use bitcoin::bip32::Xpriv; use breez_sdk_common::{ breez_server::{BreezServer, PRODUCTION_BREEZSERVER_URL}, rest::ReqwestRestClient as CommonRequestRestClient, @@ -326,11 +327,22 @@ impl SdkBuilder { "Real-time sync is enabled, but no sync storage is supplied".to_string(), )); }; + + // Use legacy master key when using default key set and default derivation path for backwards compatibility + let master_key = + if self.key_set_type == KeySetType::Default && self.account_number.is_none() { + let bitcoin_network: bitcoin::Network = self.config.network.into(); + Xpriv::new_master(bitcoin_network, &seed_bytes) + .map_err(|e| SdkError::Generic(e.to_string()))? + } else { + key_set.identity_master_key + }; + init_and_start_real_time_sync(RealTimeSyncParams { server_url: server_url.clone(), api_key: self.config.api_key.clone(), network: self.config.network, - identity_master_key: key_set.identity_master_key, + master_key, storage: Arc::clone(&storage), sync_storage, shutdown_receiver: shutdown_sender.subscribe(),