use std::sync::{Arc, RwLock};
use bitcoin::secp256k1::PublicKey;
use lightning::ln::channelmanager::{PaymentId, RecipientOnionFields, Retry, RetryableSendFailure};
use lightning::routing::router::{PaymentParameters, RouteParameters, RouteParametersConfig};
use lightning::sign::EntropySource;
use lightning_types::payment::{PaymentHash, PaymentPreimage};
use crate::config::{Config, LDK_PAYMENT_RETRY_TIMEOUT};
use crate::error::Error;
use crate::logger::{log_error, log_info, LdkLogger, Logger};
use crate::payment::store::{PaymentDetails, PaymentDirection, PaymentKind, PaymentStatus};
use crate::types::{ChannelManager, CustomTlvRecord, KeysManager, PaymentStore};
const LDK_DEFAULT_FINAL_CLTV_EXPIRY_DELTA: u32 = 144;
pub struct SpontaneousPayment {
channel_manager: Arc<ChannelManager>,
keys_manager: Arc<KeysManager>,
payment_store: Arc<PaymentStore>,
config: Arc<Config>,
is_running: Arc<RwLock<bool>>,
logger: Arc<Logger>,
}
impl SpontaneousPayment {
pub(crate) fn new(
channel_manager: Arc<ChannelManager>, keys_manager: Arc<KeysManager>,
payment_store: Arc<PaymentStore>, config: Arc<Config>, is_running: Arc<RwLock<bool>>,
logger: Arc<Logger>,
) -> Self {
Self { channel_manager, keys_manager, payment_store, config, is_running, logger }
}
pub fn send(
&self, amount_msat: u64, node_id: PublicKey,
route_parameters: Option<RouteParametersConfig>,
) -> Result<PaymentId, Error> {
self.send_inner(amount_msat, node_id, route_parameters, None, None)
}
pub fn send_with_custom_tlvs(
&self, amount_msat: u64, node_id: PublicKey,
route_parameters: Option<RouteParametersConfig>, custom_tlvs: Vec<CustomTlvRecord>,
) -> Result<PaymentId, Error> {
self.send_inner(amount_msat, node_id, route_parameters, Some(custom_tlvs), None)
}
pub fn send_with_preimage(
&self, amount_msat: u64, node_id: PublicKey, preimage: PaymentPreimage,
route_parameters: Option<RouteParametersConfig>,
) -> Result<PaymentId, Error> {
self.send_inner(amount_msat, node_id, route_parameters, None, Some(preimage))
}
pub fn send_with_preimage_and_custom_tlvs(
&self, amount_msat: u64, node_id: PublicKey, custom_tlvs: Vec<CustomTlvRecord>,
preimage: PaymentPreimage, route_parameters: Option<RouteParametersConfig>,
) -> Result<PaymentId, Error> {
self.send_inner(amount_msat, node_id, route_parameters, Some(custom_tlvs), Some(preimage))
}
fn send_inner(
&self, amount_msat: u64, node_id: PublicKey,
route_parameters: Option<RouteParametersConfig>, custom_tlvs: Option<Vec<CustomTlvRecord>>,
preimage: Option<PaymentPreimage>,
) -> Result<PaymentId, Error> {
if !*self.is_running.read().unwrap() {
return Err(Error::NotRunning);
}
let payment_preimage = preimage
.unwrap_or_else(|| PaymentPreimage(self.keys_manager.get_secure_random_bytes()));
let payment_hash = PaymentHash::from(payment_preimage);
let payment_id = PaymentId(payment_hash.0);
if let Some(payment) = self.payment_store.get(&payment_id) {
if payment.status == PaymentStatus::Pending
|| payment.status == PaymentStatus::Succeeded
{
log_error!(self.logger, "Payment error: must not send duplicate payments.");
return Err(Error::DuplicatePayment);
}
}
let mut route_params = RouteParameters::from_payment_params_and_value(
PaymentParameters::from_node_id(node_id, LDK_DEFAULT_FINAL_CLTV_EXPIRY_DELTA),
amount_msat,
);
if let Some(RouteParametersConfig {
max_total_routing_fee_msat,
max_total_cltv_expiry_delta,
max_path_count,
max_channel_saturation_power_of_half,
}) = route_parameters.as_ref().or(self.config.route_parameters.as_ref())
{
route_params.max_total_routing_fee_msat = *max_total_routing_fee_msat;
route_params.payment_params.max_total_cltv_expiry_delta = *max_total_cltv_expiry_delta;
route_params.payment_params.max_path_count = *max_path_count;
route_params.payment_params.max_channel_saturation_power_of_half =
*max_channel_saturation_power_of_half;
}
let recipient_fields = match custom_tlvs {
Some(tlvs) => RecipientOnionFields::spontaneous_empty()
.with_custom_tlvs(tlvs.into_iter().map(|tlv| (tlv.type_num, tlv.value)).collect())
.map_err(|e| {
log_error!(self.logger, "Failed to send payment with custom TLVs: {:?}", e);
Error::InvalidCustomTlvs
})?,
None => RecipientOnionFields::spontaneous_empty(),
};
match self.channel_manager.send_spontaneous_payment(
Some(payment_preimage),
recipient_fields,
PaymentId(payment_hash.0),
route_params,
Retry::Timeout(LDK_PAYMENT_RETRY_TIMEOUT),
) {
Ok(_hash) => {
log_info!(self.logger, "Initiated sending {}msat to {}.", amount_msat, node_id);
let kind = PaymentKind::Spontaneous {
hash: payment_hash,
preimage: Some(payment_preimage),
};
let payment = PaymentDetails::new(
payment_id,
kind,
Some(amount_msat),
None,
PaymentDirection::Outbound,
PaymentStatus::Pending,
);
self.payment_store.insert(payment)?;
Ok(payment_id)
},
Err(e) => {
log_error!(self.logger, "Failed to send payment: {:?}", e);
match e {
RetryableSendFailure::DuplicatePayment => Err(Error::DuplicatePayment),
_ => {
let kind = PaymentKind::Spontaneous {
hash: payment_hash,
preimage: Some(payment_preimage),
};
let payment = PaymentDetails::new(
payment_id,
kind,
Some(amount_msat),
None,
PaymentDirection::Outbound,
PaymentStatus::Failed,
);
self.payment_store.insert(payment)?;
Err(Error::PaymentSendingFailed)
},
}
},
}
}
pub fn send_probes(&self, amount_msat: u64, node_id: PublicKey) -> Result<(), Error> {
if !*self.is_running.read().unwrap() {
return Err(Error::NotRunning);
}
let liquidity_limit_multiplier = Some(self.config.probing_liquidity_limit_multiplier);
self.channel_manager
.send_spontaneous_preflight_probes(
node_id,
amount_msat,
LDK_DEFAULT_FINAL_CLTV_EXPIRY_DELTA,
liquidity_limit_multiplier,
)
.map_err(|e| {
log_error!(self.logger, "Failed to send payment probes: {:?}", e);
Error::ProbeSendingFailed
})?;
Ok(())
}
}