Skip to content

Commit 61f9472

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 e82b5f7 commit 61f9472

File tree

7 files changed

+213
-23
lines changed

7 files changed

+213
-23
lines changed

Cargo.toml

+8
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.110", features = ["max_level_trace", "std"] }
1216
lightning-invoice = { version = "0.18" }
@@ -24,7 +28,11 @@ chrono = "0.4"
2428
futures = "0.3"
2529
serde_json = { version = "1.0" }
2630
tokio = { version = "1", features = [ "io-util", "macros", "rt", "rt-multi-thread", "sync", "net", "time" ] }
31+
uniffi = { version = "0.20.0", features = ["builtin-bindgen"] }
32+
uniffi_macros = { version = "0.20.0", features = ["builtin-bindgen"] }
2733

34+
[build-dependencies]
35+
uniffi_build = "0.20.0"
2836

2937
[profile.release]
3038
panic = "abort"

build.rs

+3
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

+13-3
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,20 @@ pub enum Error {
1212
FundingTxCreationFailed,
1313
/// A network connection has been closed.
1414
ConnectionFailed,
15+
/// The given address is invalid.
16+
AddressInvalid,
17+
/// The given public key is invalid.
18+
PublicKeyInvalid,
19+
/// The given payment hash is invalid.
20+
PaymentHashInvalid,
1521
/// Payment of the given invoice has already been intiated.
1622
NonUniquePaymentHash,
1723
/// The given invoice is invalid.
1824
InvoiceInvalid,
1925
/// Invoice creation failed.
2026
InvoiceCreationFailed,
27+
/// The given channel ID is invalid.
28+
ChannelIdInvalid,
2129
/// No route for the given target could be found.
2230
RoutingFailed,
2331
/// A given peer info could not be parsed.
@@ -41,13 +49,15 @@ impl fmt::Display for Error {
4149
match *self {
4250
Self::AlreadyRunning => write!(f, "LDKLite is already running."),
4351
Self::NotRunning => write!(f, "LDKLite is not running."),
44-
Self::FundingTxCreationFailed => {
45-
write!(f, "Funding transaction could not be created.")
46-
}
52+
Self::FundingTxCreationFailed => write!(f, "Funding transaction could not be created."),
4753
Self::ConnectionFailed => write!(f, "Network connection closed."),
54+
Self::AddressInvalid => write!(f, "The given address is invalid."),
55+
Self::PublicKeyInvalid => write!(f, "The given public key is invalid."),
56+
Self::PaymentHashInvalid => write!(f, "The given payment hash is invalid."),
4857
Self::NonUniquePaymentHash => write!(f, "An invoice must not get payed twice."),
4958
Self::InvoiceInvalid => write!(f, "The given invoice is invalid."),
5059
Self::InvoiceCreationFailed => write!(f, "Failed to create invoice."),
60+
Self::ChannelIdInvalid => write!(f, "The given channel ID is invalid."),
5161
Self::RoutingFailed => write!(f, "Failed to find route."),
5262
Self::PeerInfoParseFailed => write!(f, "Failed to parse the given peer information."),
5363
Self::ChannelCreationFailed => write!(f, "Failed to create channel."),

src/event.rs

+8-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{
2-
hex_utils, ChainAccess, ChannelManager, Config, Error, FilesystemPersister, NetworkGraph,
3-
PaymentInfo, PaymentInfoStorage, PaymentStatus,
2+
hex_utils, ChainAccess, ChannelId, ChannelManager, Config, Error, FilesystemPersister,
3+
NetworkGraph, PaymentInfo, PaymentInfoStorage, PaymentStatus,
44
};
55

66
use crate::logger::{log_error, log_given_level, log_info, log_internal, FilesystemLogger, Logger};
@@ -50,10 +50,10 @@ pub enum Event {
5050
// TODO: Implement after a corresponding LDK event is added.
5151
//ChannelOpened {
5252
//},
53-
/// A channel has been closed.
53+
///// A channel has been closed.
5454
ChannelClosed {
5555
/// The channel_id of the channel which has been closed.
56-
channel_id: [u8; 32],
56+
channel_id: ChannelId,
5757
},
5858
// TODO: Implement on-chain events when better integrating with BDK wallet sync.
5959
//OnChainPaymentSent {
@@ -84,7 +84,7 @@ impl Readable for Event {
8484
// TODO ChannelOpened
8585
//}
8686
4u8 => {
87-
let channel_id: [u8; 32] = Readable::read(reader)?;
87+
let channel_id: ChannelId = ChannelId(Readable::read(reader)?);
8888
Ok(Self::ChannelClosed { channel_id })
8989
}
9090
//5u8 => {
@@ -116,13 +116,12 @@ impl Writeable for Event {
116116
payment_hash.write(writer)?;
117117
amount_msat.write(writer)?;
118118
Ok(())
119-
}
120-
//Self::ChannelOpened { .. } => {
119+
} //Self::ChannelOpened { .. } => {
121120
//TODO
122121
//}
123122
Self::ChannelClosed { channel_id } => {
124123
4u8.write(writer)?;
125-
channel_id.write(writer)?;
124+
channel_id.0.write(writer)?;
126125
Ok(())
127126
} //Self::OnChainPaymentSent { .. } => {
128127
//TODO
@@ -486,7 +485,7 @@ impl LdkEventHandler for EventHandler {
486485
reason
487486
);
488487
self.event_queue
489-
.add_event(Event::ChannelClosed { channel_id: *channel_id })
488+
.add_event(Event::ChannelClosed { channel_id: ChannelId(*channel_id) })
490489
.unwrap();
491490
}
492491
LdkEvent::DiscardFunding { .. } => {}

src/lib.rs

+102-11
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)]
@@ -62,7 +61,7 @@ use lightning_persister::FilesystemPersister;
6261
use lightning_net_tokio::SocketDescriptor;
6362

6463
use lightning_invoice::utils::DefaultRouter;
65-
use lightning_invoice::{payment, Currency, Invoice};
64+
use lightning_invoice::{payment, Currency, Invoice, SignedRawInvoice};
6665

6766
use bdk::bitcoin::secp256k1::Secp256k1;
6867
use bdk::blockchain::esplora::EsploraBlockchain;
@@ -72,10 +71,11 @@ use bdk::template::Bip84;
7271
use bitcoin::hashes::sha256::Hash as Sha256;
7372
use bitcoin::hashes::Hash;
7473
use bitcoin::secp256k1::PublicKey;
75-
use bitcoin::BlockHash;
74+
use bitcoin::{Address, BlockHash};
7675

7776
use rand::Rng;
7877

78+
use core::str::FromStr;
7979
use std::collections::HashMap;
8080
use std::convert::TryFrom;
8181
use std::fs;
@@ -84,6 +84,8 @@ use std::sync::atomic::{AtomicBool, Ordering};
8484
use std::sync::{Arc, Mutex, RwLock};
8585
use std::time::{Duration, Instant, SystemTime};
8686

87+
uniffi_macros::include_scaffolding!("ldk_lite");
88+
8789
// The used 'stop gap' parameter used by BDK's wallet sync. This seems to configure the threshold
8890
// number of blocks after which BDK stops looking for scripts belonging to the wallet.
8991
const BDK_CLIENT_STOP_GAP: usize = 20;
@@ -184,7 +186,7 @@ impl Builder {
184186
}
185187

186188
/// Builds an [`LdkLite`] instance according to the options previously configured.
187-
pub fn build(&self) -> LdkLite {
189+
pub fn build(&self) -> Arc<LdkLite> {
188190
let config = Arc::new(self.config.clone());
189191

190192
let ldk_data_dir = format!("{}/ldk", &config.storage_dir_path.clone());
@@ -397,7 +399,7 @@ impl Builder {
397399

398400
let running = RwLock::new(None);
399401

400-
LdkLite {
402+
Arc::new(LdkLite {
401403
running,
402404
config,
403405
chain_access,
@@ -414,7 +416,7 @@ impl Builder {
414416
inbound_payments,
415417
outbound_payments,
416418
peer_store,
417-
}
419+
})
418420
}
419421
}
420422

@@ -453,7 +455,7 @@ impl LdkLite {
453455
/// Starts the necessary background tasks, such as handling events coming from user input,
454456
/// LDK/BDK, and the peer-to-peer network. After this returns, the [`LdkLite`] instance can be
455457
/// controlled via the provided API methods in a thread-safe manner.
456-
pub fn start(&mut self) -> Result<(), Error> {
458+
pub fn start(&self) -> Result<(), Error> {
457459
// Acquire a run lock and hold it until we're setup.
458460
let mut run_lock = self.running.write().unwrap();
459461
if run_lock.is_some() {
@@ -467,7 +469,7 @@ impl LdkLite {
467469
}
468470

469471
/// Disconnects all peers, stops all running background tasks, and shuts down [`LdkLite`].
470-
pub fn stop(&mut self) -> Result<(), Error> {
472+
pub fn stop(&self) -> Result<(), Error> {
471473
let mut run_lock = self.running.write().unwrap();
472474
if run_lock.is_none() {
473475
return Err(Error::NotRunning);
@@ -638,7 +640,7 @@ impl LdkLite {
638640
}
639641

640642
/// Retrieve a new on-chain/funding address.
641-
pub fn new_funding_address(&mut self) -> Result<bitcoin::Address, Error> {
643+
pub fn new_funding_address(&self) -> Result<Address, Error> {
642644
if self.running.read().unwrap().is_none() {
643645
return Err(Error::NotRunning);
644646
}
@@ -1006,5 +1008,94 @@ pub(crate) type NetworkGraph = gossip::NetworkGraph<Arc<FilesystemLogger>>;
10061008

10071009
pub(crate) type PaymentInfoStorage = Mutex<HashMap<PaymentHash, PaymentInfo>>;
10081010

1009-
#[cfg(test)]
1010-
mod tests {}
1011+
impl UniffiCustomTypeConverter for PublicKey {
1012+
type Builtin = String;
1013+
1014+
fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {
1015+
if let Ok(key) = PublicKey::from_str(&val) {
1016+
return Ok(key);
1017+
}
1018+
1019+
Err(Error::PublicKeyInvalid.into())
1020+
}
1021+
1022+
fn from_custom(obj: Self) -> Self::Builtin {
1023+
obj.to_string()
1024+
}
1025+
}
1026+
1027+
impl UniffiCustomTypeConverter for Address {
1028+
type Builtin = String;
1029+
1030+
fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {
1031+
if let Ok(addr) = Address::from_str(&val) {
1032+
return Ok(addr);
1033+
}
1034+
1035+
Err(Error::AddressInvalid.into())
1036+
}
1037+
1038+
fn from_custom(obj: Self) -> Self::Builtin {
1039+
obj.to_string()
1040+
}
1041+
}
1042+
1043+
impl UniffiCustomTypeConverter for Invoice {
1044+
type Builtin = String;
1045+
1046+
fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {
1047+
if let Ok(signed) = val.parse::<SignedRawInvoice>() {
1048+
if let Ok(invoice) = Invoice::from_signed(signed) {
1049+
return Ok(invoice);
1050+
}
1051+
}
1052+
1053+
Err(Error::InvoiceInvalid.into())
1054+
}
1055+
1056+
fn from_custom(obj: Self) -> Self::Builtin {
1057+
obj.to_string()
1058+
}
1059+
}
1060+
1061+
impl UniffiCustomTypeConverter for PaymentHash {
1062+
type Builtin = String;
1063+
1064+
fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {
1065+
if let Ok(hash) = Sha256::from_str(&val) {
1066+
Ok(PaymentHash(hash.into_inner()))
1067+
} else {
1068+
Err(Error::PaymentHashInvalid.into())
1069+
}
1070+
}
1071+
1072+
fn from_custom(obj: Self) -> Self::Builtin {
1073+
Sha256::from_slice(&obj.0).unwrap().to_string()
1074+
}
1075+
}
1076+
1077+
/// The channel identifier as derived from the funding transaction, or the `temporary_channel_id`
1078+
/// if the channel has not been established yet.
1079+
///
1080+
/// See <https://github.com/lightningdevkit/rust-lightning/pull/1790> for more information.
1081+
#[derive(Debug, Clone)]
1082+
pub struct ChannelId([u8; 32]);
1083+
1084+
impl UniffiCustomTypeConverter for ChannelId {
1085+
type Builtin = String;
1086+
1087+
fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {
1088+
if let Some(hex_vec) = hex_utils::to_vec(&val) {
1089+
if hex_vec.len() == 32 {
1090+
let mut channel_id = [0u8; 32];
1091+
channel_id.copy_from_slice(&hex_vec[..]);
1092+
return Ok(Self(channel_id));
1093+
}
1094+
}
1095+
Err(Error::ChannelIdInvalid.into())
1096+
}
1097+
1098+
fn from_custom(obj: Self) -> Self::Builtin {
1099+
hex_utils::to_string(&obj.0)
1100+
}
1101+
}

uniffi/ldk_lite.udl

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
namespace ldk_lite {
2+
};
3+
4+
interface Builder {
5+
constructor();
6+
LdkLite build();
7+
};
8+
9+
interface LdkLite {
10+
[Throws=Error]
11+
void start();
12+
[Throws=Error]
13+
void stop();
14+
Event next_event();
15+
void event_handled();
16+
[Throws=Error]
17+
PublicKey my_node_id();
18+
[Throws=Error]
19+
Address new_funding_address();
20+
[Throws=Error]
21+
void connect_open_channel([ByRef]string node_pubkey_and_address, u64 channel_amount_sats, boolean announce_channel);
22+
[Throws=Error]
23+
PaymentHash send_payment(Invoice invoice);
24+
[Throws=Error]
25+
PaymentHash send_spontaneous_payment(u64 amount_msat, [ByRef]string node_id);
26+
[Throws=Error]
27+
Invoice receive_payment(u64? amount_msat, [ByRef]string description, u32 expiry_secs);
28+
// TODO: payment_info()
29+
};
30+
31+
[Error]
32+
enum Error {
33+
"AlreadyRunning",
34+
"NotRunning",
35+
"FundingTxCreationFailed",
36+
"ConnectionFailed",
37+
"AddressInvalid",
38+
"PublicKeyInvalid",
39+
"PaymentHashInvalid",
40+
"NonUniquePaymentHash",
41+
"InvoiceInvalid",
42+
"InvoiceCreationFailed",
43+
"ChannelIdInvalid",
44+
"RoutingFailed",
45+
"PeerInfoParseFailed",
46+
"ChannelCreationFailed",
47+
"ChannelClosingFailed",
48+
"PersistenceFailed",
49+
"WalletOperationFailed",
50+
"WalletSigningFailed",
51+
"ChainAccessFailed",
52+
};
53+
54+
[Enum]
55+
interface Event {
56+
PaymentSuccessful( PaymentHash payment_hash );
57+
PaymentFailed( PaymentHash payment_hash );
58+
PaymentReceived( PaymentHash payment_hash, u64 amount_msat);
59+
ChannelClosed ( ChannelId channel_id );
60+
};
61+
62+
[Custom]
63+
typedef string PublicKey;
64+
65+
[Custom]
66+
typedef string Address;
67+
68+
[Custom]
69+
typedef string Invoice;
70+
71+
[Custom]
72+
typedef string PaymentHash;
73+
74+
[Custom]
75+
typedef string ChannelId;

uniffi_bindgen_generate.sh

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/bin/bash
2+
uniffi-bindgen generate uniffi/ldk_lite.udl --language swift
3+
uniffi-bindgen generate uniffi/ldk_lite.udl --language python
4+
uniffi-bindgen generate uniffi/ldk_lite.udl --language kotlin

0 commit comments

Comments
 (0)