Skip to content

Commit a25632c

Browse files
committed
Revert to &mut Builder and expose Arced variant for Uniffi
While this generates a bunch of boilerplate, it's probably worth the maintainance effort to have a clean/paradigmatic Rust API. And, as we had previously introduced a `uniffi` feature, we're now able to easily switch out `Builder` exports based on it.
1 parent a1a81db commit a25632c

File tree

4 files changed

+191
-81
lines changed

4 files changed

+191
-81
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use ldk_node::bitcoin::Network;
1818
use std::str::FromStr;
1919

2020
fn main() {
21-
let builder = Builder::new();
21+
let mut builder = Builder::new();
2222
builder.set_network(Network::Testnet);
2323
builder.set_esplora_server("https://blockstream.info/testnet/api".to_string());
2424
builder.set_gossip_source_rgs("https://rapidsync.lightningdevkit.org/testnet/snapshot".to_string());

src/builder.rs

Lines changed: 167 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -74,117 +74,122 @@ enum GossipSourceConfig {
7474
/// - Chain data is sourced from the Esplora endpoint `https://blockstream.info/api`
7575
/// - Gossip data is sourced via the peer-to-peer network
7676
#[derive(Debug)]
77-
pub struct Builder {
78-
config: RwLock<Config>,
79-
entropy_source_config: RwLock<Option<EntropySourceConfig>>,
80-
chain_data_source_config: RwLock<Option<ChainDataSourceConfig>>,
81-
gossip_source_config: RwLock<Option<GossipSourceConfig>>,
77+
pub struct NodeBuilder {
78+
config: Config,
79+
entropy_source_config: Option<EntropySourceConfig>,
80+
chain_data_source_config: Option<ChainDataSourceConfig>,
81+
gossip_source_config: Option<GossipSourceConfig>,
8282
}
8383

84-
impl Builder {
84+
impl NodeBuilder {
8585
/// Creates a new builder instance with the default configuration.
8686
pub fn new() -> Self {
87-
let config = RwLock::new(Config::default());
88-
let entropy_source_config = RwLock::new(None);
89-
let chain_data_source_config = RwLock::new(None);
90-
let gossip_source_config = RwLock::new(None);
87+
let config = Config::default();
88+
let entropy_source_config = None;
89+
let chain_data_source_config = None;
90+
let gossip_source_config = None;
9191
Self { config, entropy_source_config, chain_data_source_config, gossip_source_config }
9292
}
9393

9494
/// Creates a new builder instance from an [`Config`].
9595
pub fn from_config(config: Config) -> Self {
96-
let config = RwLock::new(config);
97-
let entropy_source_config = RwLock::new(None);
98-
let chain_data_source_config = RwLock::new(None);
99-
let gossip_source_config = RwLock::new(None);
96+
let config = config;
97+
let entropy_source_config = None;
98+
let chain_data_source_config = None;
99+
let gossip_source_config = None;
100100
Self { config, entropy_source_config, chain_data_source_config, gossip_source_config }
101101
}
102102

103103
/// Configures the [`Node`] instance to source its wallet entropy from a seed file on disk.
104104
///
105105
/// If the given file does not exist a new random seed file will be generated and
106106
/// stored at the given location.
107-
pub fn set_entropy_seed_path(&self, seed_path: String) {
108-
*self.entropy_source_config.write().unwrap() =
109-
Some(EntropySourceConfig::SeedFile(seed_path));
107+
pub fn set_entropy_seed_path(&mut self, seed_path: String) -> &mut Self {
108+
self.entropy_source_config = Some(EntropySourceConfig::SeedFile(seed_path));
109+
self
110110
}
111111

112112
/// Configures the [`Node`] instance to source its wallet entropy from the given 64 seed bytes.
113113
///
114114
/// **Note:** Panics if the length of the given `seed_bytes` differs from 64.
115-
pub fn set_entropy_seed_bytes(&self, seed_bytes: Vec<u8>) {
115+
pub fn set_entropy_seed_bytes(&mut self, seed_bytes: Vec<u8>) -> &mut Self {
116116
if seed_bytes.len() != WALLET_KEYS_SEED_LEN {
117117
panic!("Failed to set seed due to invalid length.");
118118
}
119119
let mut bytes = [0u8; WALLET_KEYS_SEED_LEN];
120120
bytes.copy_from_slice(&seed_bytes);
121-
*self.entropy_source_config.write().unwrap() = Some(EntropySourceConfig::SeedBytes(bytes));
121+
self.entropy_source_config = Some(EntropySourceConfig::SeedBytes(bytes));
122+
self
122123
}
123124

124125
/// Configures the [`Node`] instance to source its wallet entropy from a [BIP 39] mnemonic.
125126
///
126127
/// [BIP 39]: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki
127-
pub fn set_entropy_bip39_mnemonic(&self, mnemonic: Mnemonic, passphrase: Option<String>) {
128-
*self.entropy_source_config.write().unwrap() =
128+
pub fn set_entropy_bip39_mnemonic(
129+
&mut self, mnemonic: Mnemonic, passphrase: Option<String>,
130+
) -> &mut Self {
131+
self.entropy_source_config =
129132
Some(EntropySourceConfig::Bip39Mnemonic { mnemonic, passphrase });
133+
self
130134
}
131135

132136
/// Configures the [`Node`] instance to source its chain data from the given Esplora server.
133-
pub fn set_esplora_server(&self, esplora_server_url: String) {
134-
*self.chain_data_source_config.write().unwrap() =
135-
Some(ChainDataSourceConfig::Esplora(esplora_server_url));
137+
pub fn set_esplora_server(&mut self, esplora_server_url: String) -> &mut Self {
138+
self.chain_data_source_config = Some(ChainDataSourceConfig::Esplora(esplora_server_url));
139+
self
136140
}
137141

138142
/// Configures the [`Node`] instance to source its gossip data from the Lightning peer-to-peer
139143
/// network.
140-
pub fn set_gossip_source_p2p(&self) {
141-
*self.gossip_source_config.write().unwrap() = Some(GossipSourceConfig::P2PNetwork);
144+
pub fn set_gossip_source_p2p(&mut self) -> &mut Self {
145+
self.gossip_source_config = Some(GossipSourceConfig::P2PNetwork);
146+
self
142147
}
143148

144149
/// Configures the [`Node`] instance to source its gossip data from the given RapidGossipSync
145150
/// server.
146-
pub fn set_gossip_source_rgs(&self, rgs_server_url: String) {
147-
*self.gossip_source_config.write().unwrap() =
148-
Some(GossipSourceConfig::RapidGossipSync(rgs_server_url));
151+
pub fn set_gossip_source_rgs(&mut self, rgs_server_url: String) -> &mut Self {
152+
self.gossip_source_config = Some(GossipSourceConfig::RapidGossipSync(rgs_server_url));
153+
self
149154
}
150155

151156
/// Sets the used storage directory path.
152-
pub fn set_storage_dir_path(&self, storage_dir_path: String) {
153-
let mut config = self.config.write().unwrap();
154-
config.storage_dir_path = storage_dir_path;
157+
pub fn set_storage_dir_path(&mut self, storage_dir_path: String) -> &mut Self {
158+
self.config.storage_dir_path = storage_dir_path;
159+
self
155160
}
156161

157162
/// Sets the Bitcoin network used.
158-
pub fn set_network(&self, network: Network) {
159-
let mut config = self.config.write().unwrap();
160-
config.network = network;
163+
pub fn set_network(&mut self, network: Network) -> &mut Self {
164+
self.config.network = network;
165+
self
161166
}
162167

163168
/// Sets the IP address and TCP port on which [`Node`] will listen for incoming network connections.
164-
pub fn set_listening_address(&self, listening_address: NetAddress) {
165-
let mut config = self.config.write().unwrap();
166-
config.listening_address = Some(listening_address);
169+
pub fn set_listening_address(&mut self, listening_address: NetAddress) -> &mut Self {
170+
self.config.listening_address = Some(listening_address);
171+
self
167172
}
168173

169174
/// Sets the level at which [`Node`] will log messages.
170-
pub fn set_log_level(&self, level: LogLevel) {
171-
let mut config = self.config.write().unwrap();
172-
config.log_level = level;
175+
pub fn set_log_level(&mut self, level: LogLevel) -> &mut Self {
176+
self.config.log_level = level;
177+
self
173178
}
174179

175180
/// Builds a [`Node`] instance with a [`SqliteStore`] backend and according to the options
176181
/// previously configured.
177-
pub fn build(&self) -> Arc<Node<SqliteStore>> {
178-
let storage_dir_path = self.config.read().unwrap().storage_dir_path.clone();
182+
pub fn build(&self) -> Node<SqliteStore> {
183+
let storage_dir_path = self.config.storage_dir_path.clone();
179184
fs::create_dir_all(storage_dir_path.clone()).expect("Failed to create LDK data directory");
180185
let kv_store = Arc::new(SqliteStore::new(storage_dir_path.into()));
181186
self.build_with_store(kv_store)
182187
}
183188

184189
/// Builds a [`Node`] instance with a [`FilesystemStore`] backend and according to the options
185190
/// previously configured.
186-
pub fn build_with_fs_store(&self) -> Arc<Node<FilesystemStore>> {
187-
let storage_dir_path = self.config.read().unwrap().storage_dir_path.clone();
191+
pub fn build_with_fs_store(&self) -> Node<FilesystemStore> {
192+
let storage_dir_path = self.config.storage_dir_path.clone();
188193
fs::create_dir_all(storage_dir_path.clone()).expect("Failed to create LDK data directory");
189194
let kv_store = Arc::new(FilesystemStore::new(storage_dir_path.into()));
190195
self.build_with_store(kv_store)
@@ -193,29 +198,132 @@ impl Builder {
193198
/// Builds a [`Node`] instance according to the options previously configured.
194199
pub fn build_with_store<K: KVStore + Sync + Send + 'static>(
195200
&self, kv_store: Arc<K>,
196-
) -> Arc<Node<K>> {
197-
let config = Arc::new(self.config.read().unwrap().clone());
201+
) -> Node<K> {
202+
let config = Arc::new(self.config.clone());
198203

199-
let entropy_source_config = self.entropy_source_config.read().unwrap();
200-
let chain_data_source_config = self.chain_data_source_config.read().unwrap();
201-
let gossip_source_config = self.gossip_source_config.read().unwrap();
202204
let runtime = Arc::new(RwLock::new(None));
203-
Arc::new(build_with_store_internal(
205+
build_with_store_internal(
204206
config,
205-
entropy_source_config.as_ref(),
206-
chain_data_source_config.as_ref(),
207-
gossip_source_config.as_ref(),
207+
self.entropy_source_config.as_ref(),
208+
self.chain_data_source_config.as_ref(),
209+
self.gossip_source_config.as_ref(),
208210
kv_store,
209211
runtime,
210-
))
212+
)
213+
}
214+
}
215+
216+
/// A builder for an [`Node`] instance, allowing to set some configuration and module choices from
217+
/// the getgo.
218+
///
219+
/// ### Defaults
220+
/// - Wallet entropy is sourced from a `keys_seed` file located under [`Config::storage_dir_path`]
221+
/// - Chain data is sourced from the Esplora endpoint `https://blockstream.info/api`
222+
/// - Gossip data is sourced via the peer-to-peer network
223+
#[derive(Debug)]
224+
#[cfg(feature = "uniffi")]
225+
pub struct ArcedNodeBuilder {
226+
inner: RwLock<NodeBuilder>,
227+
}
228+
229+
#[cfg(feature = "uniffi")]
230+
impl ArcedNodeBuilder {
231+
/// Creates a new builder instance with the default configuration.
232+
pub fn new() -> Self {
233+
let inner = RwLock::new(NodeBuilder::new());
234+
Self { inner }
235+
}
236+
237+
/// Creates a new builder instance from an [`Config`].
238+
pub fn from_config(config: Config) -> Self {
239+
let inner = RwLock::new(NodeBuilder::from_config(config));
240+
Self { inner }
241+
}
242+
243+
/// Configures the [`Node`] instance to source its wallet entropy from a seed file on disk.
244+
///
245+
/// If the given file does not exist a new random seed file will be generated and
246+
/// stored at the given location.
247+
pub fn set_entropy_seed_path(&self, seed_path: String) {
248+
self.inner.write().unwrap().set_entropy_seed_path(seed_path);
249+
}
250+
251+
/// Configures the [`Node`] instance to source its wallet entropy from the given 64 seed bytes.
252+
///
253+
/// **Note:** Panics if the length of the given `seed_bytes` differs from 64.
254+
pub fn set_entropy_seed_bytes(&self, seed_bytes: Vec<u8>) {
255+
self.inner.write().unwrap().set_entropy_seed_bytes(seed_bytes);
256+
}
257+
258+
/// Configures the [`Node`] instance to source its wallet entropy from a [BIP 39] mnemonic.
259+
///
260+
/// [BIP 39]: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki
261+
pub fn set_entropy_bip39_mnemonic(&self, mnemonic: Mnemonic, passphrase: Option<String>) {
262+
self.inner.write().unwrap().set_entropy_bip39_mnemonic(mnemonic, passphrase);
263+
}
264+
265+
/// Configures the [`Node`] instance to source its chain data from the given Esplora server.
266+
pub fn set_esplora_server(&self, esplora_server_url: String) {
267+
self.inner.write().unwrap().set_esplora_server(esplora_server_url);
268+
}
269+
270+
/// Configures the [`Node`] instance to source its gossip data from the Lightning peer-to-peer
271+
/// network.
272+
pub fn set_gossip_source_p2p(&self) {
273+
self.inner.write().unwrap().set_gossip_source_p2p();
274+
}
275+
276+
/// Configures the [`Node`] instance to source its gossip data from the given RapidGossipSync
277+
/// server.
278+
pub fn set_gossip_source_rgs(&self, rgs_server_url: String) {
279+
self.inner.write().unwrap().set_gossip_source_rgs(rgs_server_url);
280+
}
281+
282+
/// Sets the used storage directory path.
283+
pub fn set_storage_dir_path(&self, storage_dir_path: String) {
284+
self.inner.write().unwrap().set_storage_dir_path(storage_dir_path);
285+
}
286+
287+
/// Sets the Bitcoin network used.
288+
pub fn set_network(&self, network: Network) {
289+
self.inner.write().unwrap().set_network(network);
290+
}
291+
292+
/// Sets the IP address and TCP port on which [`Node`] will listen for incoming network connections.
293+
pub fn set_listening_address(&self, listening_address: NetAddress) {
294+
self.inner.write().unwrap().set_listening_address(listening_address);
295+
}
296+
297+
/// Sets the level at which [`Node`] will log messages.
298+
pub fn set_log_level(&self, level: LogLevel) {
299+
self.inner.write().unwrap().set_log_level(level);
300+
}
301+
302+
/// Builds a [`Node`] instance with a [`SqliteStore`] backend and according to the options
303+
/// previously configured.
304+
pub fn build(&self) -> Arc<Node<SqliteStore>> {
305+
Arc::new(self.inner.read().unwrap().build())
306+
}
307+
308+
/// Builds a [`Node`] instance with a [`FilesystemStore`] backend and according to the options
309+
/// previously configured.
310+
pub fn build_with_fs_store(&self) -> Arc<Node<FilesystemStore>> {
311+
Arc::new(self.inner.read().unwrap().build_with_fs_store())
312+
}
313+
314+
/// Builds a [`Node`] instance according to the options previously configured.
315+
pub fn build_with_store<K: KVStore + Sync + Send + 'static>(
316+
&self, kv_store: Arc<K>,
317+
) -> Arc<Node<K>> {
318+
Arc::new(self.inner.read().unwrap().build_with_store(kv_store))
211319
}
212320
}
213321

214322
/// Builds a [`Node`] instance according to the options previously configured.
215-
fn build_with_store_internal<'a, K: KVStore + Sync + Send + 'static>(
216-
config: Arc<Config>, entropy_source_config: Option<&'a EntropySourceConfig>,
217-
chain_data_source_config: Option<&'a ChainDataSourceConfig>,
218-
gossip_source_config: Option<&'a GossipSourceConfig>, kv_store: Arc<K>,
323+
fn build_with_store_internal<K: KVStore + Sync + Send + 'static>(
324+
config: Arc<Config>, entropy_source_config: Option<&EntropySourceConfig>,
325+
chain_data_source_config: Option<&ChainDataSourceConfig>,
326+
gossip_source_config: Option<&GossipSourceConfig>, kv_store: Arc<K>,
219327
runtime: Arc<RwLock<Option<tokio::runtime::Runtime>>>,
220328
) -> Node<K> {
221329
let ldk_data_dir = format!("{}/ldk", config.storage_dir_path);

src/lib.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
//! use std::str::FromStr;
3434
//!
3535
//! fn main() {
36-
//! let builder = Builder::new();
36+
//! let mut builder = Builder::new();
3737
//! builder.set_network(Network::Testnet);
3838
//! builder.set_esplora_server("https://blockstream.info/testnet/api".to_string());
3939
//! builder.set_gossip_source_rgs("https://rapidsync.lightningdevkit.org/testnet/snapshot".to_string());
@@ -109,7 +109,11 @@ pub use types::NetAddress;
109109
#[cfg(feature = "uniffi")]
110110
use {bip39::Mnemonic, bitcoin::OutPoint, lightning::ln::PaymentSecret, uniffi_types::*};
111111

112-
pub use builder::Builder;
112+
#[cfg(feature = "uniffi")]
113+
pub use builder::ArcedNodeBuilder as Builder;
114+
#[cfg(not(feature = "uniffi"))]
115+
pub use builder::NodeBuilder as Builder;
116+
113117
use event::{EventHandler, EventQueue};
114118
use gossip::GossipSource;
115119
use io::KVStore;

0 commit comments

Comments
 (0)