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 Jan 11, 2023
1 parent 303e0ee commit 8481816
Show file tree
Hide file tree
Showing 8 changed files with 261 additions and 37 deletions.
9 changes: 9 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ description = "A ready-to-go node implementation based on LDK."

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

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

[dependencies]
#lightning = { version = "0.0.112", features = ["max_level_trace", "std"] }
#lightning-invoice = { version = "0.20" }
Expand Down Expand Up @@ -47,12 +51,17 @@ chrono = "0.4"
futures = "0.3"
serde_json = { version = "1.0" }
tokio = { version = "1", features = [ "full" ] }
uniffi = { version = "0.21.0", features = ["builtin-bindgen"] }
uniffi_macros = { version = "0.21.0", features = ["builtin-bindgen"] }

[dev-dependencies]
electrsd = { version = "0.22.0", features = ["legacy", "esplora_a33e97e1", "bitcoind_23_0"] }
electrum-client = "0.12.0"
once_cell = "1.16.0"

[build-dependencies]
uniffi_build = "0.21.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_node.udl").unwrap();
}
16 changes: 13 additions & 3 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,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 @@ -40,13 +48,15 @@ impl fmt::Display for Error {
match *self {
Self::AlreadyRunning => write!(f, "Node is already running."),
Self::NotRunning => write!(f, "Node 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
43 changes: 26 additions & 17 deletions src/event.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
hex_utils, ChannelManager, Config, Error, KeysManager, NetworkGraph, PaymentInfo,
PaymentInfoStorage, PaymentStatus, Wallet,
hex_utils, ChannelId, ChannelManager, Config, Error, KeysManager, NetworkGraph, PaymentInfo,
PaymentInfoStorage, PaymentStatus, UserChannelId, Wallet,
};

use crate::logger::{log_error, log_given_level, log_info, log_internal, Logger};
Expand Down Expand Up @@ -50,16 +50,16 @@ pub enum Event {
/// A channel is ready to be used.
ChannelReady {
/// The `channel_id` of the channel.
channel_id: [u8; 32],
channel_id: ChannelId,
/// The `user_channel_id` of the channel.
user_channel_id: u128,
user_channel_id: UserChannelId,
},
/// A channel has been closed.
ChannelClosed {
/// The `channel_id` of the channel.
channel_id: [u8; 32],
channel_id: ChannelId,
/// The `user_channel_id` of the channel.
user_channel_id: u128,
user_channel_id: UserChannelId,
},
}

Expand All @@ -83,13 +83,13 @@ impl Readable for Event {
Ok(Self::PaymentReceived { payment_hash, amount_msat })
}
3u8 => {
let channel_id: [u8; 32] = Readable::read(reader)?;
let user_channel_id: u128 = Readable::read(reader)?;
let channel_id = ChannelId(Readable::read(reader)?);
let user_channel_id = UserChannelId(Readable::read(reader)?);
Ok(Self::ChannelReady { channel_id, user_channel_id })
}
4u8 => {
let channel_id: [u8; 32] = Readable::read(reader)?;
let user_channel_id: u128 = Readable::read(reader)?;
let channel_id = ChannelId(Readable::read(reader)?);
let user_channel_id = UserChannelId(Readable::read(reader)?);
Ok(Self::ChannelClosed { channel_id, user_channel_id })
}
_ => Err(lightning::ln::msgs::DecodeError::InvalidValue),
Expand Down Expand Up @@ -118,14 +118,14 @@ impl Writeable for Event {
}
Self::ChannelReady { channel_id, user_channel_id } => {
3u8.write(writer)?;
channel_id.write(writer)?;
user_channel_id.write(writer)?;
channel_id.0.write(writer)?;
user_channel_id.0.write(writer)?;
Ok(())
}
Self::ChannelClosed { channel_id, user_channel_id } => {
4u8.write(writer)?;
channel_id.write(writer)?;
user_channel_id.write(writer)?;
channel_id.0.write(writer)?;
user_channel_id.0.write(writer)?;
Ok(())
}
}
Expand Down Expand Up @@ -576,7 +576,10 @@ where
counterparty_node_id,
);
self.event_queue
.add_event(Event::ChannelReady { channel_id, user_channel_id })
.add_event(Event::ChannelReady {
channel_id: ChannelId(channel_id),
user_channel_id: UserChannelId(user_channel_id),
})
.expect("Failed to push to event queue");
}
LdkEvent::ChannelClosed { channel_id, reason, user_channel_id } => {
Expand All @@ -587,7 +590,10 @@ where
reason
);
self.event_queue
.add_event(Event::ChannelClosed { channel_id, user_channel_id })
.add_event(Event::ChannelClosed {
channel_id: ChannelId(channel_id),
user_channel_id: UserChannelId(user_channel_id),
})
.expect("Failed to push to event queue");
}
LdkEvent::DiscardFunding { .. } => {}
Expand All @@ -606,7 +612,10 @@ mod tests {
let test_persister = Arc::new(TestPersister::new());
let event_queue = EventQueue::new(Arc::clone(&test_persister));

let expected_event = Event::ChannelReady { channel_id: [23u8; 32], user_channel_id: 2323 };
let expected_event = Event::ChannelReady {
channel_id: ChannelId([23u8; 32]),
user_channel_id: UserChannelId(2323),
};
event_queue.add_event(expected_event.clone()).unwrap();
assert!(test_persister.get_and_clear_pending_persist());

Expand Down
130 changes: 118 additions & 12 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 All @@ -34,7 +33,8 @@ mod peer_store;
mod tests;
mod wallet;

pub use error::Error;
pub use error::Error as NodeError;
use error::Error;
pub use event::Event;
use event::{EventHandler, EventQueue};
use peer_store::{PeerInfo, PeerInfoStorage};
Expand Down Expand Up @@ -64,7 +64,7 @@ use lightning_transaction_sync::EsploraSyncClient;
use lightning_net_tokio::SocketDescriptor;

use lightning::routing::router::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 @@ -74,10 +74,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, TryInto};
use std::default::Default;
Expand All @@ -87,6 +88,8 @@ use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, RwLock};
use std::time::{Duration, Instant, SystemTime};

uniffi_macros::include_scaffolding!("ldk_node");

// 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 @@ -193,8 +196,8 @@ impl Builder {
self
}

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

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

let running = RwLock::new(None);

Node {
Arc::new(Node {
running,
config,
wallet,
Expand All @@ -446,7 +449,7 @@ impl Builder {
inbound_payments,
outbound_payments,
peer_store,
}
})
}
}

Expand Down Expand Up @@ -488,7 +491,7 @@ impl Node {
/// 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 [`Node`] 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 @@ -502,7 +505,7 @@ impl Node {
}

/// Disconnects all peers, stops all running background tasks, and shuts down [`Node`].
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 @@ -696,15 +699,15 @@ impl Node {
}

/// 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> {
let funding_address = self.wallet.get_new_address()?;
log_info!(self.logger, "Generated new funding address: {}", funding_address);
Ok(funding_address)
}

#[cfg(test)]
/// Retrieve the current on-chain balance.
pub fn on_chain_balance(&mut self) -> Result<bdk::Balance, Error> {
pub fn on_chain_balance(&self) -> Result<bdk::Balance, Error> {
self.wallet.get_balance()
}

Expand Down Expand Up @@ -1100,3 +1103,106 @@ pub(crate) type OnionMessenger = lightning::onion_message::OnionMessenger<
Arc<FilesystemLogger>,
IgnoringMessageHandler,
>;

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()
}
}

#[derive(Debug, Clone, PartialEq)]
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)
}
}

#[derive(Debug, Clone, PartialEq)]
pub struct UserChannelId(u128);

impl UniffiCustomTypeConverter for UserChannelId {
type Builtin = String;

fn into_custom(val: Self::Builtin) -> uniffi::Result<Self> {
Ok(UserChannelId(u128::from_str(&val).map_err(|_| Error::ChannelIdInvalid)?))
}

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

0 comments on commit 8481816

Please sign in to comment.