]> git.bitcoin.ninja Git - rust-lightning/commitdiff
Implement sending + receiving keysend payments
authorValentine Wallace <vwallace@protonmail.com>
Wed, 23 Jun 2021 19:15:16 +0000 (15:15 -0400)
committerValentine Wallace <vwallace@protonmail.com>
Fri, 25 Jun 2021 23:43:26 +0000 (19:43 -0400)
lightning/src/ln/channelmanager.rs
lightning/src/ln/functional_tests.rs
lightning/src/ln/onion_route_tests.rs
lightning/src/ln/onion_utils.rs

index a36b40fb17af8c2b6f40941900de3f3ff285e499..f5f75b50c75fef5f2cdbb501f0b0971a806de844 100644 (file)
@@ -1434,14 +1434,22 @@ 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, keysend_preimage) = match next_hop_data.format {
+                       let (mut payment_data, keysend_preimage) = match next_hop_data.format {
                                msgs::OnionHopDataFormat::Legacy { .. } => (None, None),
                                msgs::OnionHopDataFormat::NonFinalNode { .. } => return_err!("Got non final data with an HMAC of 0", 0x4000 | 22, &[0;0]),
                                msgs::OnionHopDataFormat::FinalNode { payment_data, keysend_preimage } => (payment_data, keysend_preimage),
                        };
 
                        if payment_data.is_none() {
-                               return_err!("We require payment_secrets", 0x4000|0x2000|3, &[0;0]);
+                               match keysend_preimage {
+                                       Some(_) => {
+                                               payment_data = Some(msgs::FinalOnionHopData {
+                                                       payment_secret: PaymentSecret([0; 32]),
+                                                       total_msat: msg.amount_msat
+                                               });
+                                       },
+                                       None => return_err!("We require payment_secrets", 0x4000|0x2000|3, &[0;0])
+                               }
                        }
 
                        // Note that we could obviously respond immediately with an update_fulfill_htlc
@@ -1623,7 +1631,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();
@@ -1631,7 +1639,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"});
                }
@@ -1739,6 +1747,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"}));
                }
@@ -1772,7 +1784,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;
@@ -1796,6 +1808,23 @@ 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 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.
+       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>>
@@ -3058,6 +3087,23 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                let (pending_forward_info, mut channel_state_lock) = self.decode_update_add_htlc_onion(msg);
                let channel_state = &mut *channel_state_lock;
 
+               // If we're receiving a keysend payment, then no payment will have been added to
+               // `self.pending_inbound_payments` in preparation to receive said payment. Thus, if a
+               // keysend_preimage is present, add the pending inbound payment now.
+               match pending_forward_info {
+                       PendingHTLCStatus::Forward(PendingHTLCInfo { ref routing, .. }) => {
+                               match routing {
+                                       PendingHTLCRouting::Receive { payment_data, keysend_preimage: Some(preimage), .. } => {
+                                               let hash = PaymentHash(Sha256::hash(&preimage.0[..]).into_inner());
+                                               let default_invoice_expiry_secs = 60 * 60; // 1 hour
+                                               self.set_payment_hash_secret_map(hash, Some(preimage.clone()), Some(payment_data.payment_secret), Some(payment_data.total_msat), default_invoice_expiry_secs, 0).unwrap();
+                                       },
+                                       _ => {}
+                               }
+                       },
+                       _ => {}
+               }
+
                match channel_state.by_id.entry(msg.channel_id) {
                        hash_map::Entry::Occupied(mut chan) => {
                                if chan.get().get_counterparty_node_id() != *counterparty_node_id {
@@ -3587,10 +3633,13 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                }
        }
 
-       fn set_payment_hash_secret_map(&self, payment_hash: PaymentHash, payment_preimage: Option<PaymentPreimage>, min_value_msat: Option<u64>, invoice_expiry_delta_secs: u32, user_payment_id: u64) -> Result<PaymentSecret, APIError> {
+       fn set_payment_hash_secret_map(&self, payment_hash: PaymentHash, payment_preimage: Option<PaymentPreimage>, payment_secret: Option<PaymentSecret>, min_value_msat: Option<u64>, invoice_expiry_delta_secs: u32, user_payment_id: u64) -> Result<PaymentSecret, APIError> {
                assert!(invoice_expiry_delta_secs <= 60*60*24*365); // Sadly bitcoin timestamps are u32s, so panic before 2106
 
-               let payment_secret = PaymentSecret(self.keys_manager.get_secure_random_bytes());
+               let payment_secret = match payment_secret {
+                       Some(secret) => secret,
+                       None => PaymentSecret(self.keys_manager.get_secure_random_bytes())
+               };
 
                let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
                let mut payment_secrets = self.pending_inbound_payments.lock().unwrap();
@@ -3634,7 +3683,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
                let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());
 
                (payment_hash,
-                       self.set_payment_hash_secret_map(payment_hash, Some(payment_preimage), min_value_msat, invoice_expiry_delta_secs, user_payment_id)
+                       self.set_payment_hash_secret_map(payment_hash, Some(payment_preimage), None, min_value_msat, invoice_expiry_delta_secs, user_payment_id)
                                .expect("RNG Generated Duplicate PaymentHash"))
        }
 
@@ -3684,7 +3733,7 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
        /// [`PaymentReceived`]: events::Event::PaymentReceived
        /// [`PaymentReceived::user_payment_id`]: events::Event::PaymentReceived::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)
+               self.set_payment_hash_secret_map(payment_hash, None, None, min_value_msat, invoice_expiry_delta_secs, user_payment_id)
        }
 
        #[cfg(any(test, feature = "fuzztarget", feature = "_test_utils"))]
index fe2a5d780f9063c101bf9655e0d4abbf468cdca8..673c299b5666ae1bf6e45ade887db58c1655dcd0 100644 (file)
@@ -1591,7 +1591,7 @@ fn test_fee_spike_violation_fails_htlc() {
        let cur_height = nodes[1].node.best_block.read().unwrap().height() + 1;
 
        let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.paths[0], &session_priv).unwrap();
-       let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(&route.paths[0], 3460001, &Some(payment_secret), cur_height).unwrap();
+       let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(&route.paths[0], 3460001, &Some(payment_secret), cur_height, &None).unwrap();
        let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash);
        let msg = msgs::UpdateAddHTLC {
                channel_id: chan.2,
@@ -1741,7 +1741,7 @@ fn test_chan_reserve_violation_inbound_htlc_outbound_channel() {
        let session_priv = SecretKey::from_slice(&[42; 32]).unwrap();
        let cur_height = nodes[1].node.best_block.read().unwrap().height() + 1;
        let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.paths[0], &session_priv).unwrap();
-       let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(&route.paths[0], 1000, &Some(payment_secret), cur_height).unwrap();
+       let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(&route.paths[0], 1000, &Some(payment_secret), cur_height, &None).unwrap();
        let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash);
        let msg = msgs::UpdateAddHTLC {
                channel_id: chan.2,
@@ -1854,7 +1854,7 @@ fn test_chan_reserve_violation_inbound_htlc_inbound_chan() {
        let session_priv = SecretKey::from_slice(&[42; 32]).unwrap();
        let cur_height = nodes[0].node.best_block.read().unwrap().height() + 1;
        let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route_2.paths[0], &session_priv).unwrap();
-       let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(&route_2.paths[0], recv_value_2, &None, cur_height).unwrap();
+       let (onion_payloads, htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(&route_2.paths[0], recv_value_2, &None, cur_height, &None).unwrap();
        let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &our_payment_hash_1);
        let msg = msgs::UpdateAddHTLC {
                channel_id: chan.2,
@@ -3388,7 +3388,7 @@ fn fail_backward_pending_htlc_upon_channel_failure() {
                let current_height = nodes[1].node.best_block.read().unwrap().height() + 1;
                let net_graph_msg_handler = &nodes[1].net_graph_msg_handler;
                let route = get_route(&nodes[1].node.get_our_node_id(), &net_graph_msg_handler.network_graph.read().unwrap(), &nodes[0].node.get_our_node_id(), Some(InvoiceFeatures::known()), None, &Vec::new(), 50_000, TEST_FINAL_CLTV, &logger).unwrap();
-               let (onion_payloads, _amount_msat, cltv_expiry) = onion_utils::build_onion_payloads(&route.paths[0], 50_000, &Some(payment_secret), current_height).unwrap();
+               let (onion_payloads, _amount_msat, cltv_expiry) = onion_utils::build_onion_payloads(&route.paths[0], 50_000, &Some(payment_secret), current_height, &None).unwrap();
                let onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.paths[0], &session_priv).unwrap();
                let onion_routing_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash);
 
@@ -4131,7 +4131,7 @@ fn do_test_htlc_timeout(send_partial_mpp: bool) {
                // Use the utility function send_payment_along_path to send the payment with MPP data which
                // indicates there are more HTLCs coming.
                let cur_height = CHAN_CONFIRM_DEPTH + 1; // route_payment calls send_payment, which adds 1 to the current height. So we do the same here to match.
-               nodes[0].node.send_payment_along_path(&route.paths[0], &our_payment_hash, &Some(payment_secret), 200000, cur_height).unwrap();
+               nodes[0].node.send_payment_along_path(&route.paths[0], &our_payment_hash, &Some(payment_secret), 200000, cur_height, &None).unwrap();
                check_added_monitors!(nodes[0], 1);
                let mut events = nodes[0].node.get_and_clear_pending_msg_events();
                assert_eq!(events.len(), 1);
@@ -6766,7 +6766,7 @@ fn test_update_add_htlc_bolt2_receiver_check_max_htlc_limit() {
 
        let cur_height = nodes[0].node.best_block.read().unwrap().height() + 1;
        let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::signing_only(), &route.paths[0], &session_priv).unwrap();
-       let (onion_payloads, _htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(&route.paths[0], 3999999, &Some(our_payment_secret), cur_height).unwrap();
+       let (onion_payloads, _htlc_msat, htlc_cltv) = onion_utils::build_onion_payloads(&route.paths[0], 3999999, &Some(our_payment_secret), cur_height, &None).unwrap();
        let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &our_payment_hash);
 
        let mut msg = msgs::UpdateAddHTLC {
@@ -9298,3 +9298,29 @@ fn test_invalid_funding_tx() {
        } else { panic!(); }
        assert_eq!(nodes[1].node.list_channels().len(), 0);
 }
+
+#[test]
+fn test_keysend_payments_to_public_node() {
+       let chanmon_cfgs = create_chanmon_cfgs(2);
+       let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+       let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
+       let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+
+       let _chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 10001, InitFeatures::known(), InitFeatures::known());
+       let network_graph = nodes[0].net_graph_msg_handler.network_graph.read().unwrap();
+       let payer_pubkey = nodes[0].node.get_our_node_id();
+       let payee_pubkey = nodes[1].node.get_our_node_id();
+       let route = get_route(&payer_pubkey, &network_graph, &payee_pubkey, None,
+                        None, &vec![], 10000, 40,
+                        nodes[0].logger).unwrap();
+
+       let test_preimage = PaymentPreimage([42; 32]);
+       let payment_hash = nodes[0].node.send_spontaneous_payment(&route, Some(test_preimage)).unwrap();
+       check_added_monitors!(nodes[0], 1);
+       let mut events = nodes[0].node.get_and_clear_pending_msg_events();
+       assert_eq!(events.len(), 1);
+       let event = events.pop().unwrap();
+       let path = vec![&nodes[1]];
+       pass_along_path(&nodes[0], &path, 10000, payment_hash, PaymentSecret([0; 32]), event, true, Some(test_preimage));
+       claim_payment(&nodes[0], &path, test_preimage);
+}
index 3fc01f1bb8b4ea7ce285091d9c2a4aea7170ec5d..69918c2e117d21ce30da80cc00af0959c261e223 100644 (file)
@@ -280,7 +280,7 @@ fn test_onion_failure() {
                let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
                let cur_height = nodes[0].best_block_info().1 + 1;
                let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
-               let (mut onion_payloads, _htlc_msat, _htlc_cltv) = onion_utils::build_onion_payloads(&route.paths[0], 40000, &None, cur_height).unwrap();
+               let (mut onion_payloads, _htlc_msat, _htlc_cltv) = onion_utils::build_onion_payloads(&route.paths[0], 40000, &None, cur_height, &None).unwrap();
                let mut new_payloads = Vec::new();
                for payload in onion_payloads.drain(..) {
                        new_payloads.push(BogusOnionHopData::new(payload));
@@ -296,7 +296,7 @@ fn test_onion_failure() {
                let session_priv = SecretKey::from_slice(&[3; 32]).unwrap();
                let cur_height = nodes[0].best_block_info().1 + 1;
                let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
-               let (mut onion_payloads, _htlc_msat, _htlc_cltv) = onion_utils::build_onion_payloads(&route.paths[0], 40000, &None, cur_height).unwrap();
+               let (mut onion_payloads, _htlc_msat, _htlc_cltv) = onion_utils::build_onion_payloads(&route.paths[0], 40000, &None, cur_height, &None).unwrap();
                let mut new_payloads = Vec::new();
                for payload in onion_payloads.drain(..) {
                        new_payloads.push(BogusOnionHopData::new(payload));
@@ -490,7 +490,7 @@ fn test_onion_failure() {
                let height = nodes[2].best_block_info().1;
                route.paths[0][1].cltv_expiry_delta += CLTV_FAR_FAR_AWAY + route.paths[0][0].cltv_expiry_delta + 1;
                let onion_keys = onion_utils::construct_onion_keys(&Secp256k1::new(), &route.paths[0], &session_priv).unwrap();
-               let (onion_payloads, _, htlc_cltv) = onion_utils::build_onion_payloads(&route.paths[0], 40000, &None, height).unwrap();
+               let (onion_payloads, _, htlc_cltv) = onion_utils::build_onion_payloads(&route.paths[0], 40000, &None, height, &None).unwrap();
                let onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &payment_hash);
                msg.cltv_expiry = htlc_cltv;
                msg.onion_routing_packet = onion_packet;
index bd739d86ee2c870baddfd1f125038a2d2b7d4983..cd8f95681eb346ca4d10b9c32ec7105a6b5cc027 100644 (file)
@@ -7,7 +7,7 @@
 // You may not use this file except in accordance with one or both of these
 // licenses.
 
-use ln::{PaymentHash, PaymentSecret};
+use ln::{PaymentHash, PaymentPreimage, PaymentSecret};
 use ln::channelmanager::HTLCSource;
 use ln::msgs;
 use routing::router::RouteHop;
@@ -119,7 +119,7 @@ pub(super) fn construct_onion_keys<T: secp256k1::Signing>(secp_ctx: &Secp256k1<T
 }
 
 /// returns the hop data, as well as the first-hop value_msat and CLTV value we should send.
-pub(super) fn build_onion_payloads(path: &Vec<RouteHop>, total_msat: u64, payment_secret_option: &Option<PaymentSecret>, starting_htlc_offset: u32) -> Result<(Vec<msgs::OnionHopData>, u64, u32), APIError> {
+pub(super) fn build_onion_payloads(path: &Vec<RouteHop>, total_msat: u64, payment_secret_option: &Option<PaymentSecret>, starting_htlc_offset: u32, keysend_preimage: &Option<PaymentPreimage>) -> Result<(Vec<msgs::OnionHopData>, u64, u32), APIError> {
        let mut cur_value_msat = 0u64;
        let mut cur_cltv = starting_htlc_offset;
        let mut last_short_channel_id = 0;
@@ -141,7 +141,7 @@ pub(super) fn build_onion_payloads(path: &Vec<RouteHop>, total_msat: u64, paymen
                                                                total_msat,
                                                        })
                                                } else { None },
-                                               keysend_preimage: None,
+                                               keysend_preimage: *keysend_preimage,
                                        }
                                } else {
                                        msgs::OnionHopDataFormat::NonFinalNode {