Skip to content

Commit 82b6b84

Browse files
committed
Generate UniFFI scaffolding
We generate the scaffolding from an UDL file and include it in `lib.rs`. Furthermore, we add a bindings generation shell script for convenience.
1 parent 20f8f47 commit 82b6b84

File tree

8 files changed

+253
-34
lines changed

8 files changed

+253
-34
lines changed

Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ edition = "2018"
77

88
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
99

10+
[lib]
11+
crate-type = ["staticlib", "cdylib"]
12+
name = "ldk_lite"
13+
1014
[dependencies]
1115
#lightning = { version = "0.0.112", features = ["max_level_trace", "std"] }
1216
#lightning-invoice = { version = "0.20" }
@@ -46,13 +50,18 @@ chrono = "0.4"
4650
futures = "0.3"
4751
serde_json = { version = "1.0" }
4852
tokio = { version = "1", features = [ "full" ] }
53+
uniffi = { version = "0.21.0", features = ["builtin-bindgen"] }
54+
uniffi_macros = { version = "0.21.0", features = ["builtin-bindgen"] }
4955

5056
[dev-dependencies]
5157
electrsd = { version = "0.22.0", features = ["legacy", "esplora_a33e97e1", "bitcoind_23_0"] }
5258
electrum-client = "0.12.0"
5359
lazy_static = "1.4.0"
5460
once_cell = "1.16.0"
5561

62+
[build-dependencies]
63+
uniffi_build = "0.21.0"
64+
5665
[profile.release]
5766
panic = "abort"
5867

build.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
fn main() {
2+
uniffi_build::generate_scaffolding("uniffi/ldk_lite.udl").unwrap();
3+
}

src/error.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,20 @@ pub enum Error {
1111
FundingTxCreationFailed,
1212
/// A network connection has been closed.
1313
ConnectionFailed,
14+
/// The given address is invalid.
15+
AddressInvalid,
16+
/// The given public key is invalid.
17+
PublicKeyInvalid,
18+
/// The given payment hash is invalid.
19+
PaymentHashInvalid,
1420
/// Payment of the given invoice has already been intiated.
1521
NonUniquePaymentHash,
1622
/// The given invoice is invalid.
1723
InvoiceInvalid,
1824
/// Invoice creation failed.
1925
InvoiceCreationFailed,
26+
/// The given channel ID is invalid.
27+
ChannelIdInvalid,
2028
/// No route for the given target could be found.
2129
RoutingFailed,
2230
/// A given peer info could not be parsed.
@@ -40,13 +48,15 @@ impl fmt::Display for Error {
4048
match *self {
4149
Self::AlreadyRunning => write!(f, "LDKLite is already running."),
4250
Self::NotRunning => write!(f, "LDKLite is not running."),
43-
Self::FundingTxCreationFailed => {
44-
write!(f, "Funding transaction could not be created.")
45-
}
51+
Self::FundingTxCreationFailed => write!(f, "Funding transaction could not be created."),
4652
Self::ConnectionFailed => write!(f, "Network connection closed."),
53+
Self::AddressInvalid => write!(f, "The given address is invalid."),
54+
Self::PublicKeyInvalid => write!(f, "The given public key is invalid."),
55+
Self::PaymentHashInvalid => write!(f, "The given payment hash is invalid."),
4756
Self::NonUniquePaymentHash => write!(f, "An invoice must not get payed twice."),
4857
Self::InvoiceInvalid => write!(f, "The given invoice is invalid."),
4958
Self::InvoiceCreationFailed => write!(f, "Failed to create invoice."),
59+
Self::ChannelIdInvalid => write!(f, "The given channel ID is invalid."),
5060
Self::RoutingFailed => write!(f, "Failed to find route."),
5161
Self::PeerInfoParseFailed => write!(f, "Failed to parse the given peer information."),
5262
Self::ChannelCreationFailed => write!(f, "Failed to create channel."),

src/event.rs

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{
2-
hex_utils, ChannelManager, Config, Error, KeysManager, NetworkGraph, PaymentInfo,
3-
PaymentInfoStorage, PaymentStatus, Wallet,
2+
hex_utils, ChannelId, ChannelManager, Config, Error, KeysManager, NetworkGraph, PaymentInfo,
3+
PaymentInfoStorage, PaymentStatus, UserChannelId, Wallet,
44
};
55

66
use crate::logger::{log_error, log_given_level, log_info, log_internal, Logger};
@@ -49,16 +49,16 @@ pub enum Event {
4949
/// A channel is ready to be used.
5050
ChannelReady {
5151
/// The `channel_id` of the channel.
52-
channel_id: [u8; 32],
52+
channel_id: ChannelId,
5353
/// The `user_channel_id` of the channel.
54-
user_channel_id: u128,
54+
user_channel_id: UserChannelId,
5555
},
5656
/// A channel has been closed.
5757
ChannelClosed {
5858
/// The `channel_id` of the channel.
59-
channel_id: [u8; 32],
59+
channel_id: ChannelId,
6060
/// The `user_channel_id` of the channel.
61-
user_channel_id: u128,
61+
user_channel_id: UserChannelId,
6262
},
6363
}
6464

@@ -82,13 +82,13 @@ impl Readable for Event {
8282
Ok(Self::PaymentReceived { payment_hash, amount_msat })
8383
}
8484
3u8 => {
85-
let channel_id: [u8; 32] = Readable::read(reader)?;
86-
let user_channel_id: u128 = Readable::read(reader)?;
85+
let channel_id = ChannelId(Readable::read(reader)?);
86+
let user_channel_id = UserChannelId(Readable::read(reader)?);
8787
Ok(Self::ChannelReady { channel_id, user_channel_id })
8888
}
8989
4u8 => {
90-
let channel_id: [u8; 32] = Readable::read(reader)?;
91-
let user_channel_id: u128 = Readable::read(reader)?;
90+
let channel_id = ChannelId(Readable::read(reader)?);
91+
let user_channel_id = UserChannelId(Readable::read(reader)?);
9292
Ok(Self::ChannelClosed { channel_id, user_channel_id })
9393
}
9494
_ => Err(lightning::ln::msgs::DecodeError::InvalidValue),
@@ -117,14 +117,14 @@ impl Writeable for Event {
117117
}
118118
Self::ChannelReady { channel_id, user_channel_id } => {
119119
3u8.write(writer)?;
120-
channel_id.write(writer)?;
121-
user_channel_id.write(writer)?;
120+
channel_id.0.write(writer)?;
121+
user_channel_id.0.write(writer)?;
122122
Ok(())
123123
}
124124
Self::ChannelClosed { channel_id, user_channel_id } => {
125125
4u8.write(writer)?;
126-
channel_id.write(writer)?;
127-
user_channel_id.write(writer)?;
126+
channel_id.0.write(writer)?;
127+
user_channel_id.0.write(writer)?;
128128
Ok(())
129129
}
130130
}
@@ -558,7 +558,10 @@ where
558558
counterparty_node_id,
559559
);
560560
self.event_queue
561-
.add_event(Event::ChannelReady { channel_id, user_channel_id })
561+
.add_event(Event::ChannelReady {
562+
channel_id: ChannelId(channel_id),
563+
user_channel_id: UserChannelId(user_channel_id),
564+
})
562565
.expect("Failed to push to event queue");
563566
}
564567
LdkEvent::ChannelClosed { channel_id, reason, user_channel_id } => {
@@ -569,7 +572,10 @@ where
569572
reason
570573
);
571574
self.event_queue
572-
.add_event(Event::ChannelClosed { channel_id, user_channel_id })
575+
.add_event(Event::ChannelClosed {
576+
channel_id: ChannelId(channel_id),
577+
user_channel_id: UserChannelId(user_channel_id),
578+
})
573579
.expect("Failed to push to event queue");
574580
}
575581
LdkEvent::DiscardFunding { .. } => {}
@@ -588,7 +594,10 @@ mod tests {
588594
let test_persister = Arc::new(TestPersister::new());
589595
let event_queue = EventQueue::new(Arc::clone(&test_persister));
590596

591-
let expected_event = Event::ChannelReady { channel_id: [23u8; 32], user_channel_id: 2323 };
597+
let expected_event = Event::ChannelReady {
598+
channel_id: ChannelId([23u8; 32]),
599+
user_channel_id: UserChannelId(2323),
600+
};
592601
event_queue.add_event(expected_event.clone()).unwrap();
593602
assert!(test_persister.get_and_clear_pending_persist());
594603

src/lib.rs

Lines changed: 114 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
//! - Wallet and channel states are persisted to disk.
1818
//! - Gossip is retrieved over the P2P network.
1919
20-
#![deny(missing_docs)]
2120
#![deny(broken_intra_doc_links)]
2221
#![deny(private_intra_doc_links)]
2322
#![allow(bare_trait_objects)]
@@ -64,7 +63,7 @@ use lightning_transaction_sync::EsploraSyncClient;
6463
use lightning_net_tokio::SocketDescriptor;
6564

6665
use lightning::routing::router::DefaultRouter;
67-
use lightning_invoice::{payment, Currency, Invoice};
66+
use lightning_invoice::{payment, Currency, Invoice, SignedRawInvoice};
6867

6968
use bdk::bitcoin::secp256k1::Secp256k1;
7069
use bdk::blockchain::esplora::EsploraBlockchain;
@@ -74,10 +73,11 @@ use bdk::template::Bip84;
7473
use bitcoin::hashes::sha256::Hash as Sha256;
7574
use bitcoin::hashes::Hash;
7675
use bitcoin::secp256k1::PublicKey;
77-
use bitcoin::BlockHash;
76+
use bitcoin::{Address, BlockHash};
7877

7978
use rand::Rng;
8079

80+
use core::str::FromStr;
8181
use std::collections::HashMap;
8282
use std::convert::{TryFrom, TryInto};
8383
use std::default::Default;
@@ -87,6 +87,8 @@ use std::sync::atomic::{AtomicBool, Ordering};
8787
use std::sync::{Arc, Mutex, RwLock};
8888
use std::time::{Duration, Instant, SystemTime};
8989

90+
uniffi_macros::include_scaffolding!("ldk_lite");
91+
9092
// The used 'stop gap' parameter used by BDK's wallet sync. This seems to configure the threshold
9193
// number of blocks after which BDK stops looking for scripts belonging to the wallet.
9294
const BDK_CLIENT_STOP_GAP: usize = 20;
@@ -194,7 +196,7 @@ impl Builder {
194196
}
195197

196198
/// Builds an [`LdkLite`] instance according to the options previously configured.
197-
pub fn build(&self) -> LdkLite {
199+
pub fn build(&self) -> Arc<LdkLite> {
198200
let config = Arc::new(self.config.clone());
199201

200202
let ldk_data_dir = format!("{}/ldk", &config.storage_dir_path.clone());
@@ -427,7 +429,7 @@ impl Builder {
427429

428430
let running = RwLock::new(None);
429431

430-
LdkLite {
432+
Arc::new(LdkLite {
431433
running,
432434
config,
433435
wallet,
@@ -446,7 +448,7 @@ impl Builder {
446448
inbound_payments,
447449
outbound_payments,
448450
peer_store,
449-
}
451+
})
450452
}
451453
}
452454

@@ -488,7 +490,7 @@ impl LdkLite {
488490
/// Starts the necessary background tasks, such as handling events coming from user input,
489491
/// LDK/BDK, and the peer-to-peer network. After this returns, the [`LdkLite`] instance can be
490492
/// controlled via the provided API methods in a thread-safe manner.
491-
pub fn start(&mut self) -> Result<(), Error> {
493+
pub fn start(&self) -> Result<(), Error> {
492494
// Acquire a run lock and hold it until we're setup.
493495
let mut run_lock = self.running.write().unwrap();
494496
if run_lock.is_some() {
@@ -502,7 +504,7 @@ impl LdkLite {
502504
}
503505

504506
/// Disconnects all peers, stops all running background tasks, and shuts down [`LdkLite`].
505-
pub fn stop(&mut self) -> Result<(), Error> {
507+
pub fn stop(&self) -> Result<(), Error> {
506508
let mut run_lock = self.running.write().unwrap();
507509
if run_lock.is_none() {
508510
return Err(Error::NotRunning);
@@ -696,7 +698,7 @@ impl LdkLite {
696698
}
697699

698700
/// Retrieve a new on-chain/funding address.
699-
pub fn new_funding_address(&mut self) -> Result<bitcoin::Address, Error> {
701+
pub fn new_funding_address(&self) -> Result<Address, Error> {
700702
let funding_address = self.wallet.get_new_address()?;
701703
log_info!(self.logger, "Generated new funding address: {}", funding_address);
702704
Ok(funding_address)
@@ -1100,3 +1102,106 @@ pub(crate) type OnionMessenger = lightning::onion_message::OnionMessenger<
11001102
Arc<FilesystemLogger>,
11011103
IgnoringMessageHandler,
11021104
>;
1105+
1106+
impl UniffiCustomTypeConverter for PublicKey {
1107+
type Builtin = String;
1108+
1109+
fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {
1110+
if let Ok(key) = PublicKey::from_str(&val) {
1111+
return Ok(key);
1112+
}
1113+
1114+
Err(Error::PublicKeyInvalid.into())
1115+
}
1116+
1117+
fn from_custom(obj: Self) -> Self::Builtin {
1118+
obj.to_string()
1119+
}
1120+
}
1121+
1122+
impl UniffiCustomTypeConverter for Address {
1123+
type Builtin = String;
1124+
1125+
fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {
1126+
if let Ok(addr) = Address::from_str(&val) {
1127+
return Ok(addr);
1128+
}
1129+
1130+
Err(Error::AddressInvalid.into())
1131+
}
1132+
1133+
fn from_custom(obj: Self) -> Self::Builtin {
1134+
obj.to_string()
1135+
}
1136+
}
1137+
1138+
impl UniffiCustomTypeConverter for Invoice {
1139+
type Builtin = String;
1140+
1141+
fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {
1142+
if let Ok(signed) = val.parse::<SignedRawInvoice>() {
1143+
if let Ok(invoice) = Invoice::from_signed(signed) {
1144+
return Ok(invoice);
1145+
}
1146+
}
1147+
1148+
Err(Error::InvoiceInvalid.into())
1149+
}
1150+
1151+
fn from_custom(obj: Self) -> Self::Builtin {
1152+
obj.to_string()
1153+
}
1154+
}
1155+
1156+
impl UniffiCustomTypeConverter for PaymentHash {
1157+
type Builtin = String;
1158+
1159+
fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {
1160+
if let Ok(hash) = Sha256::from_str(&val) {
1161+
Ok(PaymentHash(hash.into_inner()))
1162+
} else {
1163+
Err(Error::PaymentHashInvalid.into())
1164+
}
1165+
}
1166+
1167+
fn from_custom(obj: Self) -> Self::Builtin {
1168+
Sha256::from_slice(&obj.0).unwrap().to_string()
1169+
}
1170+
}
1171+
1172+
#[derive(Debug, Clone, PartialEq)]
1173+
pub struct ChannelId([u8; 32]);
1174+
1175+
impl UniffiCustomTypeConverter for ChannelId {
1176+
type Builtin = String;
1177+
1178+
fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {
1179+
if let Some(hex_vec) = hex_utils::to_vec(&val) {
1180+
if hex_vec.len() == 32 {
1181+
let mut channel_id = [0u8; 32];
1182+
channel_id.copy_from_slice(&hex_vec[..]);
1183+
return Ok(Self(channel_id));
1184+
}
1185+
}
1186+
Err(Error::ChannelIdInvalid.into())
1187+
}
1188+
1189+
fn from_custom(obj: Self) -> Self::Builtin {
1190+
hex_utils::to_string(&obj.0)
1191+
}
1192+
}
1193+
1194+
#[derive(Debug, Clone, PartialEq)]
1195+
pub struct UserChannelId(u128);
1196+
1197+
impl UniffiCustomTypeConverter for UserChannelId {
1198+
type Builtin = String;
1199+
1200+
fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {
1201+
Ok(UserChannelId(u128::from_str(&val).map_err(|_| Error::ChannelIdInvalid)?))
1202+
}
1203+
1204+
fn from_custom(obj: Self) -> Self::Builtin {
1205+
obj.0.to_string()
1206+
}
1207+
}

0 commit comments

Comments
 (0)