Skip to content

Commit 72617f9

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 8ba0604 commit 72617f9

File tree

5 files changed

+97
-62
lines changed

5 files changed

+97
-62
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
Node 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: 63 additions & 58 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;
@@ -199,7 +202,7 @@ impl Default for Config {
199202
storage_dir_path: "/tmp/ldk_node/".to_string(),
200203
esplora_server_url: "http://localhost:3002".to_string(),
201204
network: Network::Regtest,
202-
listening_address: Some("0.0.0.0:9735".parse().unwrap()),
205+
listening_address: Some(NetAddress::from_str("0.0.0.0:9735").unwrap()),
203206
default_cltv_expiry_delta: 144,
204207
}
205208
}
@@ -209,7 +212,7 @@ impl Default for Config {
209212
enum EntropySourceConfig {
210213
SeedFile(String),
211214
SeedBytes([u8; WALLET_KEYS_SEED_LEN]),
212-
Bip39Mnemonic { mnemonic: bip39::Mnemonic, passphrase: Option<String> },
215+
Bip39Mnemonic { mnemonic: Mnemonic, passphrase: Option<String> },
213216
}
214217

215218
#[derive(Debug, Clone)]
@@ -220,106 +223,105 @@ enum GossipSourceConfig {
220223

221224
/// A builder for an [`Node`] instance, allowing to set some configuration and module choices from
222225
/// the getgo.
223-
#[derive(Debug, Clone)]
226+
#[derive(Debug)]
224227
pub struct Builder {
225-
config: Config,
226-
entropy_source_config: Option<EntropySourceConfig>,
227-
gossip_source_config: Option<GossipSourceConfig>,
228+
config: Mutex<Config>,
229+
entropy_source_config: Mutex<Option<EntropySourceConfig>>,
230+
gossip_source_config: Mutex<Option<GossipSourceConfig>>,
228231
}
229232

230233
impl Builder {
231234
/// Creates a new builder instance with the default configuration.
232235
pub fn new() -> Self {
233-
let config = Config::default();
234-
let entropy_source_config = None;
235-
let gossip_source_config = None;
236+
let config = Mutex::new(Config::default());
237+
let entropy_source_config = Mutex::new(None);
238+
let gossip_source_config = Mutex::new(None);
236239
Self { config, entropy_source_config, gossip_source_config }
237240
}
238241

239242
/// Creates a new builder instance from an [`Config`].
240243
pub fn from_config(config: Config) -> Self {
241-
let entropy_source_config = None;
242-
let gossip_source_config = None;
244+
let config = Mutex::new(config);
245+
let entropy_source_config = Mutex::new(None);
246+
let gossip_source_config = Mutex::new(None);
243247
Self { config, entropy_source_config, gossip_source_config }
244248
}
245249

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

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

261279
/// Configures the [`Node`] instance to source its gossip data from the Lightning peer-to-peer
262280
/// network.
263-
pub fn set_gossip_source_p2p(&mut self) -> &mut Self {
264-
self.gossip_source_config = Some(GossipSourceConfig::P2PNetwork);
265-
self
281+
pub fn set_gossip_source_p2p(&self) {
282+
*self.gossip_source_config.lock().unwrap() = Some(GossipSourceConfig::P2PNetwork);
266283
}
267284

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

286292
/// Sets the used storage directory path.
287293
///
288294
/// Default: `/tmp/ldk_node/`
289-
pub fn set_storage_dir_path(&mut self, storage_dir_path: String) -> &mut Self {
290-
self.config.storage_dir_path = storage_dir_path;
291-
self
295+
pub fn set_storage_dir_path(&self, storage_dir_path: String) {
296+
let mut config = self.config.lock().unwrap();
297+
config.storage_dir_path = storage_dir_path;
292298
}
293299

294300
/// Sets the Esplora server URL.
295301
///
296302
/// Default: `https://blockstream.info/api`
297-
pub fn set_esplora_server_url(&mut self, esplora_server_url: String) -> &mut Self {
298-
self.config.esplora_server_url = esplora_server_url;
299-
self
303+
pub fn set_esplora_server_url(&self, esplora_server_url: String) {
304+
let mut config = self.config.lock().unwrap();
305+
config.esplora_server_url = esplora_server_url;
300306
}
301307

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

312314
/// Sets the IP address and TCP port on which [`Node`] will listen for incoming network connections.
313315
///
314316
/// Default: `0.0.0.0:9735`
315-
pub fn set_listening_address(&mut self, listening_address: NetAddress) -> &mut Self {
316-
self.config.listening_address = Some(listening_address);
317-
self
317+
pub fn set_listening_address(&self, listening_address: NetAddress) {
318+
let mut config = self.config.lock().unwrap();
319+
config.listening_address = Some(listening_address);
318320
}
319321

320322
/// Builds a [`Node`] instance according to the options previously configured.
321323
pub fn build(&self) -> Arc<Node> {
322-
let config = Arc::new(self.config.clone());
324+
let config = Arc::new(self.config.lock().unwrap().clone());
323325

324326
let ldk_data_dir = format!("{}/ldk", config.storage_dir_path);
325327
fs::create_dir_all(ldk_data_dir.clone()).expect("Failed to create LDK data directory");
@@ -332,7 +334,9 @@ impl Builder {
332334
let logger = Arc::new(FilesystemLogger::new(log_file_path));
333335

334336
// Initialize the on-chain wallet and chain access
335-
let seed_bytes = if let Some(entropy_source_config) = &self.entropy_source_config {
337+
let seed_bytes = if let Some(entropy_source_config) =
338+
&*self.entropy_source_config.lock().unwrap()
339+
{
336340
// Use the configured entropy source, if the user set one.
337341
match entropy_source_config {
338342
EntropySourceConfig::SeedBytes(bytes) => bytes.clone(),
@@ -539,8 +543,9 @@ impl Builder {
539543

540544
// Initialize the GossipSource
541545
// Use the configured gossip source, if the user set one, otherwise default to P2PNetwork.
546+
let gossip_source_config_lock = self.gossip_source_config.lock().unwrap();
542547
let gossip_source_config =
543-
self.gossip_source_config.as_ref().unwrap_or(&GossipSourceConfig::P2PNetwork);
548+
gossip_source_config_lock.as_ref().unwrap_or(&GossipSourceConfig::P2PNetwork);
544549

545550
let gossip_source = match gossip_source_config {
546551
GossipSourceConfig::P2PNetwork => {

src/types.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ use bitcoin::hashes::Hash;
2525
use bitcoin::secp256k1::PublicKey;
2626
use bitcoin::{Address, Network, OutPoint, Txid};
2727

28+
use bip39::Mnemonic;
2829
use core::convert::TryFrom;
30+
2931
use std::convert::TryInto;
3032
use std::fmt::Display;
3133
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
@@ -437,6 +439,17 @@ impl Display for NetAddress {
437439
}
438440
}
439441

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

0 commit comments

Comments
 (0)