@@ -123,7 +123,7 @@ use crate::types::string::UntrustedString;
123123use crate::util::config::{ChannelConfig, ChannelConfigOverrides, ChannelConfigUpdate, UserConfig};
124124use crate::util::errors::APIError;
125125use crate::util::logger::{Level, Logger, WithContext};
126- use crate::util::scid_utils::fake_scid;
126+ use crate::util::scid_utils::{ fake_scid, SCID_SELF_PAYMENT} ;
127127use crate::util::ser::{
128128 BigSize, FixedLengthReader, LengthReadable, MaybeReadable, Readable, ReadableArgs, VecWriter,
129129 Writeable, Writer,
@@ -4906,6 +4906,11 @@ where
49064906 path, payment_hash, recipient_onion, total_value, cur_height, payment_id, keysend_preimage,
49074907 invoice_request, bolt12_invoice, session_priv_bytes
49084908 } = args;
4909+
4910+ if path.hops.first().unwrap().short_channel_id == SCID_SELF_PAYMENT {
4911+ return self.handle_self_payment(&payment_hash, &recipient_onion, total_value, payment_id, &keysend_preimage);
4912+ }
4913+
49094914 // The top-level caller should hold the total_consistency_lock read lock.
49104915 debug_assert!(self.total_consistency_lock.try_write().is_err());
49114916 let prng_seed = self.entropy_source.get_secure_random_bytes();
@@ -5078,6 +5083,88 @@ where
50785083 |args| self.send_payment_along_path(args),
50795084 )
50805085 }
5086+
5087+ /// Send a payment to ourselves (self-payment).
5088+ ///
5089+ /// This method creates a payment route from the node to itself, enabling testing
5090+ /// of payment functionality and providing a way to generate payment events locally.
5091+ ///
5092+ /// # Arguments
5093+ /// * `payment_preimage` - The preimage for the payment hash
5094+ /// * `recipient_onion` - The recipient onion fields for the payment
5095+ /// * `payment_id` - A unique identifier for this payment
5096+ ///
5097+ /// # Returns
5098+ /// Returns the payment hash on success, or an APIError on failure.
5099+ pub fn send_self_payment(
5100+ &self, payment_preimage: PaymentPreimage, recipient_onion: RecipientOnionFields,
5101+ payment_id: PaymentId, final_value_msat: u64
5102+ ) -> Result<PaymentHash, APIError> {
5103+ // Create route parameters for self-payment
5104+ let payment_params = PaymentParameters::from_node_id(
5105+ self.get_our_node_id(),
5106+ MIN_FINAL_CLTV_EXPIRY_DELTA as u32
5107+ );
5108+ let route_params = RouteParameters::from_payment_params_and_value(
5109+ payment_params,
5110+ final_value_msat// Amount in msat
5111+ );
5112+
5113+ // Use existing spontaneous payment infrastructure
5114+ self.send_spontaneous_payment(
5115+ Some(payment_preimage),
5116+ recipient_onion,
5117+ payment_id,
5118+ route_params,
5119+ Retry::Attempts(0)
5120+ ).map_err(|e| APIError::APIMisuseError {
5121+ err: format!("Self-payment failed: {:?}", e)
5122+ })
5123+ }
5124+
5125+ fn handle_self_payment(
5126+ &self, payment_hash: &PaymentHash, recipient_onion: &RecipientOnionFields,
5127+ total_value: u64, payment_id: PaymentId, keysend_preimage: &Option<PaymentPreimage>
5128+ ) -> Result<(), APIError> {
5129+ // For self-payments, we need to:
5130+ // 1. Validate the payment secret/preimage
5131+ // 2. Generate a PaymentClaimable event
5132+ // 3. The user can then claim_funds() to complete the payment
5133+
5134+ let payment_preimage = if let Some(preimage) = keysend_preimage {
5135+ *preimage
5136+ } else {
5137+ // For non-keysend self-payments, try to find the preimage
5138+ // This would require looking up the payment in inbound_payment_key
5139+ return Err(APIError::APIMisuseError {
5140+ err: "Self-payment without keysend preimage not yet supported".to_owned()
5141+ });
5142+ };
5143+
5144+ // Validate payment hash matches preimage
5145+ if PaymentHash(Sha256::hash(&payment_preimage.0).to_byte_array()) != *payment_hash {
5146+ return Err(APIError::APIMisuseError {
5147+ err: "Payment hash does not match preimage".to_owned()
5148+ });
5149+ }
5150+
5151+ // Create payment claimable event
5152+ let purpose = events::PaymentPurpose::SpontaneousPayment(payment_preimage);
5153+ let payment_claimable = events::Event::PaymentClaimable {
5154+ receiver_node_id: Some(self.get_our_node_id()),
5155+ payment_hash: *payment_hash,
5156+ onion_fields: Some(recipient_onion.clone()),
5157+ amount_msat: total_value,
5158+ counterparty_skimmed_fee_msat: 0,
5159+ purpose,
5160+ via_channel_ids: vec![],
5161+ claim_deadline: None,
5162+ payment_id: Some(payment_id),
5163+ };
5164+
5165+ self.pending_events.lock().unwrap().push_back((payment_claimable, None));
5166+ Ok(())
5167+ }
50815168
50825169 #[cfg(any(test, feature = "_externalize_tests"))]
50835170 pub(super) fn test_send_payment_internal(
0 commit comments