Merge pull request #1887 from TheBlueMatt/2022-11-definitely-valid
authorMatt Corallo <649246+TheBlueMatt@users.noreply.github.com>
Sat, 3 Dec 2022 19:01:15 +0000 (19:01 +0000)
committerGitHub <noreply@github.com>
Sat, 3 Dec 2022 19:01:15 +0000 (19:01 +0000)
Remove cryptographically unreachable error conditions

15 files changed:
fuzz/src/chanmon_consistency.rs
lightning-background-processor/src/lib.rs
lightning-invoice/src/payment.rs
lightning-invoice/src/utils.rs
lightning/src/ln/chan_utils.rs
lightning/src/ln/channelmanager.rs
lightning/src/ln/functional_tests.rs
lightning/src/ln/onion_utils.rs
lightning/src/ln/payment_tests.rs
lightning/src/routing/router.rs
lightning/src/util/config.rs
lightning/src/util/errors.rs
lightning/src/util/events.rs
lightning/src/util/persist.rs
lightning/src/util/scid_utils.rs

index 783bbc06aa9fe58e4de90e5ce13650aeb0458b9d..6c1d4348dc1ff97e36263d104117ee732ea2884f 100644 (file)
@@ -253,7 +253,7 @@ fn check_api_err(api_err: APIError) {
        match api_err {
                APIError::APIMisuseError { .. } => panic!("We can't misuse the API"),
                APIError::FeeRateTooHigh { .. } => panic!("We can't send too much fee?"),
-               APIError::RouteError { .. } => panic!("Our routes should work"),
+               APIError::InvalidRoute { .. } => panic!("Our routes should work"),
                APIError::ChannelUnavailable { err } => {
                        // Test the error against a list of errors we can hit, and reject
                        // all others. If you hit this panic, the list of acceptable errors
index 4970c6920c53d75061824b70f68ae6af36ced1b9..1c720921970095d4211b4b8113b113926db3854f 100644 (file)
@@ -578,13 +578,13 @@ mod tests {
        use lightning::ln::msgs::{ChannelMessageHandler, Init};
        use lightning::ln::peer_handler::{PeerManager, MessageHandler, SocketDescriptor, IgnoringMessageHandler};
        use lightning::routing::gossip::{NetworkGraph, P2PGossipSync};
+       use lightning::routing::router::DefaultRouter;
        use lightning::util::config::UserConfig;
        use lightning::util::events::{Event, MessageSendEventsProvider, MessageSendEvent};
        use lightning::util::ser::Writeable;
        use lightning::util::test_utils;
        use lightning::util::persist::KVStorePersister;
        use lightning_invoice::payment::{InvoicePayer, Retry};
-       use lightning_invoice::utils::DefaultRouter;
        use lightning_persister::FilesystemPersister;
        use std::fs;
        use std::path::PathBuf;
index eceaacf86c93a1f8ceb5162c8b4680b6d3999f46..4fddedc0c0749e908889fcdc82db028c877c4612 100644 (file)
@@ -44,7 +44,7 @@
 //! # use lightning::util::logger::{Logger, Record};
 //! # use lightning::util::ser::{Writeable, Writer};
 //! # use lightning_invoice::Invoice;
-//! # use lightning_invoice::payment::{InvoicePayer, Payer, Retry, ScoringRouter};
+//! # use lightning_invoice::payment::{InvoicePayer, Payer, Retry};
 //! # use secp256k1::PublicKey;
 //! # use std::cell::RefCell;
 //! # use std::ops::Deref;
@@ -78,8 +78,6 @@
 //! #         &self, payer: &PublicKey, params: &RouteParameters,
 //! #         first_hops: Option<&[&ChannelDetails]>, _inflight_htlcs: InFlightHtlcs
 //! #     ) -> Result<Route, LightningError> { unimplemented!() }
-//! # }
-//! # impl ScoringRouter for FakeRouter {
 //! #     fn notify_payment_path_failed(&self, path: &[&RouteHop], short_channel_id: u64) {  unimplemented!() }
 //! #     fn notify_payment_path_successful(&self, path: &[&RouteHop]) {  unimplemented!() }
 //! #     fn notify_payment_probe_successful(&self, path: &[&RouteHop]) {  unimplemented!() }
@@ -146,7 +144,7 @@ use crate::prelude::*;
 use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
 use lightning::ln::channelmanager::{ChannelDetails, PaymentId, PaymentSendFailure};
 use lightning::ln::msgs::LightningError;
-use lightning::routing::router::{InFlightHtlcs, PaymentParameters, Route, RouteHop, RouteParameters, Router};
+use lightning::routing::router::{InFlightHtlcs, PaymentParameters, Route, RouteParameters, Router};
 use lightning::util::events::{Event, EventHandler};
 use lightning::util::logger::Logger;
 use crate::time_utils::Time;
@@ -186,7 +184,7 @@ mod sealed {
 /// (C-not exported) generally all users should use the [`InvoicePayer`] type alias.
 pub struct InvoicePayerUsingTime<
        P: Deref,
-       R: ScoringRouter,
+       R: Router,
        L: Deref,
        E: sealed::BaseEventHandler,
        T: Time
@@ -279,30 +277,6 @@ pub trait Payer {
        fn inflight_htlcs(&self) -> InFlightHtlcs;
 }
 
-/// A trait defining behavior for a [`Router`] implementation that also supports scoring channels
-/// based on payment and probe success/failure.
-///
-/// [`Router`]: lightning::routing::router::Router
-pub trait ScoringRouter: Router {
-       /// Finds a [`Route`] between `payer` and `payee` for a payment with the given values. Includes
-       /// `PaymentHash` and `PaymentId` to be able to correlate the request with a specific payment.
-       fn find_route_with_id(
-               &self, payer: &PublicKey, route_params: &RouteParameters,
-               first_hops: Option<&[&ChannelDetails]>, inflight_htlcs: InFlightHtlcs,
-               _payment_hash: PaymentHash, _payment_id: PaymentId
-       ) -> Result<Route, LightningError> {
-               self.find_route(payer, route_params, first_hops, inflight_htlcs)
-       }
-       /// Lets the router know that payment through a specific path has failed.
-       fn notify_payment_path_failed(&self, path: &[&RouteHop], short_channel_id: u64);
-       /// Lets the router know that payment through a specific path was successful.
-       fn notify_payment_path_successful(&self, path: &[&RouteHop]);
-       /// Lets the router know that a payment probe was successful.
-       fn notify_payment_probe_successful(&self, path: &[&RouteHop]);
-       /// Lets the router know that a payment probe failed.
-       fn notify_payment_probe_failed(&self, path: &[&RouteHop], short_channel_id: u64);
-}
-
 /// Strategies available to retry payment path failures for an [`Invoice`].
 ///
 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
@@ -342,7 +316,7 @@ pub enum PaymentError {
        Sending(PaymentSendFailure),
 }
 
-impl<P: Deref, R: ScoringRouter, L: Deref, E: sealed::BaseEventHandler, T: Time>
+impl<P: Deref, R: Router, L: Deref, E: sealed::BaseEventHandler, T: Time>
        InvoicePayerUsingTime<P, R, L, E, T>
 where
        P::Target: Payer,
@@ -656,7 +630,7 @@ fn has_expired(route_params: &RouteParameters) -> bool {
        } else { false }
 }
 
-impl<P: Deref, R: ScoringRouter, L: Deref, E: sealed::BaseEventHandler, T: Time>
+impl<P: Deref, R: Router, L: Deref, E: sealed::BaseEventHandler, T: Time>
        InvoicePayerUsingTime<P, R, L, E, T>
 where
        P::Target: Payer,
@@ -723,7 +697,7 @@ where
        }
 }
 
-impl<P: Deref, R: ScoringRouter, L: Deref, E: EventHandler, T: Time>
+impl<P: Deref, R: Router, L: Deref, E: EventHandler, T: Time>
        EventHandler for InvoicePayerUsingTime<P, R, L, E, T>
 where
        P::Target: Payer,
@@ -737,7 +711,7 @@ where
        }
 }
 
-impl<P: Deref, R: ScoringRouter, L: Deref, T: Time, F: Future, H: Fn(Event) -> F>
+impl<P: Deref, R: Router, L: Deref, T: Time, F: Future, H: Fn(Event) -> F>
        InvoicePayerUsingTime<P, R, L, H, T>
 where
        P::Target: Payer,
@@ -757,7 +731,7 @@ where
 mod tests {
        use super::*;
        use crate::{InvoiceBuilder, Currency};
-       use crate::utils::{ScorerAccountingForInFlightHtlcs, create_invoice_from_channelmanager_and_duration_since_epoch};
+       use crate::utils::create_invoice_from_channelmanager_and_duration_since_epoch;
        use bitcoin_hashes::sha256::Hash as Sha256;
        use lightning::ln::PaymentPreimage;
        use lightning::ln::channelmanager;
@@ -765,7 +739,7 @@ mod tests {
        use lightning::ln::functional_test_utils::*;
        use lightning::ln::msgs::{ChannelMessageHandler, ErrorAction, LightningError};
        use lightning::routing::gossip::{EffectiveCapacity, NodeId};
-       use lightning::routing::router::{InFlightHtlcs, PaymentParameters, Route, RouteHop, Router};
+       use lightning::routing::router::{InFlightHtlcs, PaymentParameters, Route, RouteHop, Router, ScorerAccountingForInFlightHtlcs};
        use lightning::routing::scoring::{ChannelUsage, LockableScore, Score};
        use lightning::util::test_utils::TestLogger;
        use lightning::util::errors::APIError;
@@ -1726,9 +1700,7 @@ mod tests {
                                payment_params: Some(route_params.payment_params.clone()), ..Self::route_for_value(route_params.final_value_msat)
                        })
                }
-       }
 
-       impl ScoringRouter for TestRouter {
                fn notify_payment_path_failed(&self, path: &[&RouteHop], short_channel_id: u64) {
                        self.scorer.lock().payment_path_failed(path, short_channel_id);
                }
@@ -1755,9 +1727,7 @@ mod tests {
                ) -> Result<Route, LightningError> {
                        Err(LightningError { err: String::new(), action: ErrorAction::IgnoreError })
                }
-       }
 
-       impl ScoringRouter for FailingRouter {
                fn notify_payment_path_failed(&self, _path: &[&RouteHop], _short_channel_id: u64) {}
 
                fn notify_payment_path_successful(&self, _path: &[&RouteHop]) {}
@@ -2045,8 +2015,7 @@ mod tests {
                ) -> Result<Route, LightningError> {
                        self.0.borrow_mut().pop_front().unwrap()
                }
-       }
-       impl ScoringRouter for ManualRouter {
+
                fn notify_payment_path_failed(&self, _path: &[&RouteHop], _short_channel_id: u64) {}
 
                fn notify_payment_path_successful(&self, _path: &[&RouteHop]) {}
index 9d96c6e61193d7ea586acef93269712fba9b3376..47856fb487cf8b4164d92433e95eabdee4b4e5a3 100644 (file)
@@ -1,11 +1,11 @@
 //! 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};
@@ -14,15 +14,12 @@ use lightning::ln::channelmanager::{ChannelDetails, ChannelManager, PaymentId, P
 #[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};
 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."
@@ -524,72 +521,6 @@ fn filter_channels<L: Deref>(
                .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>
 where
        M::Target: chain::Watch<<K::Target as KeysInterface>::Signer>,
@@ -632,54 +563,6 @@ where
        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;
index b5b9d2ee77f97c36280cdbbbf6825b838d2ed8db..39106f0c775fee350116d8830fcab915b55d2ee6 100644 (file)
@@ -14,6 +14,7 @@ use bitcoin::blockdata::script::{Script,Builder};
 use bitcoin::blockdata::opcodes;
 use bitcoin::blockdata::transaction::{TxIn,TxOut,OutPoint,Transaction, EcdsaSighashType};
 use bitcoin::util::sighash;
+use bitcoin::util::address::Payload;
 
 use bitcoin::hashes::{Hash, HashEngine};
 use bitcoin::hashes::sha256::Hash as Sha256;
@@ -25,10 +26,10 @@ use crate::ln::msgs::DecodeError;
 use crate::util::ser::{Readable, Writeable, Writer};
 use crate::util::{byte_utils, transaction_utils};
 
-use bitcoin::hash_types::WPubkeyHash;
 use bitcoin::secp256k1::{SecretKey, PublicKey, Scalar};
 use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature, Message};
 use bitcoin::{PackedLockTime, secp256k1, Sequence, Witness};
+use bitcoin::PublicKey as BitcoinPublicKey;
 
 use crate::io;
 use crate::prelude::*;
@@ -40,13 +41,20 @@ use core::ops::Deref;
 use crate::chain;
 use crate::util::crypto::sign;
 
-pub(crate) const MAX_HTLCS: u16 = 483;
-pub(crate) const OFFERED_HTLC_SCRIPT_WEIGHT: usize = 133;
-pub(crate) const OFFERED_HTLC_SCRIPT_WEIGHT_ANCHORS: usize = 136;
-// The weight of `accepted_htlc_script` can vary in function of its CLTV argument value. We define a
-// range that encompasses both its non-anchors and anchors variants.
+/// Maximum number of one-way in-flight HTLC (protocol-level value).
+pub const MAX_HTLCS: u16 = 483;
+/// The weight of a BIP141 witnessScript for a BOLT3's "offered HTLC output" on a commitment transaction, non-anchor variant.
+pub const OFFERED_HTLC_SCRIPT_WEIGHT: usize = 133;
+/// The weight of a BIP141 witnessScript for a BOLT3's "offered HTLC output" on a commitment transaction, anchor variant.
+pub const OFFERED_HTLC_SCRIPT_WEIGHT_ANCHORS: usize = 136;
+
+/// The weight of a BIP141 witnessScript for a BOLT3's "received HTLC output" can vary in function of its CLTV argument value.
+/// We define a range that encompasses both its non-anchors and anchors variants.
 pub(crate) const MIN_ACCEPTED_HTLC_SCRIPT_WEIGHT: usize = 136;
-pub(crate) const MAX_ACCEPTED_HTLC_SCRIPT_WEIGHT: usize = 143;
+/// The weight of a BIP141 witnessScript for a BOLT3's "received HTLC output" can vary in function of its CLTV argument value.
+/// We define a range that encompasses both its non-anchors and anchors variants.
+/// This is the maximum post-anchor value.
+pub const MAX_ACCEPTED_HTLC_SCRIPT_WEIGHT: usize = 143;
 
 /// Gets the weight for an HTLC-Success transaction.
 #[inline]
@@ -64,18 +72,24 @@ pub fn htlc_timeout_tx_weight(opt_anchors: bool) -> u64 {
        if opt_anchors { HTLC_TIMEOUT_ANCHOR_TX_WEIGHT } else { HTLC_TIMEOUT_TX_WEIGHT }
 }
 
+/// Describes the type of HTLC claim as determined by analyzing the witness.
 #[derive(PartialEq, Eq)]
-pub(crate) enum HTLCClaim {
+pub enum HTLCClaim {
+       /// Claims an offered output on a commitment transaction through the timeout path.
        OfferedTimeout,
+       /// Claims an offered output on a commitment transaction through the success path.
        OfferedPreimage,
+       /// Claims an accepted output on a commitment transaction through the timeout path.
        AcceptedTimeout,
+       /// Claims an accepted output on a commitment transaction through the success path.
        AcceptedPreimage,
+       /// Claims an offered/accepted output on a commitment transaction through the revocation path.
        Revocation,
 }
 
 impl HTLCClaim {
        /// Check if a given input witness attempts to claim a HTLC.
-       pub(crate) fn from_witness(witness: &Witness) -> Option<Self> {
+       pub fn from_witness(witness: &Witness) -> Option<Self> {
                debug_assert_eq!(OFFERED_HTLC_SCRIPT_WEIGHT_ANCHORS, MIN_ACCEPTED_HTLC_SCRIPT_WEIGHT);
                if witness.len() < 2 {
                        return None;
@@ -703,7 +717,7 @@ pub fn build_htlc_transaction(commitment_txid: &Txid, feerate_per_kw: u32, conte
 
 /// Gets the witnessScript for the to_remote output when anchors are enabled.
 #[inline]
-pub(crate) fn get_to_countersignatory_with_anchors_redeemscript(payment_point: &PublicKey) -> Script {
+pub fn get_to_countersignatory_with_anchors_redeemscript(payment_point: &PublicKey) -> Script {
        Builder::new()
                .push_slice(&payment_point.serialize()[..])
                .push_opcode(opcodes::all::OP_CHECKSIGVERIFY)
@@ -1287,7 +1301,7 @@ impl CommitmentTransaction {
                        let script = if opt_anchors {
                            get_to_countersignatory_with_anchors_redeemscript(&countersignatory_pubkeys.payment_point).to_v0_p2wsh()
                        } else {
-                           get_p2wpkh_redeemscript(&countersignatory_pubkeys.payment_point)
+                           Payload::p2wpkh(&BitcoinPublicKey::new(countersignatory_pubkeys.payment_point)).unwrap().script_pubkey()
                        };
                        txouts.push((
                                TxOut {
@@ -1593,18 +1607,12 @@ pub fn get_commitment_transaction_number_obscure_factor(
                | ((res[31] as u64) << 0 * 8)
 }
 
-fn get_p2wpkh_redeemscript(key: &PublicKey) -> Script {
-       Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0)
-               .push_slice(&WPubkeyHash::hash(&key.serialize())[..])
-               .into_script()
-}
-
 #[cfg(test)]
 mod tests {
        use super::CounterpartyCommitmentSecrets;
        use crate::{hex, chain};
        use crate::prelude::*;
-       use crate::ln::chan_utils::{get_htlc_redeemscript, get_to_countersignatory_with_anchors_redeemscript, get_p2wpkh_redeemscript, CommitmentTransaction, TxCreationKeys, ChannelTransactionParameters, CounterpartyChannelTransactionParameters, HTLCOutputInCommitment};
+       use crate::ln::chan_utils::{get_htlc_redeemscript, get_to_countersignatory_with_anchors_redeemscript, CommitmentTransaction, TxCreationKeys, ChannelTransactionParameters, CounterpartyChannelTransactionParameters, HTLCOutputInCommitment};
        use bitcoin::secp256k1::{PublicKey, SecretKey, Secp256k1};
        use crate::util::test_utils;
        use crate::chain::keysinterface::{KeysInterface, BaseSign};
@@ -1612,6 +1620,8 @@ mod tests {
        use bitcoin::hashes::Hash;
        use crate::ln::PaymentHash;
        use bitcoin::hashes::hex::ToHex;
+       use bitcoin::util::address::Payload;
+       use bitcoin::PublicKey as BitcoinPublicKey;
 
        #[test]
        fn test_anchors() {
@@ -1651,7 +1661,7 @@ mod tests {
                        &mut htlcs_with_aux, &channel_parameters.as_holder_broadcastable()
                );
                assert_eq!(tx.built.transaction.output.len(), 2);
-               assert_eq!(tx.built.transaction.output[1].script_pubkey, get_p2wpkh_redeemscript(&counterparty_pubkeys.payment_point));
+               assert_eq!(tx.built.transaction.output[1].script_pubkey, Payload::p2wpkh(&BitcoinPublicKey::new(counterparty_pubkeys.payment_point)).unwrap().script_pubkey());
 
                // Generate broadcaster and counterparty outputs as well as two anchors
                let tx = CommitmentTransaction::new_with_auxiliary_htlc_data(
index d788356d63c23fe312452a6560e0597c69cf5e58..306739ad6e0d0d7fd9a7e5e2df3c053201f02f7d 100644 (file)
@@ -92,8 +92,8 @@ use core::ops::Deref;
 pub(super) enum PendingHTLCRouting {
        Forward {
                onion_packet: msgs::OnionPacket,
-               /// The SCID from the onion that we should forward to. This could be a "real" SCID, an
-               /// outbound SCID alias, or a phantom node SCID.
+               /// The SCID from the onion that we should forward to. This could be a real SCID or a fake one
+               /// generated using `get_fake_scid` from the scid_utils::fake_scid module.
                short_channel_id: u64, // This should be NonZero<u64> eventually when we bump MSRV
        },
        Receive {
@@ -207,6 +207,24 @@ impl Readable for PaymentId {
                Ok(PaymentId(buf))
        }
 }
+
+/// An identifier used to uniquely identify an intercepted HTLC to LDK.
+/// (C-not exported) as we just use [u8; 32] directly
+#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug)]
+pub struct InterceptId(pub [u8; 32]);
+
+impl Writeable for InterceptId {
+       fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+               self.0.write(w)
+       }
+}
+
+impl Readable for InterceptId {
+       fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
+               let buf: [u8; 32] = Readable::read(r)?;
+               Ok(InterceptId(buf))
+       }
+}
 /// Tracks the inbound corresponding to an outbound HTLC
 #[allow(clippy::derive_hash_xor_eq)] // Our Hash is faithful to the data, we just don't have SecretKey::hash
 #[derive(Clone, PartialEq, Eq)]
@@ -269,6 +287,16 @@ pub(super) enum HTLCFailReason {
        }
 }
 
+impl HTLCFailReason {
+       pub(super) fn reason(failure_code: u16, data: Vec<u8>) -> Self {
+               Self::Reason { failure_code, data }
+       }
+
+       pub(super) fn from_failure_code(failure_code: u16) -> Self {
+               Self::Reason { failure_code, data: Vec::new() }
+       }
+}
+
 struct ReceiveError {
        err_code: u16,
        err_data: Vec<u8>,
@@ -666,6 +694,8 @@ pub type SimpleRefChannelManager<'a, 'b, 'c, 'd, 'e, M, T, F, L> = ChannelManage
 // `total_consistency_lock`
 //  |
 //  |__`forward_htlcs`
+//  |   |
+//  |   |__`pending_intercepted_htlcs`
 //  |
 //  |__`pending_inbound_payments`
 //  |   |
@@ -751,6 +781,11 @@ pub struct ChannelManager<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
        pub(super) forward_htlcs: Mutex<HashMap<u64, Vec<HTLCForwardInfo>>>,
        #[cfg(not(test))]
        forward_htlcs: Mutex<HashMap<u64, Vec<HTLCForwardInfo>>>,
+       /// Storage for HTLCs that have been intercepted and bubbled up to the user. We hold them here
+       /// until the user tells us what we should do with them.
+       ///
+       /// See `ChannelManager` struct-level documentation for lock order requirements.
+       pending_intercepted_htlcs: Mutex<HashMap<InterceptId, PendingAddHTLCInfo>>,
 
        /// Map from payment hash to the payment data and any HTLCs which are to us and can be
        /// failed/claimed by the user.
@@ -1566,6 +1601,7 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                        pending_outbound_payments: Mutex::new(HashMap::new()),
                        forward_htlcs: Mutex::new(HashMap::new()),
                        claimable_htlcs: Mutex::new(HashMap::new()),
+                       pending_intercepted_htlcs: Mutex::new(HashMap::new()),
                        id_to_peer: Mutex::new(HashMap::new()),
                        short_to_chan_info: FairRwLock::new(HashMap::new()),
 
@@ -1850,8 +1886,9 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                };
 
                for htlc_source in failed_htlcs.drain(..) {
+                       let reason = HTLCFailReason::from_failure_code(0x4000 | 8);
                        let receiver = HTLCDestination::NextHopChannel { node_id: Some(*counterparty_node_id), channel_id: *channel_id };
-                       self.fail_htlc_backwards_internal(htlc_source.0, &htlc_source.1, HTLCFailReason::Reason { failure_code: 0x4000 | 8, data: Vec::new() }, receiver);
+                       self.fail_htlc_backwards_internal(&htlc_source.0, &htlc_source.1, &reason, receiver);
                }
 
                let _ = handle_error!(self, result, *counterparty_node_id);
@@ -1908,8 +1945,9 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                log_debug!(self.logger, "Finishing force-closure of channel with {} HTLCs to fail", failed_htlcs.len());
                for htlc_source in failed_htlcs.drain(..) {
                        let (source, payment_hash, counterparty_node_id, channel_id) = htlc_source;
+                       let reason = HTLCFailReason::from_failure_code(0x4000 | 8);
                        let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id), channel_id };
-                       self.fail_htlc_backwards_internal(source, &payment_hash, HTLCFailReason::Reason { failure_code: 0x4000 | 8, data: Vec::new() }, receiver);
+                       self.fail_htlc_backwards_internal(&source, &payment_hash, &reason, receiver);
                }
                if let Some((funding_txo, monitor_update)) = monitor_update_option {
                        // There isn't anything we can do if we get an update failure - we're already
@@ -2206,8 +2244,11 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                                        let forwarding_id_opt = match id_option {
                                                None => { // unknown_next_peer
                                                        // Note that this is likely a timing oracle for detecting whether an scid is a
-                                                       // phantom.
-                                                       if fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, *short_channel_id, &self.genesis_hash) {
+                                                       // phantom or an intercept.
+                                                       if (self.default_configuration.accept_intercept_htlcs &&
+                                                          fake_scid::is_valid_intercept(&self.fake_scid_rand_bytes, *short_channel_id, &self.genesis_hash)) ||
+                                                          fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, *short_channel_id, &self.genesis_hash)
+                                                       {
                                                                None
                                                        } else {
                                                                break Some(("Don't have available channel for forwarding as requested.", 0x4000 | 10, None));
@@ -2378,10 +2419,10 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                let session_priv = SecretKey::from_slice(&session_priv_bytes[..]).expect("RNG is busted");
 
                let onion_keys = onion_utils::construct_onion_keys(&self.secp_ctx, &path, &session_priv)
-                       .map_err(|_| APIError::RouteError{err: "Pubkey along hop was maliciously selected"})?;
+                       .map_err(|_| APIError::InvalidRoute{err: "Pubkey along hop was maliciously selected"})?;
                let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(path, total_value, payment_secret, cur_height, keysend_preimage)?;
                if onion_utils::route_size_insane(&onion_payloads) {
-                       return Err(APIError::RouteError{err: "Route size too large considering onion data"});
+                       return Err(APIError::InvalidRoute{err: "Route size too large considering onion data"});
                }
                let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, prng_seed, payment_hash);
 
@@ -2398,7 +2439,7 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                        if let hash_map::Entry::Occupied(mut chan) = channel_state.by_id.entry(id) {
                                match {
                                        if chan.get().get_counterparty_node_id() != path.first().unwrap().pubkey {
-                                               return Err(APIError::RouteError{err: "Node ID mismatch on first hop!"});
+                                               return Err(APIError::InvalidRoute{err: "Node ID mismatch on first hop!"});
                                        }
                                        if !chan.get().is_live() {
                                                return Err(APIError::ChannelUnavailable{err: "Peer for first hop currently disconnected/pending monitor update!".to_owned()});
@@ -2473,7 +2514,7 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
        /// fields for more info.
        ///
        /// If a pending payment is currently in-flight with the same [`PaymentId`] provided, this
-       /// method will error with an [`APIError::RouteError`]. Note, however, that once a payment
+       /// method will error with an [`APIError::InvalidRoute`]. Note, however, that once a payment
        /// is no longer pending (either via [`ChannelManager::abandon_payment`], or handling of an
        /// [`Event::PaymentSent`]) LDK will not stop you from sending a second payment with the same
        /// [`PaymentId`].
@@ -2492,7 +2533,7 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
        /// PaymentSendFailure for more info.
        ///
        /// In general, a path may raise:
-       ///  * [`APIError::RouteError`] when an invalid route or forwarding parameter (cltv_delta, fee,
+       ///  * [`APIError::InvalidRoute`] when an invalid route or forwarding parameter (cltv_delta, fee,
        ///    node public key) is specified.
        ///  * [`APIError::ChannelUnavailable`] if the next-hop channel is not available for updates
        ///    (including due to previous monitor update failure or new permanent monitor update
@@ -2557,7 +2598,7 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
 
        fn send_payment_internal(&self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>, keysend_preimage: Option<PaymentPreimage>, payment_id: PaymentId, recv_value_msat: Option<u64>, onion_session_privs: Vec<[u8; 32]>) -> Result<(), PaymentSendFailure> {
                if route.paths.len() < 1 {
-                       return Err(PaymentSendFailure::ParameterError(APIError::RouteError{err: "There must be at least one path to send over"}));
+                       return Err(PaymentSendFailure::ParameterError(APIError::InvalidRoute{err: "There must be at least one path to send over"}));
                }
                if payment_secret.is_none() && route.paths.len() > 1 {
                        return Err(PaymentSendFailure::ParameterError(APIError::APIMisuseError{err: "Payment secret is required for multi-path payments".to_string()}));
@@ -2567,12 +2608,12 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                let mut path_errs = Vec::with_capacity(route.paths.len());
                'path_check: for path in route.paths.iter() {
                        if path.len() < 1 || path.len() > 20 {
-                               path_errs.push(Err(APIError::RouteError{err: "Path didn't go anywhere/had bogus size"}));
+                               path_errs.push(Err(APIError::InvalidRoute{err: "Path didn't go anywhere/had bogus size"}));
                                continue 'path_check;
                        }
                        for (idx, hop) in path.iter().enumerate() {
                                if idx != path.len() - 1 && hop.pubkey == our_node_id {
-                                       path_errs.push(Err(APIError::RouteError{err: "Path went through us but wasn't a simple rebalance loop to us"}));
+                                       path_errs.push(Err(APIError::InvalidRoute{err: "Path went through us but wasn't a simple rebalance loop to us"}));
                                        continue 'path_check;
                                }
                        }
@@ -3023,6 +3064,102 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                Ok(())
        }
 
+       /// Attempts to forward an intercepted HTLC over the provided channel id and with the provided
+       /// amount to forward. Should only be called in response to an [`HTLCIntercepted`] event.
+       ///
+       /// Intercepted HTLCs can be useful for Lightning Service Providers (LSPs) to open a just-in-time
+       /// channel to a receiving node if the node lacks sufficient inbound liquidity.
+       ///
+       /// To make use of intercepted HTLCs, set [`UserConfig::accept_intercept_htlcs`] and use
+       /// [`ChannelManager::get_intercept_scid`] to generate short channel id(s) to put in the
+       /// receiver's invoice route hints. These route hints will signal to LDK to generate an
+       /// [`HTLCIntercepted`] event when it receives the forwarded HTLC, and this method or
+       /// [`ChannelManager::fail_intercepted_htlc`] MUST be called in response to the event.
+       ///
+       /// Note that LDK does not enforce fee requirements in `amt_to_forward_msat`, and will not stop
+       /// you from forwarding more than you received.
+       ///
+       /// Errors if the event was not handled in time, in which case the HTLC was automatically failed
+       /// backwards.
+       ///
+       /// [`UserConfig::accept_intercept_htlcs`]: crate::util::config::UserConfig::accept_intercept_htlcs
+       /// [`HTLCIntercepted`]: events::Event::HTLCIntercepted
+       // TODO: when we move to deciding the best outbound channel at forward time, only take
+       // `next_node_id` and not `next_hop_channel_id`
+       pub fn forward_intercepted_htlc(&self, intercept_id: InterceptId, next_hop_channel_id: &[u8; 32], _next_node_id: PublicKey, amt_to_forward_msat: u64) -> Result<(), APIError> {
+               let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
+
+               let next_hop_scid = match self.channel_state.lock().unwrap().by_id.get(next_hop_channel_id) {
+                       Some(chan) => {
+                               if !chan.is_usable() {
+                                       return Err(APIError::ChannelUnavailable {
+                                               err: format!("Channel with id {} not fully established", log_bytes!(*next_hop_channel_id))
+                                       })
+                               }
+                               chan.get_short_channel_id().unwrap_or(chan.outbound_scid_alias())
+                       },
+                       None => return Err(APIError::ChannelUnavailable {
+                               err: format!("Channel with id {} not found", log_bytes!(*next_hop_channel_id))
+                       })
+               };
+
+               let payment = self.pending_intercepted_htlcs.lock().unwrap().remove(&intercept_id)
+                       .ok_or_else(|| APIError::APIMisuseError {
+                               err: format!("Payment with intercept id {} not found", log_bytes!(intercept_id.0))
+                       })?;
+
+               let routing = match payment.forward_info.routing {
+                       PendingHTLCRouting::Forward { onion_packet, .. } => {
+                               PendingHTLCRouting::Forward { onion_packet, short_channel_id: next_hop_scid }
+                       },
+                       _ => unreachable!() // Only `PendingHTLCRouting::Forward`s are intercepted
+               };
+               let pending_htlc_info = PendingHTLCInfo {
+                       outgoing_amt_msat: amt_to_forward_msat, routing, ..payment.forward_info
+               };
+
+               let mut per_source_pending_forward = [(
+                       payment.prev_short_channel_id,
+                       payment.prev_funding_outpoint,
+                       payment.prev_user_channel_id,
+                       vec![(pending_htlc_info, payment.prev_htlc_id)]
+               )];
+               self.forward_htlcs(&mut per_source_pending_forward);
+               Ok(())
+       }
+
+       /// Fails the intercepted HTLC indicated by intercept_id. Should only be called in response to
+       /// an [`HTLCIntercepted`] event. See [`ChannelManager::forward_intercepted_htlc`].
+       ///
+       /// Errors if the event was not handled in time, in which case the HTLC was automatically failed
+       /// backwards.
+       ///
+       /// [`HTLCIntercepted`]: events::Event::HTLCIntercepted
+       pub fn fail_intercepted_htlc(&self, intercept_id: InterceptId) -> Result<(), APIError> {
+               let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
+
+               let payment = self.pending_intercepted_htlcs.lock().unwrap().remove(&intercept_id)
+                       .ok_or_else(|| APIError::APIMisuseError {
+                               err: format!("Payment with intercept id {} not found", log_bytes!(intercept_id.0))
+                       })?;
+
+               if let PendingHTLCRouting::Forward { short_channel_id, .. } = payment.forward_info.routing {
+                       let htlc_source = HTLCSource::PreviousHopData(HTLCPreviousHopData {
+                               short_channel_id: payment.prev_short_channel_id,
+                               outpoint: payment.prev_funding_outpoint,
+                               htlc_id: payment.prev_htlc_id,
+                               incoming_packet_shared_secret: payment.forward_info.incoming_shared_secret,
+                               phantom_shared_secret: None,
+                       });
+
+                       let failure_reason = HTLCFailReason::from_failure_code(0x4000 | 10);
+                       let destination = HTLCDestination::UnknownNextHop { requested_forward_scid: short_channel_id };
+                       self.fail_htlc_backwards_internal(&htlc_source, &payment.forward_info.payment_hash, &failure_reason, destination);
+               } else { unreachable!() } // Only `PendingHTLCRouting::Forward`s are intercepted
+
+               Ok(())
+       }
+
        /// Processes HTLCs which are pending waiting on random forward delay.
        ///
        /// Should only really ever be called in response to a PendingHTLCsForwardable event.
@@ -3070,7 +3207,7 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                                                                                                };
 
                                                                                                failed_forwards.push((htlc_source, payment_hash,
-                                                                                                       HTLCFailReason::Reason { failure_code: $err_code, data: $err_data },
+                                                                                                       HTLCFailReason::reason($err_code, $err_data),
                                                                                                        reason
                                                                                                ));
                                                                                                continue;
@@ -3178,7 +3315,7 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                                                                                                }
                                                                                                let (failure_code, data) = self.get_htlc_temp_fail_err_and_data(0x1000|7, short_chan_id, chan.get());
                                                                                                failed_forwards.push((htlc_source, payment_hash,
-                                                                                                       HTLCFailReason::Reason { failure_code, data },
+                                                                                                       HTLCFailReason::reason(failure_code, data),
                                                                                                        HTLCDestination::NextHopChannel { node_id: Some(chan.get().get_counterparty_node_id()), channel_id: forward_chan_id }
                                                                                                ));
                                                                                                continue;
@@ -3325,7 +3462,7 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                                                                                                incoming_packet_shared_secret: $htlc.prev_hop.incoming_packet_shared_secret,
                                                                                                phantom_shared_secret,
                                                                                        }), payment_hash,
-                                                                                       HTLCFailReason::Reason { failure_code: 0x4000 | 15, data: htlc_msat_height_data },
+                                                                                       HTLCFailReason::reason(0x4000 | 15, htlc_msat_height_data),
                                                                                        HTLCDestination::FailedPayment { payment_hash: $payment_hash },
                                                                                ));
                                                                        }
@@ -3474,7 +3611,7 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                }
 
                for (htlc_source, payment_hash, failure_reason, destination) in failed_forwards.drain(..) {
-                       self.fail_htlc_backwards_internal(htlc_source, &payment_hash, failure_reason, destination);
+                       self.fail_htlc_backwards_internal(&htlc_source, &payment_hash, &failure_reason, destination);
                }
                self.forward_htlcs(&mut phantom_receives);
 
@@ -3737,8 +3874,10 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                        });
 
                        for htlc_source in timed_out_mpp_htlcs.drain(..) {
+                               let source = HTLCSource::PreviousHopData(htlc_source.0.clone());
+                               let reason = HTLCFailReason::from_failure_code(23);
                                let receiver = HTLCDestination::FailedPayment { payment_hash: htlc_source.1 };
-                               self.fail_htlc_backwards_internal(HTLCSource::PreviousHopData(htlc_source.0.clone()), &htlc_source.1, HTLCFailReason::Reason { failure_code: 23, data: Vec::new() }, receiver );
+                               self.fail_htlc_backwards_internal(&source, &htlc_source.1, &reason, receiver);
                        }
 
                        for (err, counterparty_node_id) in handle_errors.drain(..) {
@@ -3773,10 +3912,10 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                                let mut htlc_msat_height_data = byte_utils::be64_to_array(htlc.value).to_vec();
                                htlc_msat_height_data.extend_from_slice(&byte_utils::be32_to_array(
                                                self.best_block.read().unwrap().height()));
-                               self.fail_htlc_backwards_internal(
-                                               HTLCSource::PreviousHopData(htlc.prev_hop), payment_hash,
-                                               HTLCFailReason::Reason { failure_code: 0x4000 | 15, data: htlc_msat_height_data },
-                                               HTLCDestination::FailedPayment { payment_hash: *payment_hash });
+                               let source = HTLCSource::PreviousHopData(htlc.prev_hop);
+                               let reason = HTLCFailReason::reason(0x4000 | 15, htlc_msat_height_data);
+                               let receiver = HTLCDestination::FailedPayment { payment_hash: *payment_hash };
+                               self.fail_htlc_backwards_internal(&source, &payment_hash, &reason, receiver);
                        }
                }
        }
@@ -3835,23 +3974,24 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                &self, mut htlcs_to_fail: Vec<(HTLCSource, PaymentHash)>, channel_id: [u8; 32],
                counterparty_node_id: &PublicKey
        ) {
-               for (htlc_src, payment_hash) in htlcs_to_fail.drain(..) {
-                       let (failure_code, onion_failure_data) =
-                               match self.channel_state.lock().unwrap().by_id.entry(channel_id) {
-                                       hash_map::Entry::Occupied(chan_entry) => {
-                                               self.get_htlc_inbound_temp_fail_err_and_data(0x1000|7, &chan_entry.get())
-                                       },
-                                       hash_map::Entry::Vacant(_) => (0x4000|10, Vec::new())
-                               };
+               let (failure_code, onion_failure_data) =
+                       match self.channel_state.lock().unwrap().by_id.entry(channel_id) {
+                               hash_map::Entry::Occupied(chan_entry) => {
+                                       self.get_htlc_inbound_temp_fail_err_and_data(0x1000|7, &chan_entry.get())
+                               },
+                               hash_map::Entry::Vacant(_) => (0x4000|10, Vec::new())
+                       };
 
+               for (htlc_src, payment_hash) in htlcs_to_fail.drain(..) {
+                       let reason = HTLCFailReason::reason(failure_code, onion_failure_data.clone());
                        let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id.clone()), channel_id };
-                       self.fail_htlc_backwards_internal(htlc_src, &payment_hash, HTLCFailReason::Reason { failure_code, data: onion_failure_data }, receiver);
+                       self.fail_htlc_backwards_internal(&htlc_src, &payment_hash, &reason, receiver);
                }
        }
 
        /// Fails an HTLC backwards to the sender of it to us.
        /// Note that we do not assume that channels corresponding to failed HTLCs are still available.
-       fn fail_htlc_backwards_internal(&self, source: HTLCSource, payment_hash: &PaymentHash, onion_error: HTLCFailReason,destination: HTLCDestination) {
+       fn fail_htlc_backwards_internal(&self, source: &HTLCSource, payment_hash: &PaymentHash, onion_error: &HTLCFailReason, destination: HTLCDestination) {
                #[cfg(debug_assertions)]
                {
                        // Ensure that the `channel_state` lock is not held when calling this function.
@@ -3870,13 +4010,13 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                // from block_connected which may run during initialization prior to the chain_monitor
                // being fully configured. See the docs for `ChannelManagerReadArgs` for more.
                match source {
-                       HTLCSource::OutboundRoute { ref path, session_priv, payment_id, ref payment_params, .. } => {
+                       HTLCSource::OutboundRoute { ref path, ref session_priv, ref payment_id, ref payment_params, .. } => {
                                let mut session_priv_bytes = [0; 32];
                                session_priv_bytes.copy_from_slice(&session_priv[..]);
                                let mut outbounds = self.pending_outbound_payments.lock().unwrap();
                                let mut all_paths_failed = false;
                                let mut full_failure_ev = None;
-                               if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) {
+                               if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(*payment_id) {
                                        if !payment.get_mut().remove(&session_priv_bytes, Some(&path)) {
                                                log_trace!(self.logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0));
                                                return;
@@ -3889,7 +4029,7 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                                                all_paths_failed = true;
                                                if payment.get().abandoned() {
                                                        full_failure_ev = Some(events::Event::PaymentFailed {
-                                                               payment_id,
+                                                               payment_id: *payment_id,
                                                                payment_hash: payment.get().payment_hash().expect("PendingOutboundPayments::RetriesExceeded always has a payment hash set"),
                                                        });
                                                        payment.remove();
@@ -3919,13 +4059,13 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                                                if self.payment_is_probe(payment_hash, &payment_id) {
                                                        if !payment_retryable {
                                                                events::Event::ProbeSuccessful {
-                                                                       payment_id,
+                                                                       payment_id: *payment_id,
                                                                        payment_hash: payment_hash.clone(),
                                                                        path: path.clone(),
                                                                }
                                                        } else {
                                                                events::Event::ProbeFailed {
-                                                                       payment_id,
+                                                                       payment_id: *payment_id,
                                                                        payment_hash: payment_hash.clone(),
                                                                        path: path.clone(),
                                                                        short_channel_id,
@@ -3939,7 +4079,7 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                                                                retry.as_mut().map(|r| r.payment_params.previously_failed_channels.push(scid));
                                                        }
                                                        events::Event::PaymentPathFailed {
-                                                               payment_id: Some(payment_id),
+                                                               payment_id: Some(*payment_id),
                                                                payment_hash: payment_hash.clone(),
                                                                payment_failed_permanently: !payment_retryable,
                                                                network_update,
@@ -3972,14 +4112,14 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
 
                                                if self.payment_is_probe(payment_hash, &payment_id) {
                                                        events::Event::ProbeFailed {
-                                                               payment_id,
+                                                               payment_id: *payment_id,
                                                                payment_hash: payment_hash.clone(),
                                                                path: path.clone(),
                                                                short_channel_id: Some(scid),
                                                        }
                                                } else {
                                                        events::Event::PaymentPathFailed {
-                                                               payment_id: Some(payment_id),
+                                                               payment_id: Some(*payment_id),
                                                                payment_hash: payment_hash.clone(),
                                                                payment_failed_permanently: false,
                                                                network_update: None,
@@ -3999,22 +4139,22 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                                pending_events.push(path_failure);
                                if let Some(ev) = full_failure_ev { pending_events.push(ev); }
                        },
-                       HTLCSource::PreviousHopData(HTLCPreviousHopData { short_channel_id, htlc_id, incoming_packet_shared_secret, phantom_shared_secret, outpoint }) => {
+                       HTLCSource::PreviousHopData(HTLCPreviousHopData { ref short_channel_id, ref htlc_id, ref incoming_packet_shared_secret, ref phantom_shared_secret, ref outpoint }) => {
                                let err_packet = match onion_error {
-                                       HTLCFailReason::Reason { failure_code, data } => {
+                                       HTLCFailReason::Reason { ref failure_code, ref data } => {
                                                log_trace!(self.logger, "Failing HTLC with payment_hash {} backwards from us with code {}", log_bytes!(payment_hash.0), failure_code);
                                                if let Some(phantom_ss) = phantom_shared_secret {
-                                                       let phantom_packet = onion_utils::build_failure_packet(&phantom_ss, failure_code, &data[..]).encode();
-                                                       let encrypted_phantom_packet = onion_utils::encrypt_failure_packet(&phantom_ss, &phantom_packet);
-                                                       onion_utils::encrypt_failure_packet(&incoming_packet_shared_secret, &encrypted_phantom_packet.data[..])
+                                                       let phantom_packet = onion_utils::build_failure_packet(phantom_ss, *failure_code, &data[..]).encode();
+                                                       let encrypted_phantom_packet = onion_utils::encrypt_failure_packet(phantom_ss, &phantom_packet);
+                                                       onion_utils::encrypt_failure_packet(incoming_packet_shared_secret, &encrypted_phantom_packet.data[..])
                                                } else {
-                                                       let packet = onion_utils::build_failure_packet(&incoming_packet_shared_secret, failure_code, &data[..]).encode();
-                                                       onion_utils::encrypt_failure_packet(&incoming_packet_shared_secret, &packet)
+                                                       let packet = onion_utils::build_failure_packet(incoming_packet_shared_secret, *failure_code, &data[..]).encode();
+                                                       onion_utils::encrypt_failure_packet(incoming_packet_shared_secret, &packet)
                                                }
                                        },
                                        HTLCFailReason::LightningError { err } => {
                                                log_trace!(self.logger, "Failing HTLC with payment_hash {} backwards with pre-built LightningError", log_bytes!(payment_hash.0));
-                                               onion_utils::encrypt_failure_packet(&incoming_packet_shared_secret, &err.data)
+                                               onion_utils::encrypt_failure_packet(incoming_packet_shared_secret, &err.data)
                                        }
                                };
 
@@ -4023,12 +4163,12 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                                if forward_htlcs.is_empty() {
                                        forward_event = Some(Duration::from_millis(MIN_HTLC_RELAY_HOLDING_CELL_MILLIS));
                                }
-                               match forward_htlcs.entry(short_channel_id) {
+                               match forward_htlcs.entry(*short_channel_id) {
                                        hash_map::Entry::Occupied(mut entry) => {
-                                               entry.get_mut().push(HTLCForwardInfo::FailHTLC { htlc_id, err_packet });
+                                               entry.get_mut().push(HTLCForwardInfo::FailHTLC { htlc_id: *htlc_id, err_packet });
                                        },
                                        hash_map::Entry::Vacant(entry) => {
-                                               entry.insert(vec!(HTLCForwardInfo::FailHTLC { htlc_id, err_packet }));
+                                               entry.insert(vec!(HTLCForwardInfo::FailHTLC { htlc_id: *htlc_id, err_packet }));
                                        }
                                }
                                mem::drop(forward_htlcs);
@@ -4040,7 +4180,7 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                                }
                                pending_events.push(events::Event::HTLCHandlingFailed {
                                        prev_channel_id: outpoint.to_channel_id(),
-                                       failed_next_destination: destination
+                                       failed_next_destination: destination,
                                });
                        },
                }
@@ -4169,10 +4309,10 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                                        let mut htlc_msat_height_data = byte_utils::be64_to_array(htlc.value).to_vec();
                                        htlc_msat_height_data.extend_from_slice(&byte_utils::be32_to_array(
                                                self.best_block.read().unwrap().height()));
-                                       self.fail_htlc_backwards_internal(
-                                               HTLCSource::PreviousHopData(htlc.prev_hop), &payment_hash,
-                                               HTLCFailReason::Reason { failure_code: 0x4000|15, data: htlc_msat_height_data },
-                                               HTLCDestination::FailedPayment { payment_hash } );
+                                       let source = HTLCSource::PreviousHopData(htlc.prev_hop);
+                                       let reason = HTLCFailReason::reason(0x4000 | 15, htlc_msat_height_data);
+                                       let receiver = HTLCDestination::FailedPayment { payment_hash };
+                                       self.fail_htlc_backwards_internal(&source, &payment_hash, &reason, receiver);
                                }
                        }
 
@@ -4496,7 +4636,7 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                self.finalize_claims(finalized_claims);
                for failure in pending_failures.drain(..) {
                        let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id), channel_id: funding_txo.to_channel_id() };
-                       self.fail_htlc_backwards_internal(failure.0, &failure.1, failure.2, receiver);
+                       self.fail_htlc_backwards_internal(&failure.0, &failure.1, &failure.2, receiver);
                }
        }
 
@@ -4864,7 +5004,8 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                };
                for htlc_source in dropped_htlcs.drain(..) {
                        let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id.clone()), channel_id: msg.channel_id };
-                       self.fail_htlc_backwards_internal(htlc_source.0, &htlc_source.1, HTLCFailReason::Reason { failure_code: 0x4000 | 8, data: Vec::new() }, receiver);
+                       let reason = HTLCFailReason::from_failure_code(0x4000 | 8);
+                       self.fail_htlc_backwards_internal(&htlc_source.0, &htlc_source.1, &reason, receiver);
                }
 
                let _ = handle_error!(self, result, *counterparty_node_id);
@@ -5009,7 +5150,7 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                                        let chan_err: ChannelError = ChannelError::Close("Got update_fail_malformed_htlc with BADONION not set".to_owned());
                                        try_chan_entry!(self, Err(chan_err), chan);
                                }
-                               try_chan_entry!(self, chan.get_mut().update_fail_malformed_htlc(&msg, HTLCFailReason::Reason { failure_code: msg.failure_code, data: Vec::new() }), chan);
+                               try_chan_entry!(self, chan.get_mut().update_fail_malformed_htlc(&msg, HTLCFailReason::from_failure_code(msg.failure_code)), chan);
                                Ok(())
                        },
                        hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel".to_owned(), msg.channel_id))
@@ -5067,28 +5208,82 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
        fn forward_htlcs(&self, per_source_pending_forwards: &mut [(u64, OutPoint, u128, Vec<(PendingHTLCInfo, u64)>)]) {
                for &mut (prev_short_channel_id, prev_funding_outpoint, prev_user_channel_id, ref mut pending_forwards) in per_source_pending_forwards {
                        let mut forward_event = None;
+                       let mut new_intercept_events = Vec::new();
+                       let mut failed_intercept_forwards = Vec::new();
                        if !pending_forwards.is_empty() {
-                               let mut forward_htlcs = self.forward_htlcs.lock().unwrap();
-                               if forward_htlcs.is_empty() {
-                                       forward_event = Some(Duration::from_millis(MIN_HTLC_RELAY_HOLDING_CELL_MILLIS))
-                               }
                                for (forward_info, prev_htlc_id) in pending_forwards.drain(..) {
-                                       match forward_htlcs.entry(match forward_info.routing {
-                                                       PendingHTLCRouting::Forward { short_channel_id, .. } => short_channel_id,
-                                                       PendingHTLCRouting::Receive { .. } => 0,
-                                                       PendingHTLCRouting::ReceiveKeysend { .. } => 0,
-                                       }) {
+                                       let scid = match forward_info.routing {
+                                               PendingHTLCRouting::Forward { short_channel_id, .. } => short_channel_id,
+                                               PendingHTLCRouting::Receive { .. } => 0,
+                                               PendingHTLCRouting::ReceiveKeysend { .. } => 0,
+                                       };
+                                       // Pull this now to avoid introducing a lock order with `forward_htlcs`.
+                                       let is_our_scid = self.short_to_chan_info.read().unwrap().contains_key(&scid);
+
+                                       let mut forward_htlcs = self.forward_htlcs.lock().unwrap();
+                                       let forward_htlcs_empty = forward_htlcs.is_empty();
+                                       match forward_htlcs.entry(scid) {
                                                hash_map::Entry::Occupied(mut entry) => {
                                                        entry.get_mut().push(HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
                                                                prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info }));
                                                },
                                                hash_map::Entry::Vacant(entry) => {
-                                                       entry.insert(vec!(HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
-                                                               prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info })));
+                                                       if !is_our_scid && forward_info.incoming_amt_msat.is_some() &&
+                                                          fake_scid::is_valid_intercept(&self.fake_scid_rand_bytes, scid, &self.genesis_hash)
+                                                       {
+                                                               let intercept_id = InterceptId(Sha256::hash(&forward_info.incoming_shared_secret).into_inner());
+                                                               let mut pending_intercepts = self.pending_intercepted_htlcs.lock().unwrap();
+                                                               match pending_intercepts.entry(intercept_id) {
+                                                                       hash_map::Entry::Vacant(entry) => {
+                                                                               new_intercept_events.push(events::Event::HTLCIntercepted {
+                                                                                       requested_next_hop_scid: scid,
+                                                                                       payment_hash: forward_info.payment_hash,
+                                                                                       inbound_amount_msat: forward_info.incoming_amt_msat.unwrap(),
+                                                                                       expected_outbound_amount_msat: forward_info.outgoing_amt_msat,
+                                                                                       intercept_id
+                                                                               });
+                                                                               entry.insert(PendingAddHTLCInfo {
+                                                                                       prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info });
+                                                                       },
+                                                                       hash_map::Entry::Occupied(_) => {
+                                                                               log_info!(self.logger, "Failed to forward incoming HTLC: detected duplicate intercepted payment over short channel id {}", scid);
+                                                                               let htlc_source = HTLCSource::PreviousHopData(HTLCPreviousHopData {
+                                                                                       short_channel_id: prev_short_channel_id,
+                                                                                       outpoint: prev_funding_outpoint,
+                                                                                       htlc_id: prev_htlc_id,
+                                                                                       incoming_packet_shared_secret: forward_info.incoming_shared_secret,
+                                                                                       phantom_shared_secret: None,
+                                                                               });
+
+                                                                               failed_intercept_forwards.push((htlc_source, forward_info.payment_hash,
+                                                                                               HTLCFailReason::from_failure_code(0x4000 | 10),
+                                                                                               HTLCDestination::InvalidForward { requested_forward_scid: scid },
+                                                                               ));
+                                                                       }
+                                                               }
+                                                       } else {
+                                                               // We don't want to generate a PendingHTLCsForwardable event if only intercepted
+                                                               // payments are being processed.
+                                                               if forward_htlcs_empty {
+                                                                       forward_event = Some(Duration::from_millis(MIN_HTLC_RELAY_HOLDING_CELL_MILLIS));
+                                                               }
+                                                               entry.insert(vec!(HTLCForwardInfo::AddHTLC(PendingAddHTLCInfo {
+                                                                       prev_short_channel_id, prev_funding_outpoint, prev_htlc_id, prev_user_channel_id, forward_info })));
+                                                       }
                                                }
                                        }
                                }
                        }
+
+                       for (htlc_source, payment_hash, failure_reason, destination) in failed_intercept_forwards.drain(..) {
+                               self.fail_htlc_backwards_internal(&htlc_source, &payment_hash, &failure_reason, destination);
+                       }
+
+                       if !new_intercept_events.is_empty() {
+                               let mut events = self.pending_events.lock().unwrap();
+                               events.append(&mut new_intercept_events);
+                       }
+
                        match forward_event {
                                Some(time) => {
                                        let mut pending_events = self.pending_events.lock().unwrap();
@@ -5156,7 +5351,7 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                        {
                                for failure in pending_failures.drain(..) {
                                        let receiver = HTLCDestination::NextHopChannel { node_id: Some(*counterparty_node_id), channel_id: channel_outpoint.to_channel_id() };
-                                       self.fail_htlc_backwards_internal(failure.0, &failure.1, failure.2, receiver);
+                                       self.fail_htlc_backwards_internal(&failure.0, &failure.1, &failure.2, receiver);
                                }
                                self.forward_htlcs(&mut [(short_channel_id, channel_outpoint, user_channel_id, pending_forwards)]);
                                self.finalize_claims(finalized_claim_htlcs);
@@ -5316,7 +5511,8 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                                                } else {
                                                        log_trace!(self.logger, "Failing HTLC with hash {} from our monitor", log_bytes!(htlc_update.payment_hash.0));
                                                        let receiver = HTLCDestination::NextHopChannel { node_id: counterparty_node_id, channel_id: funding_outpoint.to_channel_id() };
-                                                       self.fail_htlc_backwards_internal(htlc_update.source, &htlc_update.payment_hash, HTLCFailReason::Reason { failure_code: 0x4000 | 8, data: Vec::new() }, receiver);
+                                                       let reason = HTLCFailReason::from_failure_code(0x4000 | 8);
+                                                       self.fail_htlc_backwards_internal(&htlc_update.source, &htlc_update.payment_hash, &reason, receiver);
                                                }
                                        },
                                        MonitorEvent::CommitmentTxConfirmed(funding_outpoint) |
@@ -5690,6 +5886,23 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
                }
        }
 
+       /// Gets a fake short channel id for use in receiving intercepted payments. These fake scids are
+       /// used when constructing the route hints for HTLCs intended to be intercepted. See
+       /// [`ChannelManager::forward_intercepted_htlc`].
+       ///
+       /// Note that this method is not guaranteed to return unique values, you may need to call it a few
+       /// times to get a unique scid.
+       pub fn get_intercept_scid(&self) -> u64 {
+               let best_block_height = self.best_block.read().unwrap().height();
+               let short_to_chan_info = self.short_to_chan_info.read().unwrap();
+               loop {
+                       let scid_candidate = fake_scid::Namespace::Intercept.get_fake_scid(best_block_height, &self.genesis_hash, &self.fake_scid_rand_bytes, &self.keys_manager);
+                       // Ensure the generated scid doesn't conflict with a real channel.
+                       if short_to_chan_info.contains_key(&scid_candidate) { continue }
+                       return scid_candidate
+               }
+       }
+
        /// Gets inflight HTLC information by processing pending outbound payments that are in
        /// our channels. May be used during pathfinding to account for in-use channel liquidity.
        pub fn compute_inflight_htlcs(&self) -> InFlightHtlcs {
@@ -5985,9 +6198,8 @@ where
                                if let Ok((channel_ready_opt, mut timed_out_pending_htlcs, announcement_sigs)) = res {
                                        for (source, payment_hash) in timed_out_pending_htlcs.drain(..) {
                                                let (failure_code, data) = self.get_htlc_inbound_temp_fail_err_and_data(0x1000|14 /* expiry_too_soon */, &channel);
-                                               timed_out_htlcs.push((source, payment_hash, HTLCFailReason::Reason {
-                                                       failure_code, data,
-                                               }, HTLCDestination::NextHopChannel { node_id: Some(channel.get_counterparty_node_id()), channel_id: channel.channel_id() }));
+                                               timed_out_htlcs.push((source, payment_hash, HTLCFailReason::reason(failure_code, data),
+                                                       HTLCDestination::NextHopChannel { node_id: Some(channel.get_counterparty_node_id()), channel_id: channel.channel_id() }));
                                        }
                                        if let Some(channel_ready) = channel_ready_opt {
                                                send_channel_ready!(self, pending_msg_events, channel, channel_ready);
@@ -6074,21 +6286,43 @@ where
                                                let mut htlc_msat_height_data = byte_utils::be64_to_array(htlc.value).to_vec();
                                                htlc_msat_height_data.extend_from_slice(&byte_utils::be32_to_array(height));
 
-                                               timed_out_htlcs.push((HTLCSource::PreviousHopData(htlc.prev_hop.clone()), payment_hash.clone(), HTLCFailReason::Reason {
-                                                       failure_code: 0x4000 | 15,
-                                                       data: htlc_msat_height_data
-                                               }, HTLCDestination::FailedPayment { payment_hash: payment_hash.clone() }));
+                                               timed_out_htlcs.push((HTLCSource::PreviousHopData(htlc.prev_hop.clone()), payment_hash.clone(),
+                                                       HTLCFailReason::reason(0x4000 | 15, htlc_msat_height_data),
+                                                       HTLCDestination::FailedPayment { payment_hash: payment_hash.clone() }));
                                                false
                                        } else { true }
                                });
                                !htlcs.is_empty() // Only retain this entry if htlcs has at least one entry.
                        });
+
+                       let mut intercepted_htlcs = self.pending_intercepted_htlcs.lock().unwrap();
+                       intercepted_htlcs.retain(|_, htlc| {
+                               if height >= htlc.forward_info.outgoing_cltv_value - HTLC_FAIL_BACK_BUFFER {
+                                       let prev_hop_data = HTLCSource::PreviousHopData(HTLCPreviousHopData {
+                                               short_channel_id: htlc.prev_short_channel_id,
+                                               htlc_id: htlc.prev_htlc_id,
+                                               incoming_packet_shared_secret: htlc.forward_info.incoming_shared_secret,
+                                               phantom_shared_secret: None,
+                                               outpoint: htlc.prev_funding_outpoint,
+                                       });
+
+                                       let requested_forward_scid /* intercept scid */ = match htlc.forward_info.routing {
+                                               PendingHTLCRouting::Forward { short_channel_id, .. } => short_channel_id,
+                                               _ => unreachable!(),
+                                       };
+                                       timed_out_htlcs.push((prev_hop_data, htlc.forward_info.payment_hash,
+                                                       HTLCFailReason::from_failure_code(0x2000 | 2),
+                                                       HTLCDestination::InvalidForward { requested_forward_scid }));
+                                       log_trace!(self.logger, "Timing out intercepted HTLC with requested forward scid {}", requested_forward_scid);
+                                       false
+                               } else { true }
+                       });
                }
 
                self.handle_init_event_channel_failures(failed_channels);
 
                for (source, payment_hash, reason, destination) in timed_out_htlcs.drain(..) {
-                       self.fail_htlc_backwards_internal(source, &payment_hash, reason, destination);
+                       self.fail_htlc_backwards_internal(&source, &payment_hash, &reason, destination);
                }
        }
 
@@ -6991,8 +7225,15 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> Writeable for ChannelMana
                                _ => {},
                        }
                }
+
+               let mut pending_intercepted_htlcs = None;
+               let our_pending_intercepts = self.pending_intercepted_htlcs.lock().unwrap();
+               if our_pending_intercepts.len() != 0 {
+                       pending_intercepted_htlcs = Some(our_pending_intercepts);
+               }
                write_tlv_fields!(writer, {
                        (1, pending_outbound_payments_no_retry, required),
+                       (2, pending_intercepted_htlcs, option),
                        (3, pending_outbound_payments, required),
                        (5, self.our_network_pubkey, required),
                        (7, self.fake_scid_rand_bytes, required),
@@ -7306,12 +7547,14 @@ impl<'a, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
                // pending_outbound_payments_no_retry is for compatibility with 0.0.101 clients.
                let mut pending_outbound_payments_no_retry: Option<HashMap<PaymentId, HashSet<[u8; 32]>>> = None;
                let mut pending_outbound_payments = None;
+               let mut pending_intercepted_htlcs: Option<HashMap<InterceptId, PendingAddHTLCInfo>> = Some(HashMap::new());
                let mut received_network_pubkey: Option<PublicKey> = None;
                let mut fake_scid_rand_bytes: Option<[u8; 32]> = None;
                let mut probing_cookie_secret: Option<[u8; 32]> = None;
                let mut claimable_htlc_purposes = None;
                read_tlv_fields!(reader, {
                        (1, pending_outbound_payments_no_retry, option),
+                       (2, pending_intercepted_htlcs, option),
                        (3, pending_outbound_payments, option),
                        (5, received_network_pubkey, option),
                        (7, fake_scid_rand_bytes, option),
@@ -7534,6 +7777,7 @@ impl<'a, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
                        inbound_payment_key: expanded_inbound_key,
                        pending_inbound_payments: Mutex::new(pending_inbound_payments),
                        pending_outbound_payments: Mutex::new(pending_outbound_payments.unwrap()),
+                       pending_intercepted_htlcs: Mutex::new(pending_intercepted_htlcs.unwrap()),
 
                        forward_htlcs: Mutex::new(forward_htlcs),
                        claimable_htlcs: Mutex::new(claimable_htlcs),
@@ -7565,7 +7809,8 @@ impl<'a, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
                for htlc_source in failed_htlcs.drain(..) {
                        let (source, payment_hash, counterparty_node_id, channel_id) = htlc_source;
                        let receiver = HTLCDestination::NextHopChannel { node_id: Some(counterparty_node_id), channel_id };
-                       channel_manager.fail_htlc_backwards_internal(source, &payment_hash, HTLCFailReason::Reason { failure_code: 0x4000 | 8, data: Vec::new() }, receiver);
+                       let reason = HTLCFailReason::from_failure_code(0x4000 | 8);
+                       channel_manager.fail_htlc_backwards_internal(&source, &payment_hash, &reason, receiver);
                }
 
                //TODO: Broadcast channel update for closed channels, but only after we've made a
index d2f055dd5ec3d3ef1ecf9b139a2a223d3f17b384..56411a7de8939eeee782f36cc1dac6d5e168d8cf 100644 (file)
@@ -6023,7 +6023,7 @@ fn test_update_add_htlc_bolt2_sender_cltv_expiry_too_high() {
                .with_features(channelmanager::provided_invoice_features());
        let (mut route, our_payment_hash, _, our_payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], payment_params, 100000000, 0);
        route.paths[0].last_mut().unwrap().cltv_expiry_delta = 500000001;
-       unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret), PaymentId(our_payment_hash.0)), true, APIError::RouteError { ref err },
+       unwrap_send_err!(nodes[0].node.send_payment(&route, our_payment_hash, &Some(our_payment_secret), PaymentId(our_payment_hash.0)), true, APIError::InvalidRoute { ref err },
                assert_eq!(err, &"Channel CLTV overflowed?"));
 }
 
index 23dc556cfacca4f129d7968323f997a0f3c63692..6f85e2db8d60f2684bc867bb5636cfa22b00f168 100644 (file)
@@ -182,11 +182,11 @@ pub(super) fn build_onion_payloads(path: &Vec<RouteHop>, total_msat: u64, paymen
                });
                cur_value_msat += hop.fee_msat;
                if cur_value_msat >= 21000000 * 100000000 * 1000 {
-                       return Err(APIError::RouteError{err: "Channel fees overflowed?"});
+                       return Err(APIError::InvalidRoute{err: "Channel fees overflowed?"});
                }
                cur_cltv += hop.cltv_expiry_delta as u32;
                if cur_cltv >= 500000000 {
-                       return Err(APIError::RouteError{err: "Channel CLTV overflowed?"});
+                       return Err(APIError::InvalidRoute{err: "Channel CLTV overflowed?"});
                }
                last_short_channel_id = hop.short_channel_id;
        }
index baaa60a7a6a31b3c0151226456e590c762bc55d4..d9d22b6ca6b5bac82ac8c66b26b1e08124c4c878 100644 (file)
@@ -19,7 +19,8 @@ use crate::ln::channel::EXPIRE_PREV_CONFIG_TICKS;
 use crate::ln::channelmanager::{self, BREAKDOWN_TIMEOUT, ChannelManager, MPP_TIMEOUT_TICKS, MIN_CLTV_EXPIRY_DELTA, PaymentId, PaymentSendFailure, IDEMPOTENCY_TIMEOUT_TICKS};
 use crate::ln::msgs;
 use crate::ln::msgs::ChannelMessageHandler;
-use crate::routing::router::{PaymentParameters, get_route};
+use crate::routing::gossip::RoutingFees;
+use crate::routing::router::{get_route, PaymentParameters, RouteHint, RouteHintHop, RouteParameters};
 use crate::util::events::{ClosureReason, Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider};
 use crate::util::test_utils;
 use crate::util::errors::APIError;
@@ -1242,6 +1243,13 @@ fn abandoned_send_payment_idempotent() {
        claim_payment(&nodes[0], &[&nodes[1]], second_payment_preimage);
 }
 
+#[derive(PartialEq)]
+enum InterceptTest {
+       Forward,
+       Fail,
+       Timeout,
+}
+
 #[test]
 fn test_trivial_inflight_htlc_tracking(){
        // In this test, we test three scenarios:
@@ -1371,3 +1379,190 @@ fn test_holding_cell_inflight_htlcs() {
        // Clear pending events so test doesn't throw a "Had excess message on node..." error
        nodes[0].node.get_and_clear_pending_msg_events();
 }
+
+#[test]
+fn intercepted_payment() {
+       // Test that detecting an intercept scid on payment forward will signal LDK to generate an
+       // intercept event, which the LSP can then use to either (a) open a JIT channel to forward the
+       // payment or (b) fail the payment.
+       do_test_intercepted_payment(InterceptTest::Forward);
+       do_test_intercepted_payment(InterceptTest::Fail);
+       // Make sure that intercepted payments will be automatically failed back if too many blocks pass.
+       do_test_intercepted_payment(InterceptTest::Timeout);
+}
+
+fn do_test_intercepted_payment(test: InterceptTest) {
+       let chanmon_cfgs = create_chanmon_cfgs(3);
+       let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
+
+       let mut zero_conf_chan_config = test_default_channel_config();
+       zero_conf_chan_config.manually_accept_inbound_channels = true;
+       let mut intercept_forwards_config = test_default_channel_config();
+       intercept_forwards_config.accept_intercept_htlcs = true;
+       let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, Some(intercept_forwards_config), Some(zero_conf_chan_config)]);
+
+       let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
+       let scorer = test_utils::TestScorer::with_penalty(0);
+       let random_seed_bytes = chanmon_cfgs[0].keys_manager.get_secure_random_bytes();
+
+       let _ = create_announced_chan_between_nodes(&nodes, 0, 1, channelmanager::provided_init_features(), channelmanager::provided_init_features()).2;
+
+       let amt_msat = 100_000;
+       let intercept_scid = nodes[1].node.get_intercept_scid();
+       let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id())
+               .with_route_hints(vec![
+                       RouteHint(vec![RouteHintHop {
+                               src_node_id: nodes[1].node.get_our_node_id(),
+                               short_channel_id: intercept_scid,
+                               fees: RoutingFees {
+                                       base_msat: 1000,
+                                       proportional_millionths: 0,
+                               },
+                               cltv_expiry_delta: MIN_CLTV_EXPIRY_DELTA,
+                               htlc_minimum_msat: None,
+                               htlc_maximum_msat: None,
+                       }])
+               ])
+               .with_features(channelmanager::provided_invoice_features());
+       let route_params = RouteParameters {
+               payment_params,
+               final_value_msat: amt_msat,
+               final_cltv_expiry_delta: TEST_FINAL_CLTV,
+       };
+       let route = get_route(
+               &nodes[0].node.get_our_node_id(), &route_params.payment_params,
+               &nodes[0].network_graph.read_only(), None, route_params.final_value_msat,
+               route_params.final_cltv_expiry_delta, nodes[0].logger, &scorer, &random_seed_bytes
+       ).unwrap();
+
+       let (payment_hash, payment_secret) = nodes[2].node.create_inbound_payment(Some(amt_msat), 60 * 60).unwrap();
+       nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret), PaymentId(payment_hash.0)).unwrap();
+       let payment_event = {
+               {
+                       let mut added_monitors = nodes[0].chain_monitor.added_monitors.lock().unwrap();
+                       assert_eq!(added_monitors.len(), 1);
+                       added_monitors.clear();
+               }
+               let mut events = nodes[0].node.get_and_clear_pending_msg_events();
+               assert_eq!(events.len(), 1);
+               SendEvent::from_event(events.remove(0))
+       };
+       nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &payment_event.msgs[0]);
+       commitment_signed_dance!(nodes[1], nodes[0], &payment_event.commitment_msg, false, true);
+
+       // Check that we generate the PaymentIntercepted event when an intercept forward is detected.
+       let events = nodes[1].node.get_and_clear_pending_events();
+       assert_eq!(events.len(), 1);
+       let (intercept_id, expected_outbound_amount_msat) = match events[0] {
+               crate::util::events::Event::HTLCIntercepted {
+                       intercept_id, expected_outbound_amount_msat, payment_hash: pmt_hash, inbound_amount_msat, requested_next_hop_scid: short_channel_id
+               } => {
+                       assert_eq!(pmt_hash, payment_hash);
+                       assert_eq!(inbound_amount_msat, route.get_total_amount() + route.get_total_fees());
+                       assert_eq!(short_channel_id, intercept_scid);
+                       (intercept_id, expected_outbound_amount_msat)
+               },
+               _ => panic!()
+       };
+
+       // Check for unknown channel id error.
+       let unknown_chan_id_err = nodes[1].node.forward_intercepted_htlc(intercept_id, &[42; 32], nodes[2].node.get_our_node_id(), expected_outbound_amount_msat).unwrap_err();
+       assert_eq!(unknown_chan_id_err , APIError::ChannelUnavailable  { err: format!("Channel with id {} not found", log_bytes!([42; 32])) });
+
+       if test == InterceptTest::Fail {
+               // Ensure we can fail the intercepted payment back.
+               nodes[1].node.fail_intercepted_htlc(intercept_id).unwrap();
+               expect_pending_htlcs_forwardable_and_htlc_handling_failed_ignore!(nodes[1], vec![HTLCDestination::UnknownNextHop { requested_forward_scid: intercept_scid }]);
+               nodes[1].node.process_pending_htlc_forwards();
+               let update_fail = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
+               check_added_monitors!(&nodes[1], 1);
+               assert!(update_fail.update_fail_htlcs.len() == 1);
+               let fail_msg = update_fail.update_fail_htlcs[0].clone();
+               nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &fail_msg);
+               commitment_signed_dance!(nodes[0], nodes[1], update_fail.commitment_signed, false);
+
+               // Ensure the payment fails with the expected error.
+               let fail_conditions = PaymentFailedConditions::new()
+                       .blamed_scid(intercept_scid)
+                       .blamed_chan_closed(true)
+                       .expected_htlc_error_data(0x4000 | 10, &[]);
+               expect_payment_failed_conditions(&nodes[0], payment_hash, false, fail_conditions);
+       } else if test == InterceptTest::Forward {
+               // Check that we'll fail as expected when sending to a channel that isn't in `ChannelReady` yet.
+               let temp_chan_id = nodes[1].node.create_channel(nodes[2].node.get_our_node_id(), 100_000, 0, 42, None).unwrap();
+               let unusable_chan_err = nodes[1].node.forward_intercepted_htlc(intercept_id, &temp_chan_id, nodes[2].node.get_our_node_id(), expected_outbound_amount_msat).unwrap_err();
+               assert_eq!(unusable_chan_err , APIError::ChannelUnavailable { err: format!("Channel with id {} not fully established", log_bytes!(temp_chan_id)) });
+               assert_eq!(nodes[1].node.get_and_clear_pending_msg_events().len(), 1);
+
+               // Open the just-in-time channel so the payment can then be forwarded.
+               let (_, channel_id) = open_zero_conf_channel(&nodes[1], &nodes[2], None);
+
+               // Finally, forward the intercepted payment through and claim it.
+               nodes[1].node.forward_intercepted_htlc(intercept_id, &channel_id, nodes[2].node.get_our_node_id(), expected_outbound_amount_msat).unwrap();
+               expect_pending_htlcs_forwardable!(nodes[1]);
+
+               let payment_event = {
+                       {
+                               let mut added_monitors = nodes[1].chain_monitor.added_monitors.lock().unwrap();
+                               assert_eq!(added_monitors.len(), 1);
+                               added_monitors.clear();
+                       }
+                       let mut events = nodes[1].node.get_and_clear_pending_msg_events();
+                       assert_eq!(events.len(), 1);
+                       SendEvent::from_event(events.remove(0))
+               };
+               nodes[2].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &payment_event.msgs[0]);
+               commitment_signed_dance!(nodes[2], nodes[1], &payment_event.commitment_msg, false, true);
+               expect_pending_htlcs_forwardable!(nodes[2]);
+
+               let payment_preimage = nodes[2].node.get_payment_preimage(payment_hash, payment_secret).unwrap();
+               expect_payment_received!(&nodes[2], payment_hash, payment_secret, amt_msat, Some(payment_preimage), nodes[2].node.get_our_node_id());
+               do_claim_payment_along_route(&nodes[0], &vec!(&vec!(&nodes[1], &nodes[2])[..]), false, payment_preimage);
+               let events = nodes[0].node.get_and_clear_pending_events();
+               assert_eq!(events.len(), 2);
+               match events[0] {
+                       Event::PaymentSent { payment_preimage: ref ev_preimage, payment_hash: ref ev_hash, ref fee_paid_msat, .. } => {
+                               assert_eq!(payment_preimage, *ev_preimage);
+                               assert_eq!(payment_hash, *ev_hash);
+                               assert_eq!(fee_paid_msat, &Some(1000));
+                       },
+                       _ => panic!("Unexpected event")
+               }
+               match events[1] {
+                       Event::PaymentPathSuccessful { payment_hash: hash, .. } => {
+                               assert_eq!(hash, Some(payment_hash));
+                       },
+                       _ => panic!("Unexpected event")
+               }
+       } else if test == InterceptTest::Timeout {
+               let mut block = Block {
+                       header: BlockHeader { version: 0x20000000, prev_blockhash: nodes[0].best_block_hash(), merkle_root: TxMerkleNode::all_zeros(), time: 42, bits: 42, nonce: 42 },
+                       txdata: vec![],
+               };
+               connect_block(&nodes[0], &block);
+               connect_block(&nodes[1], &block);
+               for _ in 0..TEST_FINAL_CLTV {
+                       block.header.prev_blockhash = block.block_hash();
+                       connect_block(&nodes[0], &block);
+                       connect_block(&nodes[1], &block);
+               }
+               expect_pending_htlcs_forwardable_and_htlc_handling_failed!(nodes[1], vec![HTLCDestination::InvalidForward { requested_forward_scid: intercept_scid }]);
+               check_added_monitors!(nodes[1], 1);
+               let htlc_timeout_updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
+               assert!(htlc_timeout_updates.update_add_htlcs.is_empty());
+               assert_eq!(htlc_timeout_updates.update_fail_htlcs.len(), 1);
+               assert!(htlc_timeout_updates.update_fail_malformed_htlcs.is_empty());
+               assert!(htlc_timeout_updates.update_fee.is_none());
+
+               nodes[0].node.handle_update_fail_htlc(&nodes[1].node.get_our_node_id(), &htlc_timeout_updates.update_fail_htlcs[0]);
+               commitment_signed_dance!(nodes[0], nodes[1], htlc_timeout_updates.commitment_signed, false);
+               expect_payment_failed!(nodes[0], payment_hash, false, 0x2000 | 2, []);
+
+               // Check for unknown intercept id error.
+               let (_, channel_id) = open_zero_conf_channel(&nodes[1], &nodes[2], None);
+               let unknown_intercept_id_err = nodes[1].node.forward_intercepted_htlc(intercept_id, &channel_id, nodes[2].node.get_our_node_id(), expected_outbound_amount_msat).unwrap_err();
+               assert_eq!(unknown_intercept_id_err , APIError::APIMisuseError { err: format!("Payment with intercept id {} not found", log_bytes!(intercept_id.0)) });
+               let unknown_intercept_id_err = nodes[1].node.fail_intercepted_htlc(intercept_id).unwrap_err();
+               assert_eq!(unknown_intercept_id_err , APIError::APIMisuseError { err: format!("Payment with intercept id {} not found", log_bytes!(intercept_id.0)) });
+       }
+}
index 12306ac92ad13d68f4ee0be0f4c72b3b2161509a..09cee2a2e84e8b8acb2d63842c2ed054d387e9ee 100644 (file)
 //! interrogate it to get routes for your own payments.
 
 use bitcoin::secp256k1::PublicKey;
+use bitcoin::hashes::Hash;
+use bitcoin::hashes::sha256::Hash as Sha256;
 
-use crate::ln::channelmanager::ChannelDetails;
+use crate::ln::PaymentHash;
+use crate::ln::channelmanager::{ChannelDetails, PaymentId};
 use crate::ln::features::{ChannelFeatures, InvoiceFeatures, NodeFeatures};
 use crate::ln::msgs::{DecodeError, ErrorAction, LightningError, MAX_VALUE_MSAT};
 use crate::routing::gossip::{DirectedChannelInfo, EffectiveCapacity, ReadOnlyNetworkGraph, NetworkGraph, NodeId, RoutingFees};
-use crate::routing::scoring::{ChannelUsage, Score};
+use crate::routing::scoring::{ChannelUsage, LockableScore, Score};
 use crate::util::ser::{Writeable, Readable, Writer};
 use crate::util::logger::{Level, Logger};
 use crate::util::chacha20::ChaCha20;
 
 use crate::io;
 use crate::prelude::*;
+use crate::sync::Mutex;
 use alloc::collections::BinaryHeap;
 use core::cmp;
 use core::ops::Deref;
 
+/// 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.
+       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(&*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
+               )
+       }
+
+       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);
+       }
+}
+
 /// A trait defining behavior for routing a payment.
 pub trait Router {
        /// Finds a [`Route`] between `payer` and `payee` for a payment with the given values.
@@ -36,6 +100,83 @@ pub trait Router {
                &self, payer: &PublicKey, route_params: &RouteParameters,
                first_hops: Option<&[&ChannelDetails]>, inflight_htlcs: InFlightHtlcs
        ) -> Result<Route, LightningError>;
+       /// Finds a [`Route`] between `payer` and `payee` for a payment with the given values. Includes
+       /// `PaymentHash` and `PaymentId` to be able to correlate the request with a specific payment.
+       fn find_route_with_id(
+               &self, payer: &PublicKey, route_params: &RouteParameters,
+               first_hops: Option<&[&ChannelDetails]>, inflight_htlcs: InFlightHtlcs,
+               _payment_hash: PaymentHash, _payment_id: PaymentId
+       ) -> Result<Route, LightningError> {
+               self.find_route(payer, route_params, first_hops, inflight_htlcs)
+       }
+       /// Lets the router know that payment through a specific path has failed.
+       fn notify_payment_path_failed(&self, path: &[&RouteHop], short_channel_id: u64);
+       /// Lets the router know that payment through a specific path was successful.
+       fn notify_payment_path_successful(&self, path: &[&RouteHop]);
+       /// Lets the router know that a payment probe was successful.
+       fn notify_payment_probe_successful(&self, path: &[&RouteHop]);
+       /// Lets the router know that a payment probe failed.
+       fn notify_payment_probe_failed(&self, path: &[&RouteHop], short_channel_id: u64);
+}
+
+/// [`Score`] implementation that factors in in-flight HTLC liquidity.
+///
+/// Useful for custom [`Router`] implementations to wrap their [`Score`] on-the-fly when calling
+/// [`find_route`].
+///
+/// [`Score`]: crate::routing::scoring::Score
+pub 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> {
+       /// Initialize a new `ScorerAccountingForInFlightHtlcs`.
+       pub fn new(scorer: &'a mut S, inflight_htlcs: InFlightHtlcs) -> Self {
+               ScorerAccountingForInFlightHtlcs {
+                       scorer,
+                       inflight_htlcs
+               }
+       }
+}
+
+#[cfg(c_bindings)]
+impl<'a, S:Score> Writeable for ScorerAccountingForInFlightHtlcs<'a, S> {
+       fn write<W: Writer>(&self, writer: &mut W) -> Result<(), 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_liquidity) = self.inflight_htlcs.used_liquidity_msat(
+                       source, target, short_channel_id
+               ) {
+                       let usage = ChannelUsage {
+                               inflight_htlc_msat: usage.inflight_htlc_msat + used_liquidity,
+                               ..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) {
+               self.scorer.payment_path_failed(path, short_channel_id)
+       }
+
+       fn payment_path_successful(&mut self, path: &[&RouteHop]) {
+               self.scorer.payment_path_successful(path)
+       }
+
+       fn probe_failed(&mut self, path: &[&RouteHop], short_channel_id: u64) {
+               self.scorer.probe_failed(path, short_channel_id)
+       }
+
+       fn probe_successful(&mut self, path: &[&RouteHop]) {
+               self.scorer.probe_successful(path)
+       }
 }
 
 /// A data structure for tracking in-flight HTLCs. May be used during pathfinding to account for
index 26c8da7543615777ec814a88adff9163b147ddd0..c9c76f4e9fa5480e2c8a6be0a642b2192db903ca 100644 (file)
@@ -505,6 +505,17 @@ pub struct UserConfig {
        /// [`msgs::OpenChannel`]: crate::ln::msgs::OpenChannel
        /// [`msgs::AcceptChannel`]: crate::ln::msgs::AcceptChannel
        pub manually_accept_inbound_channels: bool,
+       ///  If this is set to true, LDK will intercept HTLCs that are attempting to be forwarded over
+       ///  fake short channel ids generated via [`ChannelManager::get_intercept_scid`]. Upon HTLC
+       ///  intercept, LDK will generate an [`Event::HTLCIntercepted`] which MUST be handled by the user.
+       ///
+       ///  Setting this to true may break backwards compatibility with LDK versions < 0.0.113.
+       ///
+       ///  Default value: false.
+       ///
+       /// [`ChannelManager::get_intercept_scid`]: crate::ln::channelmanager::ChannelManager::get_intercept_scid
+       /// [`Event::HTLCIntercepted`]: crate::util::events::Event::HTLCIntercepted
+       pub accept_intercept_htlcs: bool,
 }
 
 impl Default for UserConfig {
@@ -516,6 +527,7 @@ impl Default for UserConfig {
                        accept_forwards_to_priv_channels: false,
                        accept_inbound_channels: true,
                        manually_accept_inbound_channels: false,
+                       accept_intercept_htlcs: false,
                }
        }
 }
index 092f104b1a7a0b8897a094d21ec9c0a29f5d2cd7..17e55a9799bb503c987782a2937c52a00634abdf 100644 (file)
@@ -35,7 +35,7 @@ pub enum APIError {
        },
        /// A malformed Route was provided (eg overflowed value, node id mismatch, overly-looped route,
        /// too-many-hops, etc).
-       RouteError {
+       InvalidRoute {
                /// A human-readable error message
                err: &'static str
        },
@@ -74,7 +74,7 @@ impl fmt::Debug for APIError {
                match *self {
                        APIError::APIMisuseError {ref err} => write!(f, "Misuse error: {}", err),
                        APIError::FeeRateTooHigh {ref err, ref feerate} => write!(f, "{} feerate: {}", err, feerate),
-                       APIError::RouteError {ref err} => write!(f, "Route error: {}", err),
+                       APIError::InvalidRoute {ref err} => write!(f, "Invalid route provided: {}", err),
                        APIError::ChannelUnavailable {ref err} => write!(f, "Channel unavailable: {}", err),
                        APIError::MonitorUpdateInProgress => f.write_str("Client indicated a channel monitor update is in progress but not yet complete"),
                        APIError::IncompatibleShutdownScript { ref script } => {
index 6181340001bfcd784bc2e262ee629c1acb362ad9..18058bc03a8e10505ce0f966729cb56d66a7d147 100644 (file)
@@ -17,7 +17,7 @@
 use crate::chain::keysinterface::SpendableOutputDescriptor;
 #[cfg(anchors)]
 use crate::ln::chan_utils::HTLCOutputInCommitment;
-use crate::ln::channelmanager::PaymentId;
+use crate::ln::channelmanager::{InterceptId, PaymentId};
 use crate::ln::channel::FUNDING_CONF_DEADLINE_BLOCKS;
 use crate::ln::features::ChannelTypeFeatures;
 use crate::ln::msgs;
@@ -182,6 +182,12 @@ pub enum HTLCDestination {
                /// Short channel id we are requesting to forward an HTLC to.
                requested_forward_scid: u64,
        },
+       /// We couldn't forward to the outgoing scid. An example would be attempting to send a duplicate
+       /// intercept HTLC.
+       InvalidForward {
+               /// Short channel id we are requesting to forward an HTLC to.
+               requested_forward_scid: u64
+       },
        /// Failure scenario where an HTLC may have been forwarded to be intended for us,
        /// but is invalid for some reason, so we reject it.
        ///
@@ -200,12 +206,15 @@ impl_writeable_tlv_based_enum_upgradable!(HTLCDestination,
                (0, node_id, required),
                (2, channel_id, required),
        },
+       (1, InvalidForward) => {
+               (0, requested_forward_scid, required),
+       },
        (2, UnknownNextHop) => {
                (0, requested_forward_scid, required),
        },
        (4, FailedPayment) => {
                (0, payment_hash, required),
-       }
+       },
 );
 
 #[cfg(anchors)]
@@ -288,6 +297,22 @@ pub enum BumpTransactionEvent {
        },
 }
 
+/// Will be used in [`Event::HTLCIntercepted`] to identify the next hop in the HTLC's path.
+/// Currently only used in serialization for the sake of maintaining compatibility. More variants
+/// will be added for general-purpose HTLC forward intercepts as well as trampoline forward
+/// intercepts in upcoming work.
+enum InterceptNextHop {
+       FakeScid {
+               requested_next_hop_scid: u64,
+       },
+}
+
+impl_writeable_tlv_based_enum!(InterceptNextHop,
+       (0, FakeScid) => {
+               (0, requested_next_hop_scid, required),
+       };
+);
+
 /// An Event which you should probably take some action in response to.
 ///
 /// Note that while Writeable and Readable are implemented for Event, you probably shouldn't use
@@ -585,6 +610,38 @@ pub enum Event {
                /// now + 5*time_forwardable).
                time_forwardable: Duration,
        },
+       /// Used to indicate that we've intercepted an HTLC forward. This event will only be generated if
+       /// you've encoded an intercept scid in the receiver's invoice route hints using
+       /// [`ChannelManager::get_intercept_scid`] and have set [`UserConfig::accept_intercept_htlcs`].
+       ///
+       /// [`ChannelManager::forward_intercepted_htlc`] or
+       /// [`ChannelManager::fail_intercepted_htlc`] MUST be called in response to this event. See
+       /// their docs for more information.
+       ///
+       /// [`ChannelManager::get_intercept_scid`]: crate::ln::channelmanager::ChannelManager::get_intercept_scid
+       /// [`UserConfig::accept_intercept_htlcs`]: crate::util::config::UserConfig::accept_intercept_htlcs
+       /// [`ChannelManager::forward_intercepted_htlc`]: crate::ln::channelmanager::ChannelManager::forward_intercepted_htlc
+       /// [`ChannelManager::fail_intercepted_htlc`]: crate::ln::channelmanager::ChannelManager::fail_intercepted_htlc
+       HTLCIntercepted {
+               /// An id to help LDK identify which HTLC is being forwarded or failed.
+               intercept_id: InterceptId,
+               /// The fake scid that was programmed as the next hop's scid, generated using
+               /// [`ChannelManager::get_intercept_scid`].
+               ///
+               /// [`ChannelManager::get_intercept_scid`]: crate::ln::channelmanager::ChannelManager::get_intercept_scid
+               requested_next_hop_scid: u64,
+               /// The payment hash used for this HTLC.
+               payment_hash: PaymentHash,
+               /// How many msats were received on the inbound edge of this HTLC.
+               inbound_amount_msat: u64,
+               /// How many msats the payer intended to route to the next node. Depending on the reason you are
+               /// intercepting this payment, you might take a fee by forwarding less than this amount.
+               ///
+               /// Note that LDK will NOT check that expected fees were factored into this value. You MUST
+               /// check that whatever fee you want has been included here or subtract it as required. Further,
+               /// LDK will not stop you from forwarding more than you received.
+               expected_outbound_amount_msat: u64,
+       },
        /// Used to indicate that an output which you should know how to spend was confirmed on chain
        /// and is now spendable.
        /// Such an output will *not* ever be spent by rust-lightning, and are not at risk of your
@@ -825,6 +882,17 @@ impl Writeable for Event {
                                        (0, WithoutLength(outputs), required),
                                });
                        },
+                       &Event::HTLCIntercepted { requested_next_hop_scid, payment_hash, inbound_amount_msat, expected_outbound_amount_msat, intercept_id } => {
+                               6u8.write(writer)?;
+                               let intercept_scid = InterceptNextHop::FakeScid { requested_next_hop_scid };
+                               write_tlv_fields!(writer, {
+                                       (0, intercept_id, required),
+                                       (2, intercept_scid, required),
+                                       (4, payment_hash, required),
+                                       (6, inbound_amount_msat, required),
+                                       (8, expected_outbound_amount_msat, required),
+                               });
+                       }
                        &Event::PaymentForwarded { fee_earned_msat, prev_channel_id, claim_from_onchain_tx, next_channel_id } => {
                                7u8.write(writer)?;
                                write_tlv_fields!(writer, {
@@ -1054,6 +1122,30 @@ impl MaybeReadable for Event {
                                };
                                f()
                        },
+                       6u8 => {
+                               let mut payment_hash = PaymentHash([0; 32]);
+                               let mut intercept_id = InterceptId([0; 32]);
+                               let mut requested_next_hop_scid = InterceptNextHop::FakeScid { requested_next_hop_scid: 0 };
+                               let mut inbound_amount_msat = 0;
+                               let mut expected_outbound_amount_msat = 0;
+                               read_tlv_fields!(reader, {
+                                       (0, intercept_id, required),
+                                       (2, requested_next_hop_scid, required),
+                                       (4, payment_hash, required),
+                                       (6, inbound_amount_msat, required),
+                                       (8, expected_outbound_amount_msat, required),
+                               });
+                               let next_scid = match requested_next_hop_scid {
+                                       InterceptNextHop::FakeScid { requested_next_hop_scid: scid } => scid
+                               };
+                               Ok(Some(Event::HTLCIntercepted {
+                                       payment_hash,
+                                       requested_next_hop_scid: next_scid,
+                                       inbound_amount_msat,
+                                       expected_outbound_amount_msat,
+                                       intercept_id,
+                               }))
+                       },
                        7u8 => {
                                let f = || {
                                        let mut fee_earned_msat = None;
index 40c042a27ff702c457a1c856db2097720cd4caf3..31e53239410cc69ece83795bb3456971a855aaf3 100644 (file)
 
 use core::ops::Deref;
 use bitcoin::hashes::hex::ToHex;
-use crate::io::{self};
+use crate::io;
 use crate::routing::scoring::WriteableScore;
 
-use crate::{chain::{keysinterface::{Sign, KeysInterface}, self, transaction::{OutPoint}, chaininterface::{BroadcasterInterface, FeeEstimator}, chainmonitor::{Persist, MonitorUpdateId}, channelmonitor::{ChannelMonitor, ChannelMonitorUpdate}}, ln::channelmanager::ChannelManager, routing::gossip::NetworkGraph};
+use crate::chain;
+use crate::chain::chaininterface::{BroadcasterInterface, FeeEstimator};
+use crate::chain::chainmonitor::{Persist, MonitorUpdateId};
+use crate::chain::keysinterface::{Sign, KeysInterface};
+use crate::chain::transaction::OutPoint;
+use crate::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate};
+use crate::ln::channelmanager::ChannelManager;
+use crate::routing::gossip::NetworkGraph;
 use super::{logger::Logger, ser::Writeable};
 
 /// Trait for a key-value store for persisting some writeable object at some key
index 651b36ef32c3ce5a7d4382be73562733a5fb8a4b..1d951b8f936e49acc2cf51a028725a19c360eef9 100644 (file)
@@ -63,6 +63,8 @@ pub fn scid_from_parts(block: u64, tx_index: u64, vout_index: u64) -> Result<u64
 /// LDK has multiple reasons to generate fake short channel ids:
 /// 1) outbound SCID aliases we use for private channels
 /// 2) phantom node payments, to get an scid for the phantom node's phantom channel
+/// 3) payments intended to be intercepted will route using a fake scid (this is typically used so
+///    the forwarding node can open a JIT channel to the next hop)
 pub(crate) mod fake_scid {
        use bitcoin::hash_types::BlockHash;
        use bitcoin::hashes::hex::FromHex;
@@ -91,6 +93,7 @@ pub(crate) mod fake_scid {
        pub(crate) enum Namespace {
                Phantom,
                OutboundAlias,
+               Intercept
        }
 
        impl Namespace {
@@ -150,7 +153,7 @@ pub(crate) mod fake_scid {
                }
        }
 
-       /// Returns whether the given fake scid falls into the given namespace.
+       /// Returns whether the given fake scid falls into the phantom namespace.
        pub fn is_valid_phantom(fake_scid_rand_bytes: &[u8; 32], scid: u64, genesis_hash: &BlockHash) -> bool {
                let block_height = scid_utils::block_from_scid(&scid);
                let tx_index = scid_utils::tx_index_from_scid(&scid);
@@ -160,11 +163,21 @@ pub(crate) mod fake_scid {
                        && valid_vout == scid_utils::vout_from_scid(&scid) as u8
        }
 
+       /// Returns whether the given fake scid falls into the intercept namespace.
+       pub fn is_valid_intercept(fake_scid_rand_bytes: &[u8; 32], scid: u64, genesis_hash: &BlockHash) -> bool {
+               let block_height = scid_utils::block_from_scid(&scid);
+               let tx_index = scid_utils::tx_index_from_scid(&scid);
+               let namespace = Namespace::Intercept;
+               let valid_vout = namespace.get_encrypted_vout(block_height, tx_index, fake_scid_rand_bytes);
+               block_height >= segwit_activation_height(genesis_hash)
+                       && valid_vout == scid_utils::vout_from_scid(&scid) as u8
+       }
+
        #[cfg(test)]
        mod tests {
                use bitcoin::blockdata::constants::genesis_block;
                use bitcoin::network::constants::Network;
-               use crate::util::scid_utils::fake_scid::{is_valid_phantom, MAINNET_SEGWIT_ACTIVATION_HEIGHT, MAX_TX_INDEX, MAX_NAMESPACES, Namespace, NAMESPACE_ID_BITMASK, segwit_activation_height, TEST_SEGWIT_ACTIVATION_HEIGHT};
+               use crate::util::scid_utils::fake_scid::{is_valid_intercept, is_valid_phantom, MAINNET_SEGWIT_ACTIVATION_HEIGHT, MAX_TX_INDEX, MAX_NAMESPACES, Namespace, NAMESPACE_ID_BITMASK, segwit_activation_height, TEST_SEGWIT_ACTIVATION_HEIGHT};
                use crate::util::scid_utils;
                use crate::util::test_utils;
                use crate::sync::Arc;
@@ -174,6 +187,10 @@ pub(crate) mod fake_scid {
                        let phantom_namespace = Namespace::Phantom;
                        assert!((phantom_namespace as u8) < MAX_NAMESPACES);
                        assert!((phantom_namespace as u8) <= NAMESPACE_ID_BITMASK);
+
+                       let intercept_namespace = Namespace::Intercept;
+                       assert!((intercept_namespace as u8) < MAX_NAMESPACES);
+                       assert!((intercept_namespace as u8) <= NAMESPACE_ID_BITMASK);
                }
 
                #[test]
@@ -203,6 +220,18 @@ pub(crate) mod fake_scid {
                        assert!(!is_valid_phantom(&fake_scid_rand_bytes, invalid_fake_scid, &testnet_genesis));
                }
 
+               #[test]
+               fn test_is_valid_intercept() {
+                       let namespace = Namespace::Intercept;
+                       let fake_scid_rand_bytes = [0; 32];
+                       let testnet_genesis = genesis_block(Network::Testnet).header.block_hash();
+                       let valid_encrypted_vout = namespace.get_encrypted_vout(0, 0, &fake_scid_rand_bytes);
+                       let valid_fake_scid = scid_utils::scid_from_parts(1, 0, valid_encrypted_vout as u64).unwrap();
+                       assert!(is_valid_intercept(&fake_scid_rand_bytes, valid_fake_scid, &testnet_genesis));
+                       let invalid_fake_scid = scid_utils::scid_from_parts(1, 0, 12).unwrap();
+                       assert!(!is_valid_intercept(&fake_scid_rand_bytes, invalid_fake_scid, &testnet_genesis));
+               }
+
                #[test]
                fn test_get_fake_scid() {
                        let mainnet_genesis = genesis_block(Network::Bitcoin).header.block_hash();