///
/// This is not exported to bindings users as we just use [u8; 32] directly
#[derive(Hash, Copy, Clone, PartialEq, Eq, Debug)]
-pub struct PaymentId(pub [u8; 32]);
+pub struct PaymentId(pub [u8; Self::LENGTH]);
+
+impl PaymentId {
+ /// Number of bytes in the id.
+ pub const LENGTH: usize = 32;
+}
impl Writeable for PaymentId {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
/// The number of ticks of [`ChannelManager::timer_tick_occurred`] until we time-out the
/// idempotency of payments by [`PaymentId`]. See
-/// [`OutboundPayments::remove_stale_resolved_payments`].
+/// [`OutboundPayments::remove_stale_payments`].
pub(crate) const IDEMPOTENCY_TIMEOUT_TICKS: u8 = 7;
/// The number of ticks of [`ChannelManager::timer_tick_occurred`] where a peer is disconnected
/// These include payments that have yet to find a successful path, or have unresolved HTLCs.
#[derive(Debug, PartialEq)]
pub enum RecentPaymentDetails {
+ /// When an invoice was requested but not yet received, and thus a payment has not been sent.
+ AwaitingInvoice {
+ /// Identifier for the payment to ensure idempotency.
+ payment_id: PaymentId,
+ },
/// When a payment is still being sent and awaiting successful delivery.
Pending {
/// Hash of the payment that is currently being sent but has yet to be fulfilled or
/// [`Event::PaymentSent`]: events::Event::PaymentSent
pub fn list_recent_payments(&self) -> Vec<RecentPaymentDetails> {
self.pending_outbound_payments.pending_outbound_payments.lock().unwrap().iter()
- .filter_map(|(_, pending_outbound_payment)| match pending_outbound_payment {
+ .filter_map(|(payment_id, pending_outbound_payment)| match pending_outbound_payment {
+ PendingOutboundPayment::AwaitingInvoice { .. } => {
+ Some(RecentPaymentDetails::AwaitingInvoice { payment_id: *payment_id })
+ },
PendingOutboundPayment::Retryable { payment_hash, total_msat, .. } => {
Some(RecentPaymentDetails::Pending {
payment_hash: *payment_hash,
}
- /// Signals that no further retries for the given payment should occur. Useful if you have a
+ /// Signals that no further attempts for the given payment should occur. Useful if you have a
/// pending outbound payment with retries remaining, but wish to stop retrying the payment before
/// retries are exhausted.
///
+ /// # Event Generation
+ ///
/// If no [`Event::PaymentFailed`] event had been generated before, one will be generated as soon
/// as there are no remaining pending HTLCs for this payment.
///
/// wait until you receive either a [`Event::PaymentFailed`] or [`Event::PaymentSent`] event to
/// determine the ultimate status of a payment.
///
- /// If an [`Event::PaymentFailed`] event is generated and we restart without this
- /// [`ChannelManager`] having been persisted, another [`Event::PaymentFailed`] may be generated.
+ /// # Requested Invoices
///
- /// [`Event::PaymentFailed`]: events::Event::PaymentFailed
- /// [`Event::PaymentSent`]: events::Event::PaymentSent
+ /// In the case of paying a [`Bolt12Invoice`], abandoning the payment prior to receiving the
+ /// invoice will result in an [`Event::InvoiceRequestFailed`] and prevent any attempts at paying
+ /// it once received. The other events may only be generated once the invoice has been received.
+ ///
+ /// # Restart Behavior
+ ///
+ /// If an [`Event::PaymentFailed`] is generated and we restart without first persisting the
+ /// [`ChannelManager`], another [`Event::PaymentFailed`] may be generated; likewise for
+ /// [`Event::InvoiceRequestFailed`].
+ ///
+ /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
pub fn abandon_payment(&self, payment_id: PaymentId) {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
self.pending_outbound_payments.abandon_payment(payment_id, PaymentFailureReason::UserAbandoned, &self.pending_events);
pub fn funding_transaction_generated(&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, funding_transaction: Transaction) -> Result<(), APIError> {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
- for inp in funding_transaction.input.iter() {
- if inp.witness.is_empty() {
- return Err(APIError::APIMisuseError {
- err: "Funding transaction must be fully signed and spend Segwit outputs".to_owned()
- });
+ if !funding_transaction.is_coin_base() {
+ for inp in funding_transaction.input.iter() {
+ if inp.witness.is_empty() {
+ return Err(APIError::APIMisuseError {
+ err: "Funding transaction must be fully signed and spend Segwit outputs".to_owned()
+ });
+ }
}
}
{
let _ = handle_error!(self, err, counterparty_node_id);
}
- self.pending_outbound_payments.remove_stale_resolved_payments(&self.pending_events);
+ self.pending_outbound_payments.remove_stale_payments(&self.pending_events);
// Technically we don't need to do this here, but if we have holding cell entries in a
// channel that need freeing, it's better to do that here and block a background task
session_priv.write(writer)?;
}
}
+ PendingOutboundPayment::AwaitingInvoice { .. } => {},
PendingOutboundPayment::Fulfilled { .. } => {},
PendingOutboundPayment::Abandoned { .. } => {},
}
let (payment_preimage, payment_hash, _) = route_payment(&nodes[0], &expected_route, 100_000);
// Next, attempt a keysend payment and make sure it fails.
- let route_params = RouteParameters {
- payment_params: PaymentParameters::for_keysend(expected_route.last().unwrap().node.get_our_node_id(), TEST_FINAL_CLTV, false),
- final_value_msat: 100_000,
- };
+ let route_params = RouteParameters::from_payment_params_and_value(
+ PaymentParameters::for_keysend(expected_route.last().unwrap().node.get_our_node_id(),
+ TEST_FINAL_CLTV, false), 100_000);
let route = find_route(
&nodes[0].node.get_our_node_id(), &route_params, &nodes[0].network_graph,
None, nodes[0].logger, &scorer, &(), &random_seed_bytes
pass_along_path(&nodes[0], &path, 100_000, payment_hash, None, event, true, Some(payment_preimage));
// Next, attempt a keysend payment and make sure it fails.
- let route_params = RouteParameters {
- payment_params: PaymentParameters::for_keysend(expected_route.last().unwrap().node.get_our_node_id(), TEST_FINAL_CLTV, false),
- final_value_msat: 100_000,
- };
+ let route_params = RouteParameters::from_payment_params_and_value(
+ PaymentParameters::for_keysend(expected_route.last().unwrap().node.get_our_node_id(), TEST_FINAL_CLTV, false),
+ 100_000
+ );
let route = find_route(
&nodes[0].node.get_our_node_id(), &route_params, &nodes[0].network_graph,
None, nodes[0].logger, &scorer, &(), &random_seed_bytes
let payee_pubkey = nodes[1].node.get_our_node_id();
let _chan = create_chan_between_nodes(&nodes[0], &nodes[1]);
- let route_params = RouteParameters {
- payment_params: PaymentParameters::for_keysend(payee_pubkey, 40, false),
- final_value_msat: 10_000,
- };
+ let route_params = RouteParameters::from_payment_params_and_value(
+ PaymentParameters::for_keysend(payee_pubkey, 40, false), 10_000);
let network_graph = nodes[0].network_graph.clone();
let first_hops = nodes[0].node.list_usable_channels();
let scorer = test_utils::TestScorer::new();
let payee_pubkey = nodes[1].node.get_our_node_id();
let _chan = create_chan_between_nodes(&nodes[0], &nodes[1]);
- let route_params = RouteParameters {
- payment_params: PaymentParameters::for_keysend(payee_pubkey, 40, false),
- final_value_msat: 10_000,
- };
+ let route_params = RouteParameters::from_payment_params_and_value(
+ PaymentParameters::for_keysend(payee_pubkey, 40, false), 10_000);
let network_graph = nodes[0].network_graph.clone();
let first_hops = nodes[0].node.list_usable_channels();
let scorer = test_utils::TestScorer::new();
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::{Block, BlockHeader, PackedLockTime, Transaction, TxMerkleNode, TxOut};
- use crate::sync::{Arc, Mutex};
+ use crate::sync::{Arc, Mutex, RwLock};
use criterion::Criterion;
let tx_broadcaster = test_utils::TestBroadcaster::new(network);
let fee_estimator = test_utils::TestFeeEstimator { sat_per_kw: Mutex::new(253) };
let logger_a = test_utils::TestLogger::with_id("node a".to_owned());
- let scorer = Mutex::new(test_utils::TestScorer::new());
+ let scorer = RwLock::new(test_utils::TestScorer::new());
let router = test_utils::TestRouter::new(Arc::new(NetworkGraph::new(network, &logger_a)), &scorer);
let mut config: UserConfig = Default::default();
let payment_secret = $node_b.create_inbound_payment_for_hash(payment_hash, None, 7200, None).unwrap();
$node_a.send_payment(payment_hash, RecipientOnionFields::secret_only(payment_secret),
- PaymentId(payment_hash.0), RouteParameters {
- payment_params, final_value_msat: 10_000,
- }, Retry::Attempts(0)).unwrap();
+ PaymentId(payment_hash.0),
+ RouteParameters::from_payment_params_and_value(payment_params, 10_000),
+ Retry::Attempts(0)).unwrap();
let payment_event = SendEvent::from_event($node_a.get_and_clear_pending_msg_events().pop().unwrap());
$node_b.handle_update_add_htlc(&$node_a.get_our_node_id(), &payment_event.msgs[0]);
$node_b.handle_commitment_signed(&$node_a.get_our_node_id(), &payment_event.commitment_msg);