Skip to content

Commit

Permalink
Generate UniFFI scaffolding
Browse files Browse the repository at this point in the history
We generate the scaffolding from an UDL file and include it in `lib.rs`.
Furthermore, we add a bindings generation shell script for convenience.
  • Loading branch information
tnull committed Oct 21, 2022
1 parent e82b5f7 commit 61f9472
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 23 deletions.
8 changes: 8 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ edition = "2018"

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

[lib]
crate-type = ["staticlib", "cdylib"]
name = "ldk_lite"

[dependencies]
lightning = { version = "0.0.110", features = ["max_level_trace", "std"] }
lightning-invoice = { version = "0.18" }
Expand All @@ -24,7 +28,11 @@ chrono = "0.4"
futures = "0.3"
serde_json = { version = "1.0" }
tokio = { version = "1", features = [ "io-util", "macros", "rt", "rt-multi-thread", "sync", "net", "time" ] }
uniffi = { version = "0.20.0", features = ["builtin-bindgen"] }
uniffi_macros = { version = "0.20.0", features = ["builtin-bindgen"] }

[build-dependencies]
uniffi_build = "0.20.0"

[profile.release]
panic = "abort"
Expand Down
3 changes: 3 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
uniffi_build::generate_scaffolding("uniffi/ldk_lite.udl").unwrap();
}
16 changes: 13 additions & 3 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,20 @@ pub enum Error {
FundingTxCreationFailed,
/// A network connection has been closed.
ConnectionFailed,
/// The given address is invalid.
AddressInvalid,
/// The given public key is invalid.
PublicKeyInvalid,
/// The given payment hash is invalid.
PaymentHashInvalid,
/// Payment of the given invoice has already been intiated.
NonUniquePaymentHash,
/// The given invoice is invalid.
InvoiceInvalid,
/// Invoice creation failed.
InvoiceCreationFailed,
/// The given channel ID is invalid.
ChannelIdInvalid,
/// No route for the given target could be found.
RoutingFailed,
/// A given peer info could not be parsed.
Expand All @@ -41,13 +49,15 @@ impl fmt::Display for Error {
match *self {
Self::AlreadyRunning => write!(f, "LDKLite is already running."),
Self::NotRunning => write!(f, "LDKLite is not running."),
Self::FundingTxCreationFailed => {
write!(f, "Funding transaction could not be created.")
}
Self::FundingTxCreationFailed => write!(f, "Funding transaction could not be created."),
Self::ConnectionFailed => write!(f, "Network connection closed."),
Self::AddressInvalid => write!(f, "The given address is invalid."),
Self::PublicKeyInvalid => write!(f, "The given public key is invalid."),
Self::PaymentHashInvalid => write!(f, "The given payment hash is invalid."),
Self::NonUniquePaymentHash => write!(f, "An invoice must not get payed twice."),
Self::InvoiceInvalid => write!(f, "The given invoice is invalid."),
Self::InvoiceCreationFailed => write!(f, "Failed to create invoice."),
Self::ChannelIdInvalid => write!(f, "The given channel ID is invalid."),
Self::RoutingFailed => write!(f, "Failed to find route."),
Self::PeerInfoParseFailed => write!(f, "Failed to parse the given peer information."),
Self::ChannelCreationFailed => write!(f, "Failed to create channel."),
Expand Down
17 changes: 8 additions & 9 deletions src/event.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
hex_utils, ChainAccess, ChannelManager, Config, Error, FilesystemPersister, NetworkGraph,
PaymentInfo, PaymentInfoStorage, PaymentStatus,
hex_utils, ChainAccess, ChannelId, ChannelManager, Config, Error, FilesystemPersister,
NetworkGraph, PaymentInfo, PaymentInfoStorage, PaymentStatus,
};

use crate::logger::{log_error, log_given_level, log_info, log_internal, FilesystemLogger, Logger};
Expand Down Expand Up @@ -50,10 +50,10 @@ pub enum Event {
// TODO: Implement after a corresponding LDK event is added.
//ChannelOpened {
//},
/// A channel has been closed.
///// A channel has been closed.
ChannelClosed {
/// The channel_id of the channel which has been closed.
channel_id: [u8; 32],
channel_id: ChannelId,
},
// TODO: Implement on-chain events when better integrating with BDK wallet sync.
//OnChainPaymentSent {
Expand Down Expand Up @@ -84,7 +84,7 @@ impl Readable for Event {
// TODO ChannelOpened
//}
4u8 => {
let channel_id: [u8; 32] = Readable::read(reader)?;
let channel_id: ChannelId = ChannelId(Readable::read(reader)?);
Ok(Self::ChannelClosed { channel_id })
}
//5u8 => {
Expand Down Expand Up @@ -116,13 +116,12 @@ impl Writeable for Event {
payment_hash.write(writer)?;
amount_msat.write(writer)?;
Ok(())
}
//Self::ChannelOpened { .. } => {
} //Self::ChannelOpened { .. } => {
//TODO
//}
Self::ChannelClosed { channel_id } => {
4u8.write(writer)?;
channel_id.write(writer)?;
channel_id.0.write(writer)?;
Ok(())
} //Self::OnChainPaymentSent { .. } => {
//TODO
Expand Down Expand Up @@ -486,7 +485,7 @@ impl LdkEventHandler for EventHandler {
reason
);
self.event_queue
.add_event(Event::ChannelClosed { channel_id: *channel_id })
.add_event(Event::ChannelClosed { channel_id: ChannelId(*channel_id) })
.unwrap();
}
LdkEvent::DiscardFunding { .. } => {}
Expand Down
113 changes: 102 additions & 11 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
//! - Wallet and channel states are persisted to disk.
//! - Gossip is retrieved over the P2P network.
#![deny(missing_docs)]
#![deny(broken_intra_doc_links)]
#![deny(private_intra_doc_links)]
#![allow(bare_trait_objects)]
Expand Down Expand Up @@ -62,7 +61,7 @@ use lightning_persister::FilesystemPersister;
use lightning_net_tokio::SocketDescriptor;

use lightning_invoice::utils::DefaultRouter;
use lightning_invoice::{payment, Currency, Invoice};
use lightning_invoice::{payment, Currency, Invoice, SignedRawInvoice};

use bdk::bitcoin::secp256k1::Secp256k1;
use bdk::blockchain::esplora::EsploraBlockchain;
Expand All @@ -72,10 +71,11 @@ use bdk::template::Bip84;
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::Hash;
use bitcoin::secp256k1::PublicKey;
use bitcoin::BlockHash;
use bitcoin::{Address, BlockHash};

use rand::Rng;

use core::str::FromStr;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::fs;
Expand All @@ -84,6 +84,8 @@ use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, RwLock};
use std::time::{Duration, Instant, SystemTime};

uniffi_macros::include_scaffolding!("ldk_lite");

// The used 'stop gap' parameter used by BDK's wallet sync. This seems to configure the threshold
// number of blocks after which BDK stops looking for scripts belonging to the wallet.
const BDK_CLIENT_STOP_GAP: usize = 20;
Expand Down Expand Up @@ -184,7 +186,7 @@ impl Builder {
}

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

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

let running = RwLock::new(None);

LdkLite {
Arc::new(LdkLite {
running,
config,
chain_access,
Expand All @@ -414,7 +416,7 @@ impl Builder {
inbound_payments,
outbound_payments,
peer_store,
}
})
}
}

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

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

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

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

#[cfg(test)]
mod tests {}
impl UniffiCustomTypeConverter for PublicKey {
type Builtin = String;

fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {
if let Ok(key) = PublicKey::from_str(&val) {
return Ok(key);
}

Err(Error::PublicKeyInvalid.into())
}

fn from_custom(obj: Self) -> Self::Builtin {
obj.to_string()
}
}

impl UniffiCustomTypeConverter for Address {
type Builtin = String;

fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {
if let Ok(addr) = Address::from_str(&val) {
return Ok(addr);
}

Err(Error::AddressInvalid.into())
}

fn from_custom(obj: Self) -> Self::Builtin {
obj.to_string()
}
}

impl UniffiCustomTypeConverter for Invoice {
type Builtin = String;

fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {
if let Ok(signed) = val.parse::<SignedRawInvoice>() {
if let Ok(invoice) = Invoice::from_signed(signed) {
return Ok(invoice);
}
}

Err(Error::InvoiceInvalid.into())
}

fn from_custom(obj: Self) -> Self::Builtin {
obj.to_string()
}
}

impl UniffiCustomTypeConverter for PaymentHash {
type Builtin = String;

fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {
if let Ok(hash) = Sha256::from_str(&val) {
Ok(PaymentHash(hash.into_inner()))
} else {
Err(Error::PaymentHashInvalid.into())
}
}

fn from_custom(obj: Self) -> Self::Builtin {
Sha256::from_slice(&obj.0).unwrap().to_string()
}
}

/// The channel identifier as derived from the funding transaction, or the `temporary_channel_id`
/// if the channel has not been established yet.
///
/// See <https://github.com/lightningdevkit/rust-lightning/pull/1790> for more information.
#[derive(Debug, Clone)]
pub struct ChannelId([u8; 32]);

impl UniffiCustomTypeConverter for ChannelId {
type Builtin = String;

fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {
if let Some(hex_vec) = hex_utils::to_vec(&val) {
if hex_vec.len() == 32 {
let mut channel_id = [0u8; 32];
channel_id.copy_from_slice(&hex_vec[..]);
return Ok(Self(channel_id));
}
}
Err(Error::ChannelIdInvalid.into())
}

fn from_custom(obj: Self) -> Self::Builtin {
hex_utils::to_string(&obj.0)
}
}
75 changes: 75 additions & 0 deletions uniffi/ldk_lite.udl
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
namespace ldk_lite {
};

interface Builder {
constructor();
LdkLite build();
};

interface LdkLite {
[Throws=Error]
void start();
[Throws=Error]
void stop();
Event next_event();
void event_handled();
[Throws=Error]
PublicKey my_node_id();
[Throws=Error]
Address new_funding_address();
[Throws=Error]
void connect_open_channel([ByRef]string node_pubkey_and_address, u64 channel_amount_sats, boolean announce_channel);
[Throws=Error]
PaymentHash send_payment(Invoice invoice);
[Throws=Error]
PaymentHash send_spontaneous_payment(u64 amount_msat, [ByRef]string node_id);
[Throws=Error]
Invoice receive_payment(u64? amount_msat, [ByRef]string description, u32 expiry_secs);
// TODO: payment_info()
};

[Error]
enum Error {
"AlreadyRunning",
"NotRunning",
"FundingTxCreationFailed",
"ConnectionFailed",
"AddressInvalid",
"PublicKeyInvalid",
"PaymentHashInvalid",
"NonUniquePaymentHash",
"InvoiceInvalid",
"InvoiceCreationFailed",
"ChannelIdInvalid",
"RoutingFailed",
"PeerInfoParseFailed",
"ChannelCreationFailed",
"ChannelClosingFailed",
"PersistenceFailed",
"WalletOperationFailed",
"WalletSigningFailed",
"ChainAccessFailed",
};

[Enum]
interface Event {
PaymentSuccessful( PaymentHash payment_hash );
PaymentFailed( PaymentHash payment_hash );
PaymentReceived( PaymentHash payment_hash, u64 amount_msat);
ChannelClosed ( ChannelId channel_id );
};

[Custom]
typedef string PublicKey;

[Custom]
typedef string Address;

[Custom]
typedef string Invoice;

[Custom]
typedef string PaymentHash;

[Custom]
typedef string ChannelId;
4 changes: 4 additions & 0 deletions uniffi_bindgen_generate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash
uniffi-bindgen generate uniffi/ldk_lite.udl --language swift
uniffi-bindgen generate uniffi/ldk_lite.udl --language python
uniffi-bindgen generate uniffi/ldk_lite.udl --language kotlin

0 comments on commit 61f9472

Please sign in to comment.