Skip to content

Commit a9c072f

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 cd85df1 commit a9c072f

File tree

4 files changed

+170
-70
lines changed

4 files changed

+170
-70
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: 151 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -74,117 +74,110 @@ 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) {
108+
self.entropy_source_config = Some(EntropySourceConfig::SeedFile(seed_path));
110109
}
111110

112111
/// Configures the [`Node`] instance to source its wallet entropy from the given 64 seed bytes.
113112
///
114113
/// **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>) {
114+
pub fn set_entropy_seed_bytes(&mut self, seed_bytes: Vec<u8>) {
116115
if seed_bytes.len() != WALLET_KEYS_SEED_LEN {
117116
panic!("Failed to set seed due to invalid length.");
118117
}
119118
let mut bytes = [0u8; WALLET_KEYS_SEED_LEN];
120119
bytes.copy_from_slice(&seed_bytes);
121-
*self.entropy_source_config.write().unwrap() = Some(EntropySourceConfig::SeedBytes(bytes));
120+
self.entropy_source_config = Some(EntropySourceConfig::SeedBytes(bytes));
122121
}
123122

124123
/// Configures the [`Node`] instance to source its wallet entropy from a [BIP 39] mnemonic.
125124
///
126125
/// [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() =
126+
pub fn set_entropy_bip39_mnemonic(&mut self, mnemonic: Mnemonic, passphrase: Option<String>) {
127+
self.entropy_source_config =
129128
Some(EntropySourceConfig::Bip39Mnemonic { mnemonic, passphrase });
130129
}
131130

132131
/// 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));
132+
pub fn set_esplora_server(&mut self, esplora_server_url: String) {
133+
self.chain_data_source_config = Some(ChainDataSourceConfig::Esplora(esplora_server_url));
136134
}
137135

138136
/// Configures the [`Node`] instance to source its gossip data from the Lightning peer-to-peer
139137
/// network.
140-
pub fn set_gossip_source_p2p(&self) {
141-
*self.gossip_source_config.write().unwrap() = Some(GossipSourceConfig::P2PNetwork);
138+
pub fn set_gossip_source_p2p(&mut self) {
139+
self.gossip_source_config = Some(GossipSourceConfig::P2PNetwork);
142140
}
143141

144142
/// Configures the [`Node`] instance to source its gossip data from the given RapidGossipSync
145143
/// 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));
144+
pub fn set_gossip_source_rgs(&mut self, rgs_server_url: String) {
145+
self.gossip_source_config = Some(GossipSourceConfig::RapidGossipSync(rgs_server_url));
149146
}
150147

151148
/// 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;
149+
pub fn set_storage_dir_path(&mut self, storage_dir_path: String) {
150+
self.config.storage_dir_path = storage_dir_path;
155151
}
156152

157153
/// 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;
154+
pub fn set_network(&mut self, network: Network) {
155+
self.config.network = network;
161156
}
162157

163158
/// 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);
159+
pub fn set_listening_address(&mut self, listening_address: NetAddress) {
160+
self.config.listening_address = Some(listening_address);
167161
}
168162

169163
/// 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;
164+
pub fn set_log_level(&mut self, level: LogLevel) {
165+
self.config.log_level = level;
173166
}
174167

175168
/// Builds a [`Node`] instance with a [`SqliteStore`] backend and according to the options
176169
/// previously configured.
177-
pub fn build(&self) -> Arc<Node<SqliteStore>> {
178-
let storage_dir_path = self.config.read().unwrap().storage_dir_path.clone();
170+
pub fn build(&self) -> Node<SqliteStore> {
171+
let storage_dir_path = self.config.storage_dir_path.clone();
179172
fs::create_dir_all(storage_dir_path.clone()).expect("Failed to create LDK data directory");
180173
let kv_store = Arc::new(SqliteStore::new(storage_dir_path.into()));
181174
self.build_with_store(kv_store)
182175
}
183176

184177
/// Builds a [`Node`] instance with a [`FilesystemStore`] backend and according to the options
185178
/// 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();
179+
pub fn build_with_fs_store(&self) -> Node<FilesystemStore> {
180+
let storage_dir_path = self.config.storage_dir_path.clone();
188181
fs::create_dir_all(storage_dir_path.clone()).expect("Failed to create LDK data directory");
189182
let kv_store = Arc::new(FilesystemStore::new(storage_dir_path.into()));
190183
self.build_with_store(kv_store)
@@ -193,21 +186,124 @@ impl Builder {
193186
/// Builds a [`Node`] instance according to the options previously configured.
194187
pub fn build_with_store<K: KVStore + Sync + Send + 'static>(
195188
&self, kv_store: Arc<K>,
196-
) -> Arc<Node<K>> {
197-
let config = Arc::new(self.config.read().unwrap().clone());
189+
) -> Node<K> {
190+
let config = Arc::new(self.config.clone());
198191

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

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)