@@ -123,7 +123,7 @@ use crate::types::string::UntrustedString;
123
123
use crate::util::config::{ChannelConfig, ChannelConfigOverrides, ChannelConfigUpdate, UserConfig};
124
124
use crate::util::errors::APIError;
125
125
use 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} ;
127
127
use crate::util::ser::{
128
128
BigSize, FixedLengthReader, LengthReadable, MaybeReadable, Readable, ReadableArgs, VecWriter,
129
129
Writeable, Writer,
@@ -4906,6 +4906,11 @@ where
4906
4906
path, payment_hash, recipient_onion, total_value, cur_height, payment_id, keysend_preimage,
4907
4907
invoice_request, bolt12_invoice, session_priv_bytes
4908
4908
} = 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
+
4909
4914
// The top-level caller should hold the total_consistency_lock read lock.
4910
4915
debug_assert!(self.total_consistency_lock.try_write().is_err());
4911
4916
let prng_seed = self.entropy_source.get_secure_random_bytes();
@@ -5078,6 +5083,88 @@ where
5078
5083
|args| self.send_payment_along_path(args),
5079
5084
)
5080
5085
}
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
+ }
5081
5168
5082
5169
#[cfg(any(test, feature = "_externalize_tests"))]
5083
5170
pub(super) fn test_send_payment_internal(
0 commit comments