Implement sending keysend payments (to public nodes)
[rust-lightning] / lightning / src / ln / channelmanager.rs
index 19a8ad082f87a5191f78df8f744675b2fdb8edce..cec068a10429b0c5ee94e51fa4e7d919fa949950 100644 (file)
@@ -157,14 +157,20 @@ pub(crate) struct HTLCPreviousHopData {
        outpoint: OutPoint,
 }
 
-struct ClaimableHTLC {
-       prev_hop: HTLCPreviousHopData,
-       value: u64,
+enum OnionPayload {
        /// Contains a total_msat (which may differ from value if this is a Multi-Path Payment) and a
        /// payment_secret which prevents path-probing attacks and can associate different HTLCs which
        /// are part of the same payment.
-       payment_data: msgs::FinalOnionHopData,
+       Invoice(msgs::FinalOnionHopData),
+       /// Contains the payer-provided preimage.
+       Spontaneous(PaymentPreimage),
+}
+
+struct ClaimableHTLC {
+       prev_hop: HTLCPreviousHopData,
        cltv_expiry: u32,
+       value: u64,
+       onion_payload: OnionPayload,
 }
 
 /// Tracks the inbound corresponding to an outbound HTLC
@@ -1475,15 +1481,37 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                                return_err!("Upstream node set CLTV to the wrong value", 18, &byte_utils::be32_to_array(msg.cltv_expiry));
                        }
 
-                       let payment_data = match next_hop_data.format {
-                               msgs::OnionHopDataFormat::Legacy { .. } => None,
+                       let routing = match next_hop_data.format {
+                               msgs::OnionHopDataFormat::Legacy { .. } => return_err!("We require payment_secrets", 0x4000|0x2000|3, &[0;0]),
                                msgs::OnionHopDataFormat::NonFinalNode { .. } => return_err!("Got non final data with an HMAC of 0", 0x4000 | 22, &[0;0]),
-                               msgs::OnionHopDataFormat::FinalNode { payment_data, .. } => payment_data,
-                       };
+                               msgs::OnionHopDataFormat::FinalNode { payment_data, keysend_preimage } => {
+                                       if payment_data.is_some() && keysend_preimage.is_some() {
+                                               return_err!("We don't support MPP keysend payments", 0x4000|22, &[0;0]);
+                                       } else if let Some(data) = payment_data {
+                                               PendingHTLCRouting::Receive {
+                                                       payment_data: data,
+                                                       incoming_cltv_expiry: msg.cltv_expiry,
+                                               }
+                                       } else if let Some(payment_preimage) = keysend_preimage {
+                                               // We need to check that the sender knows the keysend preimage before processing this
+                                               // payment further. Otherwise, an intermediary routing hop forwarding non-keysend-HTLC X
+                                               // could discover the final destination of X, by probing the adjacent nodes on the route
+                                               // with a keysend payment of identical payment hash to X and observing the processing
+                                               // time discrepancies due to a hash collision with X.
+                                               let hashed_preimage = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());
+                                               if hashed_preimage != msg.payment_hash {
+                                                       return_err!("Payment preimage didn't match payment hash", 0x4000|22, &[0;0]);
+                                               }
 
-                       if payment_data.is_none() {
-                               return_err!("We require payment_secrets", 0x4000|0x2000|3, &[0;0]);
-                       }
+                                               PendingHTLCRouting::ReceiveKeysend {
+                                                       payment_preimage,
+                                                       incoming_cltv_expiry: msg.cltv_expiry,
+                                               }
+                                       } else {
+                                               return_err!("We require payment_secrets", 0x4000|0x2000|3, &[0;0]);
+                                       }
+                               },
+                       };
 
                        // Note that we could obviously respond immediately with an update_fulfill_htlc
                        // message, however that would leak that we are the recipient of this payment, so
@@ -1491,10 +1519,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                        // delay) once they've send us a commitment_signed!
 
                        PendingHTLCStatus::Forward(PendingHTLCInfo {
-                               routing: PendingHTLCRouting::Receive {
-                                       payment_data: payment_data.unwrap(),
-                                       incoming_cltv_expiry: msg.cltv_expiry,
-                               },
+                               routing,
                                payment_hash: msg.payment_hash.clone(),
                                incoming_shared_secret: shared_secret,
                                amt_to_forward: next_hop_data.amt_to_forward,
@@ -1693,7 +1718,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
        }
 
        // Only public for testing, this should otherwise never be called direcly
-       pub(crate) fn send_payment_along_path(&self, path: &Vec<RouteHop>, payment_hash: &PaymentHash, payment_secret: &Option<PaymentSecret>, total_value: u64, cur_height: u32) -> Result<(), APIError> {
+       pub(crate) fn send_payment_along_path(&self, path: &Vec<RouteHop>, payment_hash: &PaymentHash, payment_secret: &Option<PaymentSecret>, total_value: u64, cur_height: u32, keysend_preimage: &Option<PaymentPreimage>) -> Result<(), APIError> {
                log_trace!(self.logger, "Attempting to send payment for path with next hop {}", path.first().unwrap().short_channel_id);
                let prng_seed = self.keys_manager.get_secure_random_bytes();
                let session_priv_bytes = self.keys_manager.get_secure_random_bytes();
@@ -1701,7 +1726,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
 
                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"})?;
-               let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(path, total_value, payment_secret, cur_height)?;
+               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"});
                }
@@ -1810,6 +1835,10 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
        /// bit set (either as required or as available). If multiple paths are present in the Route,
        /// we assume the invoice had the basic_mpp feature set.
        pub fn send_payment(&self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>) -> Result<(), PaymentSendFailure> {
+               self.send_payment_internal(route, payment_hash, payment_secret, None)
+       }
+
+       fn send_payment_internal(&self, route: &Route, payment_hash: PaymentHash, payment_secret: &Option<PaymentSecret>, keysend_preimage: Option<PaymentPreimage>) -> Result<(), PaymentSendFailure> {
                if route.paths.len() < 1 {
                        return Err(PaymentSendFailure::ParameterError(APIError::RouteError{err: "There must be at least one path to send over"}));
                }
@@ -1843,7 +1872,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                let cur_height = self.best_block.read().unwrap().height() + 1;
                let mut results = Vec::new();
                for path in route.paths.iter() {
-                       results.push(self.send_payment_along_path(&path, &payment_hash, payment_secret, total_value, cur_height));
+                       results.push(self.send_payment_along_path(&path, &payment_hash, payment_secret, total_value, cur_height, &keysend_preimage));
                }
                let mut has_ok = false;
                let mut has_err = false;
@@ -1867,6 +1896,28 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                }
        }
 
+       /// Send a spontaneous payment, which is a payment that does not require the recipient to have
+       /// generated an invoice. Optionally, you may specify the preimage. If you do choose to specify
+       /// the preimage, it must be a cryptographically secure random value that no intermediate node
+       /// would be able to guess -- otherwise, an intermediate node may claim the payment and it will
+       /// never reach the recipient.
+       ///
+       /// Similar to regular payments, you MUST NOT reuse a `payment_preimage` value. See
+       /// [`send_payment`] for more information about the risks of duplicate preimage usage.
+       ///
+       /// [`send_payment`]: Self::send_payment
+       pub fn send_spontaneous_payment(&self, route: &Route, payment_preimage: Option<PaymentPreimage>) -> Result<PaymentHash, PaymentSendFailure> {
+               let preimage = match payment_preimage {
+                       Some(p) => p,
+                       None => PaymentPreimage(self.keys_manager.get_secure_random_bytes()),
+               };
+               let payment_hash = PaymentHash(Sha256::hash(&preimage.0).into_inner());
+               match self.send_payment_internal(route, payment_hash, &None, Some(preimage)) {
+                       Ok(()) => Ok(payment_hash),
+                       Err(e) => Err(e)
+               }
+       }
+
        /// Handles the generation of a funding transaction, optionally (for tests) with a function
        /// which checks the correctness of the funding transaction given the associated channel.
        fn funding_transaction_generated_intern<FundingOutput: Fn(&Channel<Signer>, &Transaction) -> Result<OutPoint, APIError>>
@@ -2252,9 +2303,17 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                                        for forward_info in pending_forwards.drain(..) {
                                                match forward_info {
                                                        HTLCForwardInfo::AddHTLC { prev_short_channel_id, prev_htlc_id, forward_info: PendingHTLCInfo {
-                                                                       routing: PendingHTLCRouting::Receive { payment_data, incoming_cltv_expiry },
-                                                                       incoming_shared_secret, payment_hash, amt_to_forward, .. },
+                                                                       routing, incoming_shared_secret, payment_hash, amt_to_forward, .. },
                                                                        prev_funding_outpoint } => {
+                                                               let (cltv_expiry, onion_payload) = match routing {
+                                                                       PendingHTLCRouting::Receive { payment_data, incoming_cltv_expiry } =>
+                                                                               (incoming_cltv_expiry, OnionPayload::Invoice(payment_data)),
+                                                                       PendingHTLCRouting::ReceiveKeysend { payment_preimage, incoming_cltv_expiry } =>
+                                                                               (incoming_cltv_expiry, OnionPayload::Spontaneous(payment_preimage)),
+                                                                       _ => {
+                                                                               panic!("short_channel_id == 0 should imply any pending_forward entries are of type Receive");
+                                                                       }
+                                                               };
                                                                let claimable_htlc = ClaimableHTLC {
                                                                        prev_hop: HTLCPreviousHopData {
                                                                                short_channel_id: prev_short_channel_id,
@@ -2263,8 +2322,8 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                                                                                incoming_packet_shared_secret: incoming_shared_secret,
                                                                        },
                                                                        value: amt_to_forward,
-                                                                       payment_data: payment_data.clone(),
-                                                                       cltv_expiry: incoming_cltv_expiry,
+                                                                       cltv_expiry,
+                                                                       onion_payload,
                                                                };
 
                                                                macro_rules! fail_htlc {
@@ -2293,10 +2352,38 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                                                                let mut payment_secrets = self.pending_inbound_payments.lock().unwrap();
                                                                match payment_secrets.entry(payment_hash) {
                                                                        hash_map::Entry::Vacant(_) => {
-                                                                               log_trace!(self.logger, "Failing new HTLC with payment_hash {} as we didn't have a corresponding inbound payment.", log_bytes!(payment_hash.0));
-                                                                               fail_htlc!(claimable_htlc);
+                                                                               match claimable_htlc.onion_payload {
+                                                                                       OnionPayload::Invoice(_) => {
+                                                                                               log_trace!(self.logger, "Failing new HTLC with payment_hash {} as we didn't have a corresponding inbound payment.", log_bytes!(payment_hash.0));
+                                                                                               fail_htlc!(claimable_htlc);
+                                                                                       },
+                                                                                       OnionPayload::Spontaneous(preimage) => {
+                                                                                               match channel_state.claimable_htlcs.entry(payment_hash) {
+                                                                                                       hash_map::Entry::Vacant(e) => {
+                                                                                                               e.insert(vec![claimable_htlc]);
+                                                                                                               new_events.push(events::Event::PaymentReceived {
+                                                                                                                       payment_hash,
+                                                                                                                       amt: amt_to_forward,
+                                                                                                                       purpose: events::PaymentPurpose::SpontaneousPayment(preimage),
+                                                                                                               });
+                                                                                                       },
+                                                                                                       hash_map::Entry::Occupied(_) => {
+                                                                                                               log_trace!(self.logger, "Failing new keysend HTLC with payment_hash {} for a duplicative payment hash", log_bytes!(payment_hash.0));
+                                                                                                               fail_htlc!(claimable_htlc);
+                                                                                                       }
+                                                                                               }
+                                                                                       }
+                                                                               }
                                                                        },
                                                                        hash_map::Entry::Occupied(inbound_payment) => {
+                                                                               let payment_data =
+                                                                                       if let OnionPayload::Invoice(ref data) = claimable_htlc.onion_payload {
+                                                                                               data.clone()
+                                                                                       } else {
+                                                                                               log_trace!(self.logger, "Failing new keysend HTLC with payment_hash {} because we already have an inbound payment with the same payment hash", log_bytes!(payment_hash.0));
+                                                                                               fail_htlc!(claimable_htlc);
+                                                                                               continue
+                                                                                       };
                                                                                if inbound_payment.get().payment_secret != payment_data.payment_secret {
                                                                                        log_trace!(self.logger, "Failing new HTLC with payment_hash {} as it didn't match our expected payment secret.", log_bytes!(payment_hash.0));
                                                                                        fail_htlc!(claimable_htlc);
@@ -2308,15 +2395,27 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                                                                                        let mut total_value = 0;
                                                                                        let htlcs = channel_state.claimable_htlcs.entry(payment_hash)
                                                                                                .or_insert(Vec::new());
+                                                                                       if htlcs.len() == 1 {
+                                                                                               if let OnionPayload::Spontaneous(_) = htlcs[0].onion_payload {
+                                                                                                       log_trace!(self.logger, "Failing new HTLC with payment_hash {} as we already had an existing keysend HTLC with the same payment hash", log_bytes!(payment_hash.0));
+                                                                                                       fail_htlc!(claimable_htlc);
+                                                                                                       continue
+                                                                                               }
+                                                                                       }
                                                                                        htlcs.push(claimable_htlc);
                                                                                        for htlc in htlcs.iter() {
                                                                                                total_value += htlc.value;
-                                                                                               if htlc.payment_data.total_msat != payment_data.total_msat {
-                                                                                                       log_trace!(self.logger, "Failing HTLCs with payment_hash {} as the HTLCs had inconsistent total values (eg {} and {})",
-                                                                                                               log_bytes!(payment_hash.0), payment_data.total_msat, htlc.payment_data.total_msat);
-                                                                                                       total_value = msgs::MAX_VALUE_MSAT;
+                                                                                               match &htlc.onion_payload {
+                                                                                                       OnionPayload::Invoice(htlc_payment_data) => {
+                                                                                                               if htlc_payment_data.total_msat != payment_data.total_msat {
+                                                                                                                       log_trace!(self.logger, "Failing HTLCs with payment_hash {} as the HTLCs had inconsistent total values (eg {} and {})",
+                                                                                                                                                                log_bytes!(payment_hash.0), payment_data.total_msat, htlc_payment_data.total_msat);
+                                                                                                                       total_value = msgs::MAX_VALUE_MSAT;
+                                                                                                               }
+                                                                                                               if total_value >= msgs::MAX_VALUE_MSAT { break; }
+                                                                                                       },
+                                                                                                       _ => unreachable!(),
                                                                                                }
-                                                                                               if total_value >= msgs::MAX_VALUE_MSAT { break; }
                                                                                        }
                                                                                        if total_value >= msgs::MAX_VALUE_MSAT || total_value > payment_data.total_msat {
                                                                                                log_trace!(self.logger, "Failing HTLCs with payment_hash {} as the total value {} ran over expected value {} (or HTLCs were inconsistent)",
@@ -2327,10 +2426,12 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                                                                                        } else if total_value == payment_data.total_msat {
                                                                                                new_events.push(events::Event::PaymentReceived {
                                                                                                        payment_hash,
-                                                                                                       payment_preimage: inbound_payment.get().payment_preimage,
-                                                                                                       payment_secret: payment_data.payment_secret,
+                                                                                                       purpose: events::PaymentPurpose::InvoicePayment {
+                                                                                                               payment_preimage: inbound_payment.get().payment_preimage,
+                                                                                                               payment_secret: payment_data.payment_secret,
+                                                                                                               user_payment_id: inbound_payment.get().user_payment_id,
+                                                                                                       },
                                                                                                        amt: total_value,
-                                                                                                       user_payment_id: inbound_payment.get().user_payment_id,
                                                                                                });
                                                                                                // Only ever generate at most one PaymentReceived
                                                                                                // per registered payment_hash, even if it isn't
@@ -2345,9 +2446,6 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                                                                        },
                                                                };
                                                        },
-                                                       HTLCForwardInfo::AddHTLC { .. } => {
-                                                               panic!("short_channel_id == 0 should imply any pending_forward entries are of type Receive");
-                                                       },
                                                        HTLCForwardInfo::FailHTLC { .. } => {
                                                                panic!("Got pending fail of our own HTLC");
                                                        }
@@ -3778,7 +3876,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
        /// The [`PaymentHash`] (and corresponding [`PaymentPreimage`]) must be globally unique. This
        /// method may return an Err if another payment with the same payment_hash is still pending.
        ///
-       /// `user_payment_id` will be provided back in [`PaymentReceived::user_payment_id`] events to
+       /// `user_payment_id` will be provided back in [`PaymentPurpose::InvoicePayment::user_payment_id`] events to
        /// allow tracking of which events correspond with which calls to this and
        /// [`create_inbound_payment`]. `user_payment_id` has no meaning inside of LDK, it is simply
        /// copied to events and otherwise ignored. It may be used to correlate PaymentReceived events
@@ -3812,7 +3910,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
        ///
        /// [`create_inbound_payment`]: Self::create_inbound_payment
        /// [`PaymentReceived`]: events::Event::PaymentReceived
-       /// [`PaymentReceived::user_payment_id`]: events::Event::PaymentReceived::user_payment_id
+       /// [`PaymentPurpose::InvoicePayment::user_payment_id`]: events::PaymentPurpose::InvoicePayment::user_payment_id
        pub fn create_inbound_payment_for_hash(&self, payment_hash: PaymentHash, min_value_msat: Option<u64>, invoice_expiry_delta_secs: u32, user_payment_id: u64) -> Result<PaymentSecret, APIError> {
                self.set_payment_hash_secret_map(payment_hash, None, min_value_msat, invoice_expiry_delta_secs, user_payment_id)
        }
@@ -4507,12 +4605,63 @@ impl_writeable_tlv_based!(HTLCPreviousHopData, {
        (6, incoming_packet_shared_secret, required)
 });
 
-impl_writeable_tlv_based!(ClaimableHTLC, {
-       (0, prev_hop, required),
-       (2, value, required),
-       (4, payment_data, required),
-       (6, cltv_expiry, required),
-});
+impl Writeable for ClaimableHTLC {
+       fn write<W: Writer>(&self, writer: &mut W) -> Result<(), ::std::io::Error> {
+               let payment_data = match &self.onion_payload {
+                       OnionPayload::Invoice(data) => Some(data.clone()),
+                       _ => None,
+               };
+               let keysend_preimage = match self.onion_payload {
+                       OnionPayload::Invoice(_) => None,
+                       OnionPayload::Spontaneous(preimage) => Some(preimage.clone()),
+               };
+               write_tlv_fields!
+               (writer,
+                {
+                  (0, self.prev_hop, required), (2, self.value, required),
+                  (4, payment_data, option), (6, self.cltv_expiry, required),
+                        (8, keysend_preimage, option),
+                });
+               Ok(())
+       }
+}
+
+impl Readable for ClaimableHTLC {
+       fn read<R: Read>(reader: &mut R) -> Result<Self, DecodeError> {
+               let mut prev_hop = ::util::ser::OptionDeserWrapper(None);
+               let mut value = 0;
+               let mut payment_data: Option<msgs::FinalOnionHopData> = None;
+               let mut cltv_expiry = 0;
+               let mut keysend_preimage: Option<PaymentPreimage> = None;
+               read_tlv_fields!
+               (reader,
+                {
+                  (0, prev_hop, required), (2, value, required),
+                  (4, payment_data, option), (6, cltv_expiry, required),
+                        (8, keysend_preimage, option)
+                });
+               let onion_payload = match keysend_preimage {
+                       Some(p) => {
+                               if payment_data.is_some() {
+                                       return Err(DecodeError::InvalidValue)
+                               }
+                               OnionPayload::Spontaneous(p)
+                       },
+                       None => {
+                               if payment_data.is_none() {
+                                       return Err(DecodeError::InvalidValue)
+                               }
+                               OnionPayload::Invoice(payment_data.unwrap())
+                       },
+               };
+               Ok(Self {
+                       prev_hop: prev_hop.0.unwrap(),
+                       value,
+                       onion_payload,
+                       cltv_expiry,
+               })
+       }
+}
 
 impl_writeable_tlv_based_enum!(HTLCSource,
        (0, OutboundRoute) => {
@@ -5098,7 +5247,7 @@ pub mod bench {
        use routing::router::get_route;
        use util::test_utils;
        use util::config::UserConfig;
-       use util::events::{Event, MessageSendEvent, MessageSendEventsProvider};
+       use util::events::{Event, MessageSendEvent, MessageSendEventsProvider, PaymentPurpose};
 
        use bitcoin::hashes::Hash;
        use bitcoin::hashes::sha256::Hash as Sha256;