//! Convenient utilities to create an invoice.
use crate::{CreationError, Currency, Invoice, InvoiceBuilder, SignOrCreationError};
-use crate::payment::{Payer, ScoringRouter};
+use crate::payment::Payer;
use crate::{prelude::*, Description, InvoiceDescription, Sha256};
use bech32::ToBase32;
-use bitcoin_hashes::{Hash, sha256};
+use bitcoin_hashes::Hash;
use lightning::chain;
use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
-use lightning::chain::keysinterface::{Recipient, KeysInterface};
+use lightning::chain::keysinterface::{Recipient, KeysInterface, NodeSigner, SignerProvider};
use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
use lightning::ln::channelmanager::{ChannelDetails, ChannelManager, PaymentId, PaymentSendFailure, MIN_FINAL_CLTV_EXPIRY};
#[cfg(feature = "std")]
use lightning::ln::channelmanager::{PhantomRouteHints, MIN_CLTV_EXPIRY_DELTA};
use lightning::ln::inbound_payment::{create, create_from_hash, ExpandedKey};
-use lightning::ln::msgs::LightningError;
-use lightning::routing::gossip::{NetworkGraph, NodeId, RoutingFees};
-use lightning::routing::router::{InFlightHtlcs, Route, RouteHint, RouteHintHop, RouteParameters, find_route, RouteHop, Router};
-use lightning::routing::scoring::{ChannelUsage, LockableScore, Score};
+use lightning::routing::gossip::RoutingFees;
+use lightning::routing::router::{InFlightHtlcs, Route, RouteHint, RouteHintHop, Router};
use lightning::util::logger::Logger;
use secp256k1::PublicKey;
use core::ops::Deref;
use core::time::Duration;
-use crate::sync::Mutex;
#[cfg(feature = "std")]
/// Utility to create an invoice that can be paid to one of multiple nodes, or a "phantom invoice."
///
/// `invoice_expiry_delta_secs` describes the number of seconds that the invoice is valid for
/// in excess of the current time.
-pub fn create_invoice_from_channelmanager<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>(
- channelmanager: &ChannelManager<M, T, K, F, L>, keys_manager: K, logger: L,
+pub fn create_invoice_from_channelmanager<M: Deref, T: Deref, K: Deref, F: Deref, R: Deref, L: Deref>(
+ channelmanager: &ChannelManager<M, T, K, F, R, L>, keys_manager: K, logger: L,
network: Currency, amt_msat: Option<u64>, description: String, invoice_expiry_delta_secs: u32
) -> Result<Invoice, SignOrCreationError<()>>
where
- M::Target: chain::Watch<<K::Target as KeysInterface>::Signer>,
+ M::Target: chain::Watch<<K::Target as SignerProvider>::Signer>,
T::Target: BroadcasterInterface,
K::Target: KeysInterface,
F::Target: FeeEstimator,
+ R::Target: Router,
L::Target: Logger,
{
use std::time::SystemTime;
///
/// `invoice_expiry_delta_secs` describes the number of seconds that the invoice is valid for
/// in excess of the current time.
-pub fn create_invoice_from_channelmanager_with_description_hash<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>(
- channelmanager: &ChannelManager<M, T, K, F, L>, keys_manager: K, logger: L,
+pub fn create_invoice_from_channelmanager_with_description_hash<M: Deref, T: Deref, K: Deref, F: Deref, R: Deref, L: Deref>(
+ channelmanager: &ChannelManager<M, T, K, F, R, L>, keys_manager: K, logger: L,
network: Currency, amt_msat: Option<u64>, description_hash: Sha256,
invoice_expiry_delta_secs: u32
) -> Result<Invoice, SignOrCreationError<()>>
where
- M::Target: chain::Watch<<K::Target as KeysInterface>::Signer>,
+ M::Target: chain::Watch<<K::Target as SignerProvider>::Signer>,
T::Target: BroadcasterInterface,
K::Target: KeysInterface,
F::Target: FeeEstimator,
+ R::Target: Router,
L::Target: Logger,
{
use std::time::SystemTime;
/// See [`create_invoice_from_channelmanager_with_description_hash`]
/// This version can be used in a `no_std` environment, where [`std::time::SystemTime`] is not
/// available and the current time is supplied by the caller.
-pub fn create_invoice_from_channelmanager_with_description_hash_and_duration_since_epoch<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>(
- channelmanager: &ChannelManager<M, T, K, F, L>, keys_manager: K, logger: L,
+pub fn create_invoice_from_channelmanager_with_description_hash_and_duration_since_epoch<M: Deref, T: Deref, K: Deref, F: Deref, R: Deref, L: Deref>(
+ channelmanager: &ChannelManager<M, T, K, F, R, L>, keys_manager: K, logger: L,
network: Currency, amt_msat: Option<u64>, description_hash: Sha256,
duration_since_epoch: Duration, invoice_expiry_delta_secs: u32
) -> Result<Invoice, SignOrCreationError<()>>
-where
- M::Target: chain::Watch<<K::Target as KeysInterface>::Signer>,
- T::Target: BroadcasterInterface,
- K::Target: KeysInterface,
- F::Target: FeeEstimator,
- L::Target: Logger,
+ where
+ M::Target: chain::Watch<<K::Target as SignerProvider>::Signer>,
+ T::Target: BroadcasterInterface,
+ K::Target: KeysInterface,
+ F::Target: FeeEstimator,
+ R::Target: Router,
+ L::Target: Logger,
{
_create_invoice_from_channelmanager_and_duration_since_epoch(
channelmanager, keys_manager, logger, network, amt_msat,
/// See [`create_invoice_from_channelmanager`]
/// This version can be used in a `no_std` environment, where [`std::time::SystemTime`] is not
/// available and the current time is supplied by the caller.
-pub fn create_invoice_from_channelmanager_and_duration_since_epoch<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>(
- channelmanager: &ChannelManager<M, T, K, F, L>, keys_manager: K, logger: L,
+pub fn create_invoice_from_channelmanager_and_duration_since_epoch<M: Deref, T: Deref, K: Deref, F: Deref, R: Deref, L: Deref>(
+ channelmanager: &ChannelManager<M, T, K, F, R, L>, keys_manager: K, logger: L,
network: Currency, amt_msat: Option<u64>, description: String, duration_since_epoch: Duration,
invoice_expiry_delta_secs: u32
) -> Result<Invoice, SignOrCreationError<()>>
-where
- M::Target: chain::Watch<<K::Target as KeysInterface>::Signer>,
- T::Target: BroadcasterInterface,
- K::Target: KeysInterface,
- F::Target: FeeEstimator,
- L::Target: Logger,
+ where
+ M::Target: chain::Watch<<K::Target as SignerProvider>::Signer>,
+ T::Target: BroadcasterInterface,
+ K::Target: KeysInterface,
+ F::Target: FeeEstimator,
+ R::Target: Router,
+ L::Target: Logger,
{
_create_invoice_from_channelmanager_and_duration_since_epoch(
channelmanager, keys_manager, logger, network, amt_msat,
)
}
-fn _create_invoice_from_channelmanager_and_duration_since_epoch<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>(
- channelmanager: &ChannelManager<M, T, K, F, L>, keys_manager: K, logger: L,
+fn _create_invoice_from_channelmanager_and_duration_since_epoch<M: Deref, T: Deref, K: Deref, F: Deref, R: Deref, L: Deref>(
+ channelmanager: &ChannelManager<M, T, K, F, R, L>, keys_manager: K, logger: L,
network: Currency, amt_msat: Option<u64>, description: InvoiceDescription,
duration_since_epoch: Duration, invoice_expiry_delta_secs: u32
) -> Result<Invoice, SignOrCreationError<()>>
-where
- M::Target: chain::Watch<<K::Target as KeysInterface>::Signer>,
- T::Target: BroadcasterInterface,
- K::Target: KeysInterface,
- F::Target: FeeEstimator,
- L::Target: Logger,
+ where
+ M::Target: chain::Watch<<K::Target as SignerProvider>::Signer>,
+ T::Target: BroadcasterInterface,
+ K::Target: KeysInterface,
+ F::Target: FeeEstimator,
+ R::Target: Router,
+ L::Target: Logger,
{
// `create_inbound_payment` only returns an error if the amount is greater than the total bitcoin
// supply.
let (payment_hash, payment_secret) = channelmanager
.create_inbound_payment(amt_msat, invoice_expiry_delta_secs)
.map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?;
+ _create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_hash(
+ channelmanager, keys_manager, logger, network, amt_msat, description, duration_since_epoch, invoice_expiry_delta_secs, payment_hash, payment_secret)
+}
+
+/// See [`create_invoice_from_channelmanager_and_duration_since_epoch`]
+/// This version allows for providing a custom [`PaymentHash`] for the invoice.
+/// This may be useful if you're building an on-chain swap or involving another protocol where
+/// the payment hash is also involved outside the scope of lightning.
+pub fn create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_hash<M: Deref, T: Deref, K: Deref, F: Deref, R: Deref, L: Deref>(
+ channelmanager: &ChannelManager<M, T, K, F, R, L>, keys_manager: K, logger: L,
+ network: Currency, amt_msat: Option<u64>, description: String, duration_since_epoch: Duration,
+ invoice_expiry_delta_secs: u32, payment_hash: PaymentHash
+) -> Result<Invoice, SignOrCreationError<()>>
+ where
+ M::Target: chain::Watch<<K::Target as SignerProvider>::Signer>,
+ T::Target: BroadcasterInterface,
+ K::Target: KeysInterface,
+ F::Target: FeeEstimator,
+ R::Target: Router,
+ L::Target: Logger,
+{
+ let payment_secret = channelmanager
+ .create_inbound_payment_for_hash(payment_hash,amt_msat, invoice_expiry_delta_secs)
+ .map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?;
+ _create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_hash(
+ channelmanager, keys_manager, logger, network, amt_msat,
+ InvoiceDescription::Direct(
+ &Description::new(description).map_err(SignOrCreationError::CreationError)?,
+ ),
+ duration_since_epoch, invoice_expiry_delta_secs, payment_hash, payment_secret
+ )
+}
+
+fn _create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_hash<M: Deref, T: Deref, K: Deref, F: Deref, R: Deref, L: Deref>(
+ channelmanager: &ChannelManager<M, T, K, F, R, L>, keys_manager: K, logger: L,
+ network: Currency, amt_msat: Option<u64>, description: InvoiceDescription, duration_since_epoch: Duration,
+ invoice_expiry_delta_secs: u32, payment_hash: PaymentHash, payment_secret: PaymentSecret
+) -> Result<Invoice, SignOrCreationError<()>>
+ where
+ M::Target: chain::Watch<<K::Target as SignerProvider>::Signer>,
+ T::Target: BroadcasterInterface,
+ K::Target: KeysInterface,
+ F::Target: FeeEstimator,
+ R::Target: Router,
+ L::Target: Logger,
+{
let our_node_pubkey = channelmanager.get_our_node_id();
let channels = channelmanager.list_channels();
.collect::<Vec<RouteHint>>()
}
-/// A [`Router`] implemented using [`find_route`].
-pub struct DefaultRouter<G: Deref<Target = NetworkGraph<L>>, L: Deref, S: Deref> where
- L::Target: Logger,
- S::Target: for <'a> LockableScore<'a>,
-{
- network_graph: G,
- logger: L,
- random_seed_bytes: Mutex<[u8; 32]>,
- scorer: S
-}
-
-impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, S: Deref> DefaultRouter<G, L, S> where
- L::Target: Logger,
- S::Target: for <'a> LockableScore<'a>,
-{
- /// Creates a new router using the given [`NetworkGraph`], a [`Logger`], and a randomness source
- /// `random_seed_bytes`.
- pub fn new(network_graph: G, logger: L, random_seed_bytes: [u8; 32], scorer: S) -> Self {
- let random_seed_bytes = Mutex::new(random_seed_bytes);
- Self { network_graph, logger, random_seed_bytes, scorer }
- }
-}
-
-impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, S: Deref> Router for DefaultRouter<G, L, S> where
- L::Target: Logger,
- S::Target: for <'a> LockableScore<'a>,
-{
- fn find_route(
- &self, payer: &PublicKey, params: &RouteParameters, first_hops: Option<&[&ChannelDetails]>,
- inflight_htlcs: InFlightHtlcs
- ) -> Result<Route, LightningError> {
- let random_seed_bytes = {
- let mut locked_random_seed_bytes = self.random_seed_bytes.lock().unwrap();
- *locked_random_seed_bytes = sha256::Hash::hash(&*locked_random_seed_bytes).into_inner();
- *locked_random_seed_bytes
- };
-
- find_route(
- payer, params, &self.network_graph, first_hops, &*self.logger,
- &ScorerAccountingForInFlightHtlcs::new(&mut self.scorer.lock(), inflight_htlcs),
- &random_seed_bytes
- )
- }
-}
-
-impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, S: Deref> ScoringRouter for DefaultRouter<G, L, S> where
- L::Target: Logger,
- S::Target: for <'a> LockableScore<'a>,
-{
- fn notify_payment_path_failed(&self, path: &[&RouteHop], short_channel_id: u64) {
- self.scorer.lock().payment_path_failed(path, short_channel_id);
- }
-
- fn notify_payment_path_successful(&self, path: &[&RouteHop]) {
- self.scorer.lock().payment_path_successful(path);
- }
-
- fn notify_payment_probe_successful(&self, path: &[&RouteHop]) {
- self.scorer.lock().probe_successful(path);
- }
-
- fn notify_payment_probe_failed(&self, path: &[&RouteHop], short_channel_id: u64) {
- self.scorer.lock().probe_failed(path, short_channel_id);
- }
-}
-
-impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Payer for ChannelManager<M, T, K, F, L>
+impl<M: Deref, T: Deref, K: Deref, F: Deref, R: Deref, L: Deref> Payer for ChannelManager<M, T, K, F, R, L>
where
- M::Target: chain::Watch<<K::Target as KeysInterface>::Signer>,
+ M::Target: chain::Watch<<K::Target as SignerProvider>::Signer>,
T::Target: BroadcasterInterface,
K::Target: KeysInterface,
F::Target: FeeEstimator,
+ R::Target: Router,
L::Target: Logger,
{
fn node_id(&self) -> PublicKey {
fn inflight_htlcs(&self) -> InFlightHtlcs { self.compute_inflight_htlcs() }
}
-
-/// Used to store information about all the HTLCs that are inflight across all payment attempts.
-pub(crate) struct ScorerAccountingForInFlightHtlcs<'a, S: Score> {
- scorer: &'a mut S,
- /// Maps a channel's short channel id and its direction to the liquidity used up.
- inflight_htlcs: InFlightHtlcs,
-}
-
-impl<'a, S: Score> ScorerAccountingForInFlightHtlcs<'a, S> {
- pub(crate) fn new(scorer: &'a mut S, inflight_htlcs: InFlightHtlcs) -> Self {
- ScorerAccountingForInFlightHtlcs {
- scorer,
- inflight_htlcs
- }
- }
-}
-
-#[cfg(c_bindings)]
-impl<'a, S:Score> lightning::util::ser::Writeable for ScorerAccountingForInFlightHtlcs<'a, S> {
- fn write<W: lightning::util::ser::Writer>(&self, writer: &mut W) -> Result<(), lightning::io::Error> { self.scorer.write(writer) }
-}
-
-impl<'a, S: Score> Score for ScorerAccountingForInFlightHtlcs<'a, S> {
- fn channel_penalty_msat(&self, short_channel_id: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage) -> u64 {
- if let Some(used_liqudity) = self.inflight_htlcs.used_liquidity_msat(
- source, target, short_channel_id
- ) {
- let usage = ChannelUsage {
- inflight_htlc_msat: usage.inflight_htlc_msat + used_liqudity,
- ..usage
- };
-
- self.scorer.channel_penalty_msat(short_channel_id, source, target, usage)
- } else {
- self.scorer.channel_penalty_msat(short_channel_id, source, target, usage)
- }
- }
-
- fn payment_path_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) { unreachable!() }
-
- fn payment_path_successful(&mut self, _path: &[&RouteHop]) { unreachable!() }
-
- fn probe_failed(&mut self, _path: &[&RouteHop], _short_channel_id: u64) { unreachable!() }
-
- fn probe_successful(&mut self, _path: &[&RouteHop]) { unreachable!() }
-}
-
-
#[cfg(test)]
mod test {
use core::time::Duration;
use crate::{Currency, Description, InvoiceDescription};
- use bitcoin_hashes::Hash;
+ use bitcoin_hashes::{Hash, sha256};
use bitcoin_hashes::sha256::Hash as Sha256;
- use lightning::chain::keysinterface::PhantomKeysManager;
+ use lightning::chain::keysinterface::{EntropySource, PhantomKeysManager};
use lightning::ln::{PaymentPreimage, PaymentHash};
use lightning::ln::channelmanager::{self, PhantomRouteHints, MIN_FINAL_CLTV_EXPIRY, PaymentId};
use lightning::ln::functional_test_utils::*;
assert_eq!(invoice.description(), InvoiceDescription::Hash(&crate::Sha256(Sha256::hash("Testing description_hash".as_bytes()))));
}
+ #[test]
+ fn test_create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_hash() {
+ let chanmon_cfgs = create_chanmon_cfgs(2);
+ let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
+ let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+ let payment_hash = PaymentHash([0; 32]);
+ let invoice = crate::utils::create_invoice_from_channelmanager_and_duration_since_epoch_with_payment_hash(
+ &nodes[1].node, nodes[1].keys_manager, nodes[1].logger, Currency::BitcoinTestnet,
+ Some(10_000), "test".to_string(), Duration::from_secs(1234567), 3600,
+ payment_hash
+ ).unwrap();
+ assert_eq!(invoice.amount_pico_btc(), Some(100_000));
+ assert_eq!(invoice.min_final_cltv_expiry(), MIN_FINAL_CLTV_EXPIRY as u64);
+ assert_eq!(invoice.description(), InvoiceDescription::Direct(&Description("test".to_string())));
+ assert_eq!(invoice.payment_hash(), &sha256::Hash::from_slice(&payment_hash.0[..]).unwrap());
+ }
+
#[test]
fn test_hints_includes_single_channels_to_nodes() {
let chanmon_cfgs = create_chanmon_cfgs(3);
let _chan_1_0_low_inbound_capacity = create_unannounced_chan_between_nodes_with_value(&nodes, 1, 0, 100_000, 0, channelmanager::provided_init_features(), channelmanager::provided_init_features());
let chan_1_0_high_inbound_capacity = create_unannounced_chan_between_nodes_with_value(&nodes, 1, 0, 10_000_000, 0, channelmanager::provided_init_features(), channelmanager::provided_init_features());
let _chan_1_0_medium_inbound_capacity = create_unannounced_chan_between_nodes_with_value(&nodes, 1, 0, 1_000_000, 0, channelmanager::provided_init_features(), channelmanager::provided_init_features());
-
let mut scid_aliases = HashSet::new();
scid_aliases.insert(chan_1_0_high_inbound_capacity.0.short_channel_id_alias.unwrap());
-
match_invoice_routes(Some(5000), &nodes[0], scid_aliases);
}
nodes[fwd_idx].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &payment_event.msgs[0]);
commitment_signed_dance!(nodes[fwd_idx], nodes[0], &payment_event.commitment_msg, false, true);
- // Note that we have to "forward pending HTLCs" twice before we see the PaymentReceived as
+ // Note that we have to "forward pending HTLCs" twice before we see the PaymentClaimable as
// this "emulates" the payment taking two hops, providing some privacy to make phantom node
// payments "look real" by taking more time.
expect_pending_htlcs_forwardable_ignore!(nodes[fwd_idx]);
nodes[fwd_idx].node.process_pending_htlc_forwards();
let payment_preimage_opt = if user_generated_pmt_hash { None } else { Some(payment_preimage) };
- expect_payment_received!(&nodes[fwd_idx], payment_hash, payment_secret, payment_amt, payment_preimage_opt);
+ expect_payment_claimable!(&nodes[fwd_idx], payment_hash, payment_secret, payment_amt, payment_preimage_opt, route.paths[0].last().unwrap().pubkey);
do_claim_payment_along_route(&nodes[0], &vec!(&vec!(&nodes[fwd_idx])[..]), false, payment_preimage);
let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 2);