Skip to content

Commit c634094

Browse files
committed
Break builder pattern and expose setters via bindings
Unfortunately there is no good way of exposing the builder pattern via Uniffi bindings currenlty. We therefore resort to internal mutability and using setters on the `Builder` object.
1 parent 319f4ae commit c634094

File tree

5 files changed

+99
-65
lines changed

5 files changed

+99
-65
lines changed

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,15 @@ The primary abstraction of the library is the `Node`, which can be retrieved by
1111
use ldk_node::{Builder, NetAddress};
1212
use ldk_node::lightning_invoice::Invoice;
1313
use ldk_node::bitcoin::secp256k1::PublicKey;
14+
use ldk_node::bitcoin::Network;
1415
use std::str::FromStr;
1516

1617
fn main() {
17-
let node = Builder::new()
18-
.set_network("testnet")
19-
.set_esplora_server_url("https://blockstream.info/testnet/api".to_string())
20-
.build();
18+
let mut builder = Builder::new();
19+
builder.set_network(Network::Testnet);
20+
builder.set_esplora_server_url("https://blockstream.info/testnet/api".to_string());
2121

22+
let node = builder.build();
2223
node.start().unwrap();
2324

2425
let _funding_address = node.new_funding_address();

bindings/ldk_node.udl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@ interface Builder {
1313
constructor();
1414
[Name=from_config]
1515
constructor(Config config);
16+
void set_entropy_seed_path(string seed_path);
17+
void set_entropy_seed_bytes(sequence<u8> seed_bytes);
18+
void set_entropy_bip39_mnemonic(Mnemonic mnemonic, string? passphrase);
19+
void set_gossip_source_p2p();
20+
void set_gossip_source_rgs(string rgs_server_url);
21+
void set_storage_dir_path(string storage_dir_path);
22+
void set_esplora_server_url(string esplora_server_url);
23+
void set_network(Network network);
24+
void set_listening_address(NetAddress listening_address);
1625
LDKNode build();
1726
};
1827

@@ -85,6 +94,7 @@ enum NodeError {
8594
"InvalidAddress",
8695
"InvalidNetAddress",
8796
"InvalidPublicKey",
97+
"InvalidSecretKey",
8898
"InvalidPaymentHash",
8999
"InvalidPaymentPreimage",
90100
"InvalidPaymentSecret",
@@ -188,3 +198,6 @@ typedef string UserChannelId;
188198

189199
[Custom]
190200
typedef string Network;
201+
202+
[Custom]
203+
typedef string Mnemonic;

src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ pub enum Error {
3737
InvalidNetAddress,
3838
/// The given public key is invalid.
3939
InvalidPublicKey,
40+
/// The given secret key is invalid.
41+
InvalidSecretKey,
4042
/// The given payment hash is invalid.
4143
InvalidPaymentHash,
4244
/// The given payment preimage is invalid.
@@ -79,6 +81,7 @@ impl fmt::Display for Error {
7981
Self::InvalidAddress => write!(f, "The given address is invalid."),
8082
Self::InvalidNetAddress => write!(f, "The given network address is invalid."),
8183
Self::InvalidPublicKey => write!(f, "The given public key is invalid."),
84+
Self::InvalidSecretKey => write!(f, "The given secret key is invalid."),
8285
Self::InvalidPaymentHash => write!(f, "The given payment hash is invalid."),
8386
Self::InvalidPaymentPreimage => write!(f, "The given payment preimage is invalid."),
8487
Self::InvalidPaymentSecret => write!(f, "The given payment secret is invalid."),

src/lib.rs

Lines changed: 64 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,15 @@
2929
//! use ldk_node::{Builder, NetAddress};
3030
//! use ldk_node::lightning_invoice::Invoice;
3131
//! use ldk_node::bitcoin::secp256k1::PublicKey;
32+
//! use ldk_node::bitcoin::Network;
3233
//! use std::str::FromStr;
3334
//!
3435
//! fn main() {
35-
//! let node = Builder::new()
36-
//! .set_network("testnet")
37-
//! .set_esplora_server_url("https://blockstream.info/testnet/api".to_string())
38-
//! .build();
36+
//! let mut builder = Builder::new();
37+
//! builder.set_network(Network::Testnet);
38+
//! builder.set_esplora_server_url("https://blockstream.info/testnet/api".to_string());
3939
//!
40+
//! let node = builder.build();
4041
//! node.start().unwrap();
4142
//!
4243
//! let _funding_address = node.new_funding_address();
@@ -144,6 +145,8 @@ use bitcoin::hashes::Hash;
144145
use bitcoin::secp256k1::PublicKey;
145146
use bitcoin::Network;
146147

148+
use bip39::Mnemonic;
149+
147150
use bitcoin::{Address, BlockHash, OutPoint, Txid};
148151

149152
use rand::Rng;
@@ -152,7 +155,6 @@ use std::convert::TryInto;
152155
use std::default::Default;
153156
use std::fs;
154157
use std::net::ToSocketAddrs;
155-
use std::str::FromStr;
156158
use std::sync::atomic::{AtomicBool, Ordering};
157159
use std::sync::{Arc, Mutex, RwLock};
158160
use std::time::{Duration, Instant, SystemTime};
@@ -212,7 +214,7 @@ impl Default for Config {
212214
enum EntropySourceConfig {
213215
SeedFile(String),
214216
SeedBytes([u8; WALLET_KEYS_SEED_LEN]),
215-
Bip39Mnemonic { mnemonic: bip39::Mnemonic, passphrase: Option<String> },
217+
Bip39Mnemonic { mnemonic: Mnemonic, passphrase: Option<String> },
216218
}
217219

218220
#[derive(Debug, Clone)]
@@ -223,107 +225,107 @@ enum GossipSourceConfig {
223225

224226
/// A builder for an [`Node`] instance, allowing to set some configuration and module choices from
225227
/// the getgo.
226-
#[derive(Debug, Clone)]
228+
#[derive(Debug)]
227229
pub struct Builder {
228-
config: Config,
229-
entropy_source_config: Option<EntropySourceConfig>,
230-
gossip_source_config: Option<GossipSourceConfig>,
230+
config: Mutex<Config>,
231+
entropy_source_config: Mutex<Option<EntropySourceConfig>>,
232+
gossip_source_config: Mutex<Option<GossipSourceConfig>>,
231233
}
232234

233235
impl Builder {
234236
/// Creates a new builder instance with the default configuration.
235237
pub fn new() -> Self {
236-
let config = Config::default();
237-
let entropy_source_config = None;
238-
let gossip_source_config = None;
238+
let config = Mutex::new(Config::default());
239+
let entropy_source_config = Mutex::new(None);
240+
let gossip_source_config = Mutex::new(None);
239241
Self { config, entropy_source_config, gossip_source_config }
240242
}
241243

242244
/// Creates a new builder instance from an [`Config`].
243245
pub fn from_config(config: Config) -> Self {
244-
let entropy_source_config = None;
245-
let gossip_source_config = None;
246+
let config = Mutex::new(config);
247+
let entropy_source_config = Mutex::new(None);
248+
let gossip_source_config = Mutex::new(None);
246249
Self { config, entropy_source_config, gossip_source_config }
247250
}
248251

249252
/// Configures the [`Node`] instance to source its wallet entropy from a seed file on disk.
250253
///
251254
/// If the given file does not exist a new random seed file will be generated and
252255
/// stored at the given location.
253-
pub fn set_entropy_seed_path(&mut self, seed_path: String) -> &mut Self {
254-
self.entropy_source_config = Some(EntropySourceConfig::SeedFile(seed_path));
255-
self
256+
pub fn set_entropy_seed_path(&self, seed_path: String) {
257+
*self.entropy_source_config.lock().unwrap() =
258+
Some(EntropySourceConfig::SeedFile(seed_path));
259+
}
260+
261+
/// Configures the [`Node`] instance to source its wallet entropy from the given 64 seed bytes.
262+
///
263+
/// **Note:** Panics if the length of the given `seed_bytes` differs from 64.
264+
pub fn set_entropy_seed_bytes(&self, seed_bytes: Vec<u8>) {
265+
if seed_bytes.len() != WALLET_KEYS_SEED_LEN {
266+
panic!("Failed to set seed due to invalid length.");
267+
}
268+
let mut bytes = [0u8; WALLET_KEYS_SEED_LEN];
269+
bytes.copy_from_slice(&seed_bytes);
270+
*self.entropy_source_config.lock().unwrap() = Some(EntropySourceConfig::SeedBytes(bytes));
256271
}
257272

258-
/// Configures the [`Node`] instance to source its wallet entropy from the given seed bytes.
259-
pub fn set_entropy_seed_bytes(&mut self, seed_bytes: [u8; WALLET_KEYS_SEED_LEN]) -> &mut Self {
260-
self.entropy_source_config = Some(EntropySourceConfig::SeedBytes(seed_bytes));
261-
self
273+
/// Configures the [`Node`] instance to source its wallet entropy from a [BIP 39] mnemonic.
274+
///
275+
/// [BIP 39]: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki
276+
pub fn set_entropy_bip39_mnemonic(&self, mnemonic: Mnemonic, passphrase: Option<String>) {
277+
*self.entropy_source_config.lock().unwrap() =
278+
Some(EntropySourceConfig::Bip39Mnemonic { mnemonic, passphrase });
262279
}
263280

264281
/// Configures the [`Node`] instance to source its gossip data from the Lightning peer-to-peer
265282
/// network.
266-
pub fn set_gossip_source_p2p(&mut self) -> &mut Self {
267-
self.gossip_source_config = Some(GossipSourceConfig::P2PNetwork);
268-
self
283+
pub fn set_gossip_source_p2p(&self) {
284+
*self.gossip_source_config.lock().unwrap() = Some(GossipSourceConfig::P2PNetwork);
269285
}
270286

271287
/// Configures the [`Node`] instance to source its gossip data from the given RapidGossipSync
272288
/// server.
273-
pub fn set_gossip_source_rgs(&mut self, rgs_server_url: String) -> &mut Self {
274-
self.gossip_source_config = Some(GossipSourceConfig::RapidGossipSync(rgs_server_url));
275-
self
276-
}
277-
278-
/// Configures the [`Node`] instance to source its wallet entropy from a [BIP 39] mnemonic.
279-
///
280-
/// [BIP 39]: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki
281-
pub fn set_entropy_bip39_mnemonic(
282-
&mut self, mnemonic: bip39::Mnemonic, passphrase: Option<String>,
283-
) -> &mut Self {
284-
self.entropy_source_config =
285-
Some(EntropySourceConfig::Bip39Mnemonic { mnemonic, passphrase });
286-
self
289+
pub fn set_gossip_source_rgs(&self, rgs_server_url: String) {
290+
*self.gossip_source_config.lock().unwrap() =
291+
Some(GossipSourceConfig::RapidGossipSync(rgs_server_url));
287292
}
288293

289294
/// Sets the used storage directory path.
290295
///
291296
/// Default: `/tmp/ldk_node/`
292-
pub fn set_storage_dir_path(&mut self, storage_dir_path: String) -> &mut Self {
293-
self.config.storage_dir_path = storage_dir_path;
294-
self
297+
pub fn set_storage_dir_path(&self, storage_dir_path: String) {
298+
let mut config = self.config.lock().unwrap();
299+
config.storage_dir_path = storage_dir_path;
295300
}
296301

297302
/// Sets the Esplora server URL.
298303
///
299304
/// Default: `https://blockstream.info/api`
300-
pub fn set_esplora_server_url(&mut self, esplora_server_url: String) -> &mut Self {
301-
self.config.esplora_server_url = esplora_server_url;
302-
self
305+
pub fn set_esplora_server_url(&self, esplora_server_url: String) {
306+
let mut config = self.config.lock().unwrap();
307+
config.esplora_server_url = esplora_server_url;
303308
}
304309

305310
/// Sets the Bitcoin network used.
306-
///
307-
/// Options: `mainnet`/`bitcoin`, `testnet`, `regtest`, `signet`
308-
///
309-
/// Default: `regtest`
310-
pub fn set_network(&mut self, network: &str) -> &mut Self {
311-
self.config.network = Network::from_str(network).unwrap_or(Network::Regtest);
312-
self
311+
pub fn set_network(&self, network: Network) {
312+
let mut config = self.config.lock().unwrap();
313+
config.network = network;
313314
}
314315

315316
/// Sets the IP address and TCP port on which [`Node`] will listen for incoming network connections.
316317
///
317318
/// Default: `0.0.0.0:9735`
318-
pub fn set_listening_address(&mut self, listening_address: NetAddress) -> &mut Self {
319-
self.config.listening_address = Some(listening_address);
320-
self
319+
pub fn set_listening_address(&self, listening_address: NetAddress) {
320+
let mut config = self.config.lock().unwrap();
321+
config.listening_address = Some(listening_address);
321322
}
322323

323324
/// Builds a [`Node`] instance with a [`FilesystemStore`] backend and according to the options
324325
/// previously configured.
325326
pub fn build(&self) -> Arc<Node<FilesystemStore>> {
326-
let ldk_data_dir = format!("{}/ldk", self.config.storage_dir_path);
327+
let config = self.config.lock().unwrap();
328+
let ldk_data_dir = format!("{}/ldk", config.storage_dir_path);
327329
let kv_store = Arc::new(FilesystemStore::new(ldk_data_dir.clone().into()));
328330
self.build_with_store(kv_store)
329331
}
@@ -332,7 +334,7 @@ impl Builder {
332334
pub fn build_with_store<K: KVStore + Sync + Send + 'static>(
333335
&self, kv_store: Arc<K>,
334336
) -> Arc<Node<K>> {
335-
let config = Arc::new(self.config.clone());
337+
let config = Arc::new(self.config.lock().unwrap().clone());
336338

337339
let ldk_data_dir = format!("{}/ldk", config.storage_dir_path);
338340
fs::create_dir_all(ldk_data_dir.clone()).expect("Failed to create LDK data directory");
@@ -345,7 +347,9 @@ impl Builder {
345347
let logger = Arc::new(FilesystemLogger::new(log_file_path));
346348

347349
// Initialize the on-chain wallet and chain access
348-
let seed_bytes = if let Some(entropy_source_config) = &self.entropy_source_config {
350+
let seed_bytes = if let Some(entropy_source_config) =
351+
&*self.entropy_source_config.lock().unwrap()
352+
{
349353
// Use the configured entropy source, if the user set one.
350354
match entropy_source_config {
351355
EntropySourceConfig::SeedBytes(bytes) => bytes.clone(),
@@ -550,8 +554,9 @@ impl Builder {
550554

551555
// Initialize the GossipSource
552556
// Use the configured gossip source, if the user set one, otherwise default to P2PNetwork.
557+
let gossip_source_config_lock = self.gossip_source_config.lock().unwrap();
553558
let gossip_source_config =
554-
self.gossip_source_config.as_ref().unwrap_or(&GossipSourceConfig::P2PNetwork);
559+
gossip_source_config_lock.as_ref().unwrap_or(&GossipSourceConfig::P2PNetwork);
555560

556561
let gossip_source = match gossip_source_config {
557562
GossipSourceConfig::P2PNetwork => {

src/types.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ use bitcoin::hashes::Hash;
2424
use bitcoin::secp256k1::PublicKey;
2525
use bitcoin::{Address, Network, OutPoint, Txid};
2626

27-
use core::convert::TryFrom;
28-
use std::convert::TryInto;
27+
use bip39::Mnemonic;
28+
29+
use std::convert::{TryFrom, TryInto};
2930
use std::fmt::Display;
3031
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
3132
use std::str::FromStr;
@@ -436,6 +437,17 @@ impl Display for NetAddress {
436437
}
437438
}
438439

440+
impl UniffiCustomTypeConverter for Mnemonic {
441+
type Builtin = String;
442+
fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {
443+
Ok(Mnemonic::from_str(&val).map_err(|_| Error::InvalidSecretKey)?)
444+
}
445+
446+
fn from_custom(obj: Self) -> Self::Builtin {
447+
obj.to_string()
448+
}
449+
}
450+
439451
impl UniffiCustomTypeConverter for NetAddress {
440452
type Builtin = String;
441453
fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {

0 commit comments

Comments
 (0)