Skip to content
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ log = { version = "0.4.22", default-features = false, features = ["std"]}

vss-client = "0.3"
prost = { version = "0.11.6", default-features = false}
#bitcoin-payment-instructions = { version = "0.5" }
bitcoin-payment-instructions = { git = "https://github.com/chuksys/bitcoin-payment-instructions", branch = "bump-ldk-deps" }

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["winbase"] }
Expand Down Expand Up @@ -146,4 +148,4 @@ check-cfg = [
"cfg(tokio_unstable)",
"cfg(cln_test)",
"cfg(lnd_test)",
]
]
20 changes: 14 additions & 6 deletions bindings/ldk_node.udl
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ interface Node {
Bolt12Payment bolt12_payment();
SpontaneousPayment spontaneous_payment();
OnchainPayment onchain_payment();
UnifiedQrPayment unified_qr_payment();
UnifiedPayment unified_payment();
LSPS1Liquidity lsps1_liquidity();
[Throws=NodeError]
void connect(PublicKey node_id, SocketAddress address, boolean persist);
Expand Down Expand Up @@ -202,7 +202,7 @@ interface Bolt12Payment {
[Throws=NodeError]
PaymentId send([ByRef]Offer offer, u64? quantity, string? payer_note);
[Throws=NodeError]
PaymentId send_using_amount([ByRef]Offer offer, u64 amount_msat, u64? quantity, string? payer_note);
PaymentId send_using_amount([ByRef]Offer offer, u64 amount_msat, u64? quantity, string? payer_note, HumanReadableName? hrn);
[Throws=NodeError]
Offer receive(u64 amount_msat, [ByRef]string description, u32? expiry_secs, u64? quantity);
[Throws=NodeError]
Expand Down Expand Up @@ -251,11 +251,11 @@ interface FeeRate {
u64 to_sat_per_vb_ceil();
};

interface UnifiedQrPayment {
interface UnifiedPayment {
[Throws=NodeError]
string receive(u64 amount_sats, [ByRef]string message, u32 expiry_sec);
[Throws=NodeError]
QrPaymentResult send([ByRef]string uri_str);
[Throws=NodeError, Async]
UnifiedPaymentResult send([ByRef]string uri_str, u64? amount_msat);
};

interface LSPS1Liquidity {
Expand Down Expand Up @@ -321,6 +321,7 @@ enum NodeError {
"LiquidityFeeTooHigh",
"InvalidBlindedPaths",
"AsyncPaymentServicesDisabled",
"HrnParsingFailed",
};

dictionary NodeStatus {
Expand Down Expand Up @@ -429,7 +430,7 @@ interface PaymentKind {
};

[Enum]
interface QrPaymentResult {
interface UnifiedPaymentResult {
Onchain(Txid txid);
Bolt11(PaymentId payment_id);
Bolt12(PaymentId payment_id);
Expand Down Expand Up @@ -781,6 +782,13 @@ interface Offer {
PublicKey? issuer_signing_pubkey();
};

interface HumanReadableName {
[Throws=NodeError, Name=from_encoded]
constructor([ByRef] string encoded);
string user();
string domain();
};

[Traits=(Debug, Display, Eq)]
interface Refund {
[Throws=NodeError, Name=from_str]
Expand Down
14 changes: 12 additions & 2 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use bip39::Mnemonic;
use bitcoin::bip32::{ChildNumber, Xpriv};
use bitcoin::secp256k1::PublicKey;
use bitcoin::{BlockHash, Network};
use bitcoin_payment_instructions::onion_message_resolver::LDKOnionMessageDNSSECHrnResolver;
use lightning::chain::{chainmonitor, BestBlock, Watch};
use lightning::io::Cursor;
use lightning::ln::channelmanager::{self, ChainParameters, ChannelManagerReadArgs};
Expand Down Expand Up @@ -1490,6 +1491,8 @@ fn build_with_store_internal(
})?;
}

let hrn_resolver = Arc::new(LDKOnionMessageDNSSECHrnResolver::new(Arc::clone(&network_graph)));

// Initialize the PeerManager
let onion_messenger: Arc<OnionMessenger> =
if let Some(AsyncPaymentsRole::Server) = async_payments_role {
Expand All @@ -1501,7 +1504,7 @@ fn build_with_store_internal(
message_router,
Arc::clone(&channel_manager),
Arc::clone(&channel_manager),
IgnoringMessageHandler {},
Arc::clone(&hrn_resolver),
IgnoringMessageHandler {},
))
} else {
Expand All @@ -1513,7 +1516,7 @@ fn build_with_store_internal(
message_router,
Arc::clone(&channel_manager),
Arc::clone(&channel_manager),
IgnoringMessageHandler {},
Arc::clone(&hrn_resolver),
IgnoringMessageHandler {},
))
};
Expand Down Expand Up @@ -1645,6 +1648,12 @@ fn build_with_store_internal(
Arc::clone(&keys_manager),
));

let peer_manager_clone = Arc::clone(&peer_manager);

hrn_resolver.register_post_queue_action(Box::new(move || {
peer_manager_clone.process_events();
}));

liquidity_source.as_ref().map(|l| l.set_peer_manager(Arc::clone(&peer_manager)));

gossip_source.set_gossip_verifier(
Expand Down Expand Up @@ -1745,6 +1754,7 @@ fn build_with_store_internal(
node_metrics,
om_mailbox,
async_payments_role,
hrn_resolver,
})
}

Expand Down
5 changes: 5 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ pub enum Error {
InvalidBlindedPaths,
/// Asynchronous payment services are disabled.
AsyncPaymentServicesDisabled,
/// Parsing a Human-Readable Name has failed.
HrnParsingFailed,
}

impl fmt::Display for Error {
Expand Down Expand Up @@ -202,6 +204,9 @@ impl fmt::Display for Error {
Self::AsyncPaymentServicesDisabled => {
write!(f, "Asynchronous payment services are disabled.")
},
Self::HrnParsingFailed => {
write!(f, "Failed to parse a human-readable name.")
},
}
}
}
Expand Down
57 changes: 56 additions & 1 deletion src/ffi/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ pub use crate::logger::{LogLevel, LogRecord, LogWriter};
pub use crate::payment::store::{
ConfirmationStatus, LSPFeeLimits, PaymentDirection, PaymentKind, PaymentStatus,
};
pub use crate::payment::QrPaymentResult;
pub use crate::payment::UnifiedPaymentResult;

pub use lightning::onion_message::dns_resolution::HumanReadableName as LdkHumanReadableName;

use crate::{hex_utils, SocketAddress, UniffiCustomTypeConverter, UserChannelId};

impl UniffiCustomTypeConverter for PublicKey {
Expand Down Expand Up @@ -267,6 +270,58 @@ impl std::fmt::Display for Offer {
}
}

pub struct HumanReadableName {
pub(crate) inner: LdkHumanReadableName,
}

impl HumanReadableName {
pub fn into_inner(&self) -> LdkHumanReadableName {
self.inner.clone()
}

pub fn from_encoded(encoded: &str) -> Result<Self, Error> {
let hrn = match LdkHumanReadableName::from_encoded(encoded) {
Ok(hrn) => Ok(hrn),
Err(_) => Err(Error::HrnParsingFailed),
}?;

Ok(Self { inner: hrn })
}

pub fn user(&self) -> String {
self.inner.user().to_string()
}

pub fn domain(&self) -> String {
self.inner.domain().to_string()
}
}

impl From<LdkHumanReadableName> for HumanReadableName {
fn from(ldk_hrn: LdkHumanReadableName) -> Self {
HumanReadableName { inner: ldk_hrn }
}
}

impl From<HumanReadableName> for LdkHumanReadableName {
fn from(wrapper: HumanReadableName) -> Self {
wrapper.into_inner()
}
}

impl Deref for HumanReadableName {
type Target = LdkHumanReadableName;
fn deref(&self) -> &Self::Target {
&self.inner
}
}

impl AsRef<LdkHumanReadableName> for HumanReadableName {
fn as_ref(&self) -> &LdkHumanReadableName {
self.deref()
}
}

/// A `Refund` is a request to send an [`Bolt12Invoice`] without a preceding [`Offer`].
///
/// Typically, after an invoice is paid, the recipient may publish a refund allowing the sender to
Expand Down
48 changes: 29 additions & 19 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,33 +127,34 @@ use gossip::GossipSource;
use graph::NetworkGraph;
pub use io::utils::generate_entropy_mnemonic;
use io::utils::write_node_metrics;
use lightning::chain::BestBlock;
use lightning::events::bump_transaction::Wallet as LdkWallet;
use lightning::impl_writeable_tlv_based;
use lightning::ln::channel_state::ChannelShutdownState;
use lightning::ln::channelmanager::PaymentId;
use lightning::ln::msgs::SocketAddress;
use lightning::routing::gossip::NodeAlias;
use lightning::util::persist::KVStoreSync;
use lightning_background_processor::process_events_async;
use liquidity::{LSPS1Liquidity, LiquiditySource};
use logger::{log_debug, log_error, log_info, log_trace, LdkLogger, Logger};
use payment::asynchronous::om_mailbox::OnionMessageMailbox;
use payment::asynchronous::static_invoice_store::StaticInvoiceStore;
use payment::{
Bolt11Payment, Bolt12Payment, OnchainPayment, PaymentDetails, SpontaneousPayment,
UnifiedQrPayment,
UnifiedPayment,
};
use peer_store::{PeerInfo, PeerStore};
use rand::Rng;
use runtime::Runtime;
use types::{
Broadcaster, BumpTransactionEventHandler, ChainMonitor, ChannelManager, Graph, KeysManager,
OnionMessenger, PaymentStore, PeerManager, Router, Scorer, Sweeper, Wallet,
Broadcaster, BumpTransactionEventHandler, ChainMonitor, ChannelManager, Graph, HRNResolver,
KeysManager, OnionMessenger, PaymentStore, PeerManager, Router, Scorer, Sweeper, Wallet,
};
pub use types::{
ChannelDetails, CustomTlvRecord, DynStore, PeerDetails, SyncAndAsyncKVStore, UserChannelId,
};

use lightning::chain::BestBlock;
use lightning::events::bump_transaction::Wallet as LdkWallet;
use lightning::impl_writeable_tlv_based;
use lightning::ln::channel_state::ChannelShutdownState;
use lightning::ln::channelmanager::PaymentId;
use lightning::ln::msgs::SocketAddress;
use lightning::routing::gossip::NodeAlias;
use lightning::util::persist::KVStoreSync;
use lightning_background_processor::process_events_async;
use logger::{log_debug, log_error, log_info, log_trace, LdkLogger, Logger};
use payment::asynchronous::om_mailbox::OnionMessageMailbox;
use rand::Rng;
pub use {
bip39, bitcoin, lightning, lightning_invoice, lightning_liquidity, lightning_types, tokio,
vss_client,
Expand Down Expand Up @@ -194,6 +195,7 @@ pub struct Node {
node_metrics: Arc<RwLock<NodeMetrics>>,
om_mailbox: Option<Arc<OnionMessageMailbox>>,
async_payments_role: Option<AsyncPaymentsRole>,
hrn_resolver: Arc<HRNResolver>,
}

impl Node {
Expand Down Expand Up @@ -904,34 +906,42 @@ impl Node {
/// Returns a payment handler allowing to create [BIP 21] URIs with an on-chain, [BOLT 11],
/// and [BOLT 12] payment options.
///
/// This handler allows you to send payments to these URIs as well as [BIP 353] HRNs.
///
/// [BOLT 11]: https://github.com/lightning/bolts/blob/master/11-payment-encoding.md
/// [BOLT 12]: https://github.com/lightning/bolts/blob/master/12-offer-encoding.md
/// [BIP 21]: https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki
/// [BIP 353]: https://github.com/bitcoin/bips/blob/master/bip-0353.mediawiki
#[cfg(not(feature = "uniffi"))]
pub fn unified_qr_payment(&self) -> UnifiedQrPayment {
UnifiedQrPayment::new(
pub fn unified_payment(&self) -> UnifiedPayment {
UnifiedPayment::new(
self.onchain_payment().into(),
self.bolt11_payment().into(),
self.bolt12_payment().into(),
Arc::clone(&self.config),
Arc::clone(&self.logger),
Arc::clone(&self.hrn_resolver),
)
}

/// Returns a payment handler allowing to create [BIP 21] URIs with an on-chain, [BOLT 11],
/// and [BOLT 12] payment options.
///
/// This handler allows you to send payments to these URIs as well as [BIP 353] HRNs.
///
/// [BOLT 11]: https://github.com/lightning/bolts/blob/master/11-payment-encoding.md
/// [BOLT 12]: https://github.com/lightning/bolts/blob/master/12-offer-encoding.md
/// [BIP 21]: https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki
/// [BIP 353]: https://github.com/bitcoin/bips/blob/master/bip-0353.mediawiki
#[cfg(feature = "uniffi")]
pub fn unified_qr_payment(&self) -> Arc<UnifiedQrPayment> {
Arc::new(UnifiedQrPayment::new(
pub fn unified_payment(&self) -> Arc<UnifiedPayment> {
Arc::new(UnifiedPayment::new(
self.onchain_payment(),
self.bolt11_payment(),
self.bolt12_payment(),
Arc::clone(&self.config),
Arc::clone(&self.logger),
Arc::clone(&self.hrn_resolver),
))
}

Expand Down
14 changes: 12 additions & 2 deletions src/payment/bolt12.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH};

use lightning::blinded_path::message::BlindedMessagePath;
use lightning::ln::channelmanager::{OptionalOfferPaymentParams, PaymentId, Retry};
use lightning::offers::offer::{Amount, Offer as LdkOffer, Quantity};
use lightning::offers::offer::{Amount, Offer as LdkOffer, OfferFromHrn, Quantity};
use lightning::offers::parse::Bolt12SemanticError;
use lightning::routing::router::RouteParametersConfig;
#[cfg(feature = "uniffi")]
Expand Down Expand Up @@ -45,6 +45,11 @@ type Refund = lightning::offers::refund::Refund;
#[cfg(feature = "uniffi")]
type Refund = Arc<crate::ffi::Refund>;

#[cfg(not(feature = "uniffi"))]
type HumanReadableName = lightning::onion_message::dns_resolution::HumanReadableName;
#[cfg(feature = "uniffi")]
type HumanReadableName = Arc<crate::ffi::HumanReadableName>;

/// A payment handler allowing to create and pay [BOLT 12] offers and refunds.
///
/// Should be retrieved by calling [`Node::bolt12_payment`].
Expand Down Expand Up @@ -183,6 +188,7 @@ impl Bolt12Payment {
/// response.
pub fn send_using_amount(
&self, offer: &Offer, amount_msat: u64, quantity: Option<u64>, payer_note: Option<String>,
hrn: Option<HumanReadableName>,
) -> Result<PaymentId, Error> {
if !*self.is_running.read().unwrap() {
return Err(Error::NotRunning);
Expand Down Expand Up @@ -217,7 +223,11 @@ impl Bolt12Payment {
retry_strategy,
route_params_config,
};
let res = if let Some(quantity) = quantity {
let res = if let Some(hrn) = hrn {
let hrn = maybe_deref(&hrn);
let offer = OfferFromHrn { offer: offer.clone(), hrn: *hrn };
self.channel_manager.pay_for_offer_from_hrn(&offer, amount_msat, payment_id, params)
} else if let Some(quantity) = quantity {
self.channel_manager.pay_for_offer_with_quantity(
&offer,
Some(amount_msat),
Expand Down
4 changes: 2 additions & 2 deletions src/payment/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ mod bolt12;
mod onchain;
mod spontaneous;
pub(crate) mod store;
mod unified_qr;
mod unified;

pub use bolt11::Bolt11Payment;
pub use bolt12::Bolt12Payment;
Expand All @@ -22,4 +22,4 @@ pub use spontaneous::SpontaneousPayment;
pub use store::{
ConfirmationStatus, LSPFeeLimits, PaymentDetails, PaymentDirection, PaymentKind, PaymentStatus,
};
pub use unified_qr::{QrPaymentResult, UnifiedQrPayment};
pub use unified::{UnifiedPayment, UnifiedPaymentResult};
Loading
Loading