X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning-invoice%2Fsrc%2Futils.rs;h=ef95b3e355b5abd1c91303e3e708b2014041482a;hb=d29ae1826e1d3fe39f65474532f8788413ce8737;hp=409fa803cab9cac20f423e21483d0bf58d8face8;hpb=16ad7f17a1cf0d1d57ffd0e15532a670b749e075;p=rust-lightning diff --git a/lightning-invoice/src/utils.rs b/lightning-invoice/src/utils.rs index 409fa803..ef95b3e3 100644 --- a/lightning-invoice/src/utils.rs +++ b/lightning-invoice/src/utils.rs @@ -1,17 +1,27 @@ //! Convenient utilities to create an invoice. -use {Currency, DEFAULT_EXPIRY_TIME, Invoice, InvoiceBuilder, SignOrCreationError, RawInvoice}; + +use {CreationError, Currency, DEFAULT_EXPIRY_TIME, Invoice, InvoiceBuilder, SignOrCreationError}; +use payment::{Payer, Router}; + use bech32::ToBase32; use bitcoin_hashes::Hash; +use crate::prelude::*; use lightning::chain; use lightning::chain::chaininterface::{BroadcasterInterface, FeeEstimator}; use lightning::chain::keysinterface::{Sign, KeysInterface}; -use lightning::ln::channelmanager::{ChannelManager, MIN_FINAL_CLTV_EXPIRY}; -use lightning::routing::network_graph::RoutingFees; -use lightning::routing::router::{RouteHint, RouteHintHop}; +use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret}; +use lightning::ln::channelmanager::{ChannelDetails, ChannelManager, PaymentId, PaymentSendFailure, MIN_FINAL_CLTV_EXPIRY}; +use lightning::ln::msgs::LightningError; +use lightning::routing::scoring::Score; +use lightning::routing::network_graph::{NetworkGraph, RoutingFees}; +use lightning::routing::router::{Route, RouteHint, RouteHintHop, RouteParameters, find_route}; use lightning::util::logger::Logger; -use std::convert::TryInto; -use std::ops::Deref; +use secp256k1::key::PublicKey; +use core::convert::TryInto; +use core::ops::Deref; +use core::time::Duration; +#[cfg(feature = "std")] /// Utility to construct an invoice. Generally, unless you want to do something like a custom /// cltv_expiry, this is what you should be using to create an invoice. The reason being, this /// method stores the invoice's payment secret and preimage in `ChannelManager`, so (a) the user @@ -21,6 +31,33 @@ pub fn create_invoice_from_channelmanager, keys_manager: K, network: Currency, amt_msat: Option, description: String ) -> Result> +where + M::Target: chain::Watch, + T::Target: BroadcasterInterface, + K::Target: KeysInterface, + F::Target: FeeEstimator, + L::Target: Logger, +{ + use std::time::SystemTime; + let duration = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) + .expect("for the foreseeable future this shouldn't happen"); + create_invoice_from_channelmanager_and_duration_since_epoch( + channelmanager, + keys_manager, + network, + amt_msat, + description, + duration + ) +} + +/// 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( + channelmanager: &ChannelManager, keys_manager: K, network: Currency, + amt_msat: Option, description: String, duration_since_epoch: Duration, +) -> Result> where M::Target: chain::Watch, T::Target: BroadcasterInterface, @@ -53,15 +90,15 @@ where }])); } + // `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, - DEFAULT_EXPIRY_TIME.try_into().unwrap(), - 0, - ); + amt_msat, DEFAULT_EXPIRY_TIME.try_into().unwrap()) + .map_err(|()| SignOrCreationError::CreationError(CreationError::InvalidAmount))?; let our_node_pubkey = channelmanager.get_our_node_id(); let mut invoice = InvoiceBuilder::new(network) .description(description) - .current_timestamp() + .duration_since_epoch(duration_since_epoch) .payee_pub_key(our_node_pubkey) .payment_hash(Hash::from_slice(&payment_hash.0).unwrap()) .payment_secret(payment_secret) @@ -81,25 +118,90 @@ where let hrp_str = raw_invoice.hrp.to_string(); let hrp_bytes = hrp_str.as_bytes(); let data_without_signature = raw_invoice.data.to_base32(); - let invoice_preimage = RawInvoice::construct_invoice_preimage(hrp_bytes, &data_without_signature); - let signed_raw_invoice = raw_invoice.sign(|_| keys_manager.sign_invoice(invoice_preimage)); + let signed_raw_invoice = raw_invoice.sign(|_| keys_manager.sign_invoice(hrp_bytes, &data_without_signature)); match signed_raw_invoice { Ok(inv) => Ok(Invoice::from_signed(inv).unwrap()), Err(e) => Err(SignOrCreationError::SignError(e)) } } +/// A [`Router`] implemented using [`find_route`]. +pub struct DefaultRouter, L: Deref> where L::Target: Logger { + network_graph: G, + logger: L, +} + +impl, L: Deref> DefaultRouter where L::Target: Logger { + /// Creates a new router using the given [`NetworkGraph`] and [`Logger`]. + pub fn new(network_graph: G, logger: L) -> Self { + Self { network_graph, logger } + } +} + +impl, L: Deref, S: Score> Router for DefaultRouter +where L::Target: Logger { + fn find_route( + &self, payer: &PublicKey, params: &RouteParameters, _payment_hash: &PaymentHash, + first_hops: Option<&[&ChannelDetails]>, scorer: &S + ) -> Result { + find_route(payer, params, &*self.network_graph, first_hops, &*self.logger, scorer) + } +} + +impl Payer for ChannelManager +where + M::Target: chain::Watch, + T::Target: BroadcasterInterface, + K::Target: KeysInterface, + F::Target: FeeEstimator, + L::Target: Logger, +{ + fn node_id(&self) -> PublicKey { + self.get_our_node_id() + } + + fn first_hops(&self) -> Vec { + self.list_usable_channels() + } + + fn send_payment( + &self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option + ) -> Result { + self.send_payment(route, payment_hash, payment_secret) + } + + fn send_spontaneous_payment( + &self, route: &Route, payment_preimage: PaymentPreimage, + ) -> Result { + self.send_spontaneous_payment(route, Some(payment_preimage)) + .map(|(_, payment_id)| payment_id) + } + + fn retry_payment( + &self, route: &Route, payment_id: PaymentId + ) -> Result<(), PaymentSendFailure> { + self.retry_payment(route, payment_id) + } + + fn abandon_payment(&self, payment_id: PaymentId) { + self.abandon_payment(payment_id) + } +} + #[cfg(test)] mod test { + use core::time::Duration; use {Currency, Description, InvoiceDescription}; use lightning::ln::PaymentHash; use lightning::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY; use lightning::ln::functional_test_utils::*; use lightning::ln::features::InitFeatures; use lightning::ln::msgs::ChannelMessageHandler; - use lightning::routing::router; + use lightning::routing::router::{PaymentParameters, RouteParameters, find_route}; use lightning::util::events::MessageSendEventsProvider; use lightning::util::test_utils; + use utils::create_invoice_from_channelmanager_and_duration_since_epoch; + #[test] fn test_from_channelmanager() { let chanmon_cfgs = create_chanmon_cfgs(2); @@ -107,26 +209,28 @@ mod test { let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]); let nodes = create_network(2, &node_cfgs, &node_chanmgrs); let _chan = create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known()); - let invoice = ::utils::create_invoice_from_channelmanager(&nodes[1].node, nodes[1].keys_manager, Currency::BitcoinTestnet, Some(10_000), "test".to_string()).unwrap(); + let invoice = create_invoice_from_channelmanager_and_duration_since_epoch( + &nodes[1].node, nodes[1].keys_manager, Currency::BitcoinTestnet, Some(10_000), "test".to_string(), + Duration::from_secs(1234567)).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()))); - let amt_msat = invoice.amount_pico_btc().unwrap() / 10; + let payment_params = PaymentParameters::from_node_id(invoice.recover_payee_pub_key()) + .with_features(invoice.features().unwrap().clone()) + .with_route_hints(invoice.route_hints()); + let route_params = RouteParameters { + payment_params, + final_value_msat: invoice.amount_milli_satoshis().unwrap(), + final_cltv_expiry_delta: invoice.min_final_cltv_expiry() as u32, + }; let first_hops = nodes[0].node.list_usable_channels(); - let last_hops = invoice.route_hints(); - let network_graph = &nodes[0].net_graph_msg_handler.network_graph; + let network_graph = node_cfgs[0].network_graph; let logger = test_utils::TestLogger::new(); - let route = router::get_route( - &nodes[0].node.get_our_node_id(), - network_graph, - &invoice.recover_payee_pub_key(), - Some(invoice.features().unwrap().clone()), - Some(&first_hops.iter().collect::>()), - &last_hops, - amt_msat, - invoice.min_final_cltv_expiry() as u32, - &logger, + let scorer = test_utils::TestScorer::with_penalty(0); + let route = find_route( + &nodes[0].node.get_our_node_id(), &route_params, network_graph, + Some(&first_hops.iter().collect::>()), &logger, &scorer, ).unwrap(); let payment_event = {