mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-03-15 15:39:09 +01:00
Merge pull request #3010 from shaavan/issue2836
Introduce Retry InvoiceRequest Flow
This commit is contained in:
commit
1059f5ffc5
8 changed files with 223 additions and 35 deletions
|
@ -786,6 +786,7 @@ mod tests {
|
|||
fn get_chain_hashes(&self) -> Option<Vec<ChainHash>> {
|
||||
Some(vec![ChainHash::using_genesis_block(Network::Testnet)])
|
||||
}
|
||||
fn message_received(&self) {}
|
||||
}
|
||||
impl MessageSendEventsProvider for MsgHandler {
|
||||
fn get_and_clear_pending_msg_events(&self) -> Vec<MessageSendEvent> {
|
||||
|
|
|
@ -61,11 +61,11 @@ use crate::ln::onion_utils::{HTLCFailReason, INVALID_ONION_BLINDING};
|
|||
use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError};
|
||||
#[cfg(test)]
|
||||
use crate::ln::outbound_payment;
|
||||
use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutboundPayment, SendAlongPathArgs, StaleExpiration};
|
||||
use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutboundPayment, RetryableInvoiceRequest, SendAlongPathArgs, StaleExpiration};
|
||||
use crate::ln::wire::Encode;
|
||||
use crate::offers::invoice::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder, UnsignedBolt12Invoice};
|
||||
use crate::offers::invoice_error::InvoiceError;
|
||||
use crate::offers::invoice_request::{DerivedPayerId, InvoiceRequestBuilder};
|
||||
use crate::offers::invoice_request::{DerivedPayerId, InvoiceRequest, InvoiceRequestBuilder};
|
||||
use crate::offers::nonce::Nonce;
|
||||
use crate::offers::offer::{Offer, OfferBuilder};
|
||||
use crate::offers::parse::Bolt12SemanticError;
|
||||
|
@ -3105,7 +3105,7 @@ where
|
|||
|
||||
outbound_scid_aliases: Mutex::new(new_hash_set()),
|
||||
pending_inbound_payments: Mutex::new(new_hash_map()),
|
||||
pending_outbound_payments: OutboundPayments::new(),
|
||||
pending_outbound_payments: OutboundPayments::new(new_hash_map()),
|
||||
forward_htlcs: Mutex::new(new_hash_map()),
|
||||
decode_update_add_htlcs: Mutex::new(new_hash_map()),
|
||||
claimable_payments: Mutex::new(ClaimablePayments { claimable_payments: new_hash_map(), pending_claiming_payments: new_hash_map() }),
|
||||
|
@ -9005,7 +9005,7 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
|
|||
let expiration = StaleExpiration::AbsoluteTimeout(absolute_expiry);
|
||||
$self.pending_outbound_payments
|
||||
.add_new_awaiting_invoice(
|
||||
payment_id, expiration, retry_strategy, max_total_routing_fee_msat,
|
||||
payment_id, expiration, retry_strategy, max_total_routing_fee_msat, None,
|
||||
)
|
||||
.map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?;
|
||||
|
||||
|
@ -9131,17 +9131,30 @@ where
|
|||
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
|
||||
|
||||
let expiration = StaleExpiration::TimerTicks(1);
|
||||
let retryable_invoice_request = RetryableInvoiceRequest {
|
||||
invoice_request: invoice_request.clone(),
|
||||
nonce,
|
||||
};
|
||||
self.pending_outbound_payments
|
||||
.add_new_awaiting_invoice(
|
||||
payment_id, expiration, retry_strategy, max_total_routing_fee_msat
|
||||
payment_id, expiration, retry_strategy, max_total_routing_fee_msat,
|
||||
Some(retryable_invoice_request)
|
||||
)
|
||||
.map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?;
|
||||
|
||||
self.enqueue_invoice_request(invoice_request, reply_paths)
|
||||
}
|
||||
|
||||
fn enqueue_invoice_request(
|
||||
&self,
|
||||
invoice_request: InvoiceRequest,
|
||||
reply_paths: Vec<BlindedMessagePath>,
|
||||
) -> Result<(), Bolt12SemanticError> {
|
||||
let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap();
|
||||
if !offer.paths().is_empty() {
|
||||
if !invoice_request.paths().is_empty() {
|
||||
reply_paths
|
||||
.iter()
|
||||
.flat_map(|reply_path| offer.paths().iter().map(move |path| (path, reply_path)))
|
||||
.flat_map(|reply_path| invoice_request.paths().iter().map(move |path| (path, reply_path)))
|
||||
.take(OFFERS_MESSAGE_REQUEST_LIMIT)
|
||||
.for_each(|(path, reply_path)| {
|
||||
let instructions = MessageSendInstructions::WithSpecifiedReplyPath {
|
||||
|
@ -9151,7 +9164,7 @@ where
|
|||
let message = OffersMessage::InvoiceRequest(invoice_request.clone());
|
||||
pending_offers_messages.push((message, instructions));
|
||||
});
|
||||
} else if let Some(signing_pubkey) = offer.signing_pubkey() {
|
||||
} else if let Some(signing_pubkey) = invoice_request.signing_pubkey() {
|
||||
for reply_path in reply_paths {
|
||||
let instructions = MessageSendInstructions::WithSpecifiedReplyPath {
|
||||
destination: Destination::Node(signing_pubkey),
|
||||
|
@ -10811,6 +10824,39 @@ where
|
|||
"Dual-funded channels not supported".to_owned(),
|
||||
msg.channel_id.clone())), counterparty_node_id);
|
||||
}
|
||||
|
||||
fn message_received(&self) {
|
||||
for (payment_id, retryable_invoice_request) in self
|
||||
.pending_outbound_payments
|
||||
.release_invoice_requests_awaiting_invoice()
|
||||
{
|
||||
let RetryableInvoiceRequest { invoice_request, nonce } = retryable_invoice_request;
|
||||
let hmac = payment_id.hmac_for_offer_payment(nonce, &self.inbound_payment_key);
|
||||
let context = OffersContext::OutboundPayment {
|
||||
payment_id,
|
||||
nonce,
|
||||
hmac: Some(hmac)
|
||||
};
|
||||
match self.create_blinded_paths(context) {
|
||||
Ok(reply_paths) => match self.enqueue_invoice_request(invoice_request, reply_paths) {
|
||||
Ok(_) => {}
|
||||
Err(_) => {
|
||||
log_warn!(self.logger,
|
||||
"Retry failed for an invoice request with payment_id: {}",
|
||||
payment_id
|
||||
);
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
log_warn!(self.logger,
|
||||
"Retry failed for an invoice request with payment_id: {}. \
|
||||
Reason: router could not find a blinded path to include as the reply path",
|
||||
payment_id
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, L: Deref>
|
||||
|
@ -12227,10 +12273,7 @@ where
|
|||
}
|
||||
pending_outbound_payments = Some(outbounds);
|
||||
}
|
||||
let pending_outbounds = OutboundPayments {
|
||||
pending_outbound_payments: Mutex::new(pending_outbound_payments.unwrap()),
|
||||
retry_lock: Mutex::new(())
|
||||
};
|
||||
let pending_outbounds = OutboundPayments::new(pending_outbound_payments.unwrap());
|
||||
|
||||
// We have to replay (or skip, if they were completed after we wrote the `ChannelManager`)
|
||||
// each `ChannelMonitorUpdate` in `in_flight_monitor_updates`. After doing so, we have to
|
||||
|
|
|
@ -1605,6 +1605,14 @@ pub trait ChannelMessageHandler : MessageSendEventsProvider {
|
|||
/// If it's `None`, then no particular network chain hash compatibility will be enforced when
|
||||
/// connecting to peers.
|
||||
fn get_chain_hashes(&self) -> Option<Vec<ChainHash>>;
|
||||
|
||||
/// Indicates that a message was received from any peer for any handler.
|
||||
/// Called before the message is passed to the appropriate handler.
|
||||
/// Useful for indicating that a network connection is active.
|
||||
///
|
||||
/// Note: Since this function is called frequently, it should be as
|
||||
/// efficient as possible for its intended purpose.
|
||||
fn message_received(&self);
|
||||
}
|
||||
|
||||
/// A trait to describe an object which can receive routing messages.
|
||||
|
|
|
@ -1070,6 +1070,78 @@ fn send_invoice_for_refund_with_distinct_reply_path() {
|
|||
assert_eq!(reply_path.introduction_node(), &IntroductionNode::NodeId(nodes[6].node.get_our_node_id()));
|
||||
}
|
||||
|
||||
/// Verifies that the invoice request message can be retried if it fails to reach the
|
||||
/// payee on the first attempt.
|
||||
#[test]
|
||||
fn creates_and_pays_for_offer_with_retry() {
|
||||
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);
|
||||
|
||||
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 1_000_000_000);
|
||||
|
||||
let alice = &nodes[0];
|
||||
let alice_id = alice.node.get_our_node_id();
|
||||
let bob = &nodes[1];
|
||||
let bob_id = bob.node.get_our_node_id();
|
||||
|
||||
let offer = alice.node
|
||||
.create_offer_builder(None).unwrap()
|
||||
.amount_msats(10_000_000)
|
||||
.build().unwrap();
|
||||
assert_ne!(offer.signing_pubkey(), Some(alice_id));
|
||||
assert!(!offer.paths().is_empty());
|
||||
for path in offer.paths() {
|
||||
assert_eq!(path.introduction_node(), &IntroductionNode::NodeId(alice_id));
|
||||
}
|
||||
let payment_id = PaymentId([1; 32]);
|
||||
bob.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), None).unwrap();
|
||||
expect_recent_payment!(bob, RecentPaymentDetails::AwaitingInvoice, payment_id);
|
||||
|
||||
let _lost_onion_message = bob.onion_messenger.next_onion_message_for_peer(alice_id).unwrap();
|
||||
|
||||
// Simulate a scenario where the original onion_message is lost before reaching Alice.
|
||||
// Use handle_message_received to regenerate the message.
|
||||
bob.node.message_received();
|
||||
let onion_message = bob.onion_messenger.next_onion_message_for_peer(alice_id).unwrap();
|
||||
|
||||
alice.onion_messenger.handle_onion_message(bob_id, &onion_message);
|
||||
|
||||
let (invoice_request, reply_path) = extract_invoice_request(alice, &onion_message);
|
||||
let payment_context = PaymentContext::Bolt12Offer(Bolt12OfferContext {
|
||||
offer_id: offer.id(),
|
||||
invoice_request: InvoiceRequestFields {
|
||||
payer_id: invoice_request.payer_id(),
|
||||
quantity: None,
|
||||
payer_note_truncated: None,
|
||||
},
|
||||
});
|
||||
assert_eq!(invoice_request.amount_msats(), None);
|
||||
assert_ne!(invoice_request.payer_id(), bob_id);
|
||||
assert_eq!(reply_path.introduction_node(), &IntroductionNode::NodeId(bob_id));
|
||||
let onion_message = alice.onion_messenger.next_onion_message_for_peer(bob_id).unwrap();
|
||||
bob.onion_messenger.handle_onion_message(alice_id, &onion_message);
|
||||
|
||||
// Expect no more OffersMessage to be enqueued by this point, even after calling
|
||||
// handle_message_received.
|
||||
bob.node.message_received();
|
||||
|
||||
assert!(bob.onion_messenger.next_onion_message_for_peer(alice_id).is_none());
|
||||
|
||||
let (invoice, _) = extract_invoice(bob, &onion_message);
|
||||
assert_eq!(invoice.amount_msats(), 10_000_000);
|
||||
assert_ne!(invoice.signing_pubkey(), alice_id);
|
||||
assert!(!invoice.payment_paths().is_empty());
|
||||
for path in invoice.payment_paths() {
|
||||
assert_eq!(path.introduction_node(), &IntroductionNode::NodeId(alice_id));
|
||||
}
|
||||
route_bolt12_payment(bob, &[alice], &invoice);
|
||||
expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id);
|
||||
claim_bolt12_payment(bob, &[alice], payment_context);
|
||||
expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id);
|
||||
}
|
||||
|
||||
/// Checks that a deferred invoice can be paid asynchronously from an Event::InvoiceReceived.
|
||||
#[test]
|
||||
fn pays_bolt12_invoice_asynchronously() {
|
||||
|
|
|
@ -22,6 +22,8 @@ use crate::ln::features::Bolt12InvoiceFeatures;
|
|||
use crate::ln::onion_utils;
|
||||
use crate::ln::onion_utils::{DecodedOnionFailure, HTLCFailReason};
|
||||
use crate::offers::invoice::Bolt12Invoice;
|
||||
use crate::offers::invoice_request::InvoiceRequest;
|
||||
use crate::offers::nonce::Nonce;
|
||||
use crate::routing::router::{BlindedTail, InFlightHtlcs, Path, PaymentParameters, Route, RouteParameters, Router};
|
||||
use crate::sign::{EntropySource, NodeSigner, Recipient};
|
||||
use crate::util::errors::APIError;
|
||||
|
@ -32,6 +34,7 @@ use crate::util::ser::ReadableArgs;
|
|||
|
||||
use core::fmt::{self, Display, Formatter};
|
||||
use core::ops::Deref;
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
use core::time::Duration;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
@ -53,6 +56,7 @@ pub(crate) enum PendingOutboundPayment {
|
|||
expiration: StaleExpiration,
|
||||
retry_strategy: Retry,
|
||||
max_total_routing_fee_msat: Option<u64>,
|
||||
retryable_invoice_request: Option<RetryableInvoiceRequest>
|
||||
},
|
||||
InvoiceReceived {
|
||||
payment_hash: PaymentHash,
|
||||
|
@ -100,6 +104,16 @@ pub(crate) enum PendingOutboundPayment {
|
|||
},
|
||||
}
|
||||
|
||||
pub(crate) struct RetryableInvoiceRequest {
|
||||
pub(crate) invoice_request: InvoiceRequest,
|
||||
pub(crate) nonce: Nonce,
|
||||
}
|
||||
|
||||
impl_writeable_tlv_based!(RetryableInvoiceRequest, {
|
||||
(0, invoice_request, required),
|
||||
(2, nonce, required),
|
||||
});
|
||||
|
||||
impl PendingOutboundPayment {
|
||||
fn increment_attempts(&mut self) {
|
||||
if let PendingOutboundPayment::Retryable { attempts, .. } = self {
|
||||
|
@ -666,13 +680,19 @@ pub(super) struct SendAlongPathArgs<'a> {
|
|||
|
||||
pub(super) struct OutboundPayments {
|
||||
pub(super) pending_outbound_payments: Mutex<HashMap<PaymentId, PendingOutboundPayment>>,
|
||||
pub(super) retry_lock: Mutex<()>,
|
||||
awaiting_invoice: AtomicBool,
|
||||
retry_lock: Mutex<()>,
|
||||
}
|
||||
|
||||
impl OutboundPayments {
|
||||
pub(super) fn new() -> Self {
|
||||
pub(super) fn new(pending_outbound_payments: HashMap<PaymentId, PendingOutboundPayment>) -> Self {
|
||||
let has_invoice_requests = pending_outbound_payments.values().any(|payment| {
|
||||
matches!(payment, PendingOutboundPayment::AwaitingInvoice { retryable_invoice_request: Some(_), .. })
|
||||
});
|
||||
|
||||
Self {
|
||||
pending_outbound_payments: Mutex::new(new_hash_map()),
|
||||
pending_outbound_payments: Mutex::new(pending_outbound_payments),
|
||||
awaiting_invoice: AtomicBool::new(has_invoice_requests),
|
||||
retry_lock: Mutex::new(()),
|
||||
}
|
||||
}
|
||||
|
@ -1393,16 +1413,20 @@ impl OutboundPayments {
|
|||
|
||||
pub(super) fn add_new_awaiting_invoice(
|
||||
&self, payment_id: PaymentId, expiration: StaleExpiration, retry_strategy: Retry,
|
||||
max_total_routing_fee_msat: Option<u64>
|
||||
max_total_routing_fee_msat: Option<u64>, retryable_invoice_request: Option<RetryableInvoiceRequest>
|
||||
) -> Result<(), ()> {
|
||||
let mut pending_outbounds = self.pending_outbound_payments.lock().unwrap();
|
||||
match pending_outbounds.entry(payment_id) {
|
||||
hash_map::Entry::Occupied(_) => Err(()),
|
||||
hash_map::Entry::Vacant(entry) => {
|
||||
if retryable_invoice_request.is_some() {
|
||||
self.awaiting_invoice.store(true, Ordering::Release);
|
||||
}
|
||||
entry.insert(PendingOutboundPayment::AwaitingInvoice {
|
||||
expiration,
|
||||
retry_strategy,
|
||||
max_total_routing_fee_msat,
|
||||
retryable_invoice_request,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
|
@ -1874,6 +1898,31 @@ impl OutboundPayments {
|
|||
pub fn clear_pending_payments(&self) {
|
||||
self.pending_outbound_payments.lock().unwrap().clear()
|
||||
}
|
||||
|
||||
pub fn release_invoice_requests_awaiting_invoice(&self) -> Vec<(PaymentId, RetryableInvoiceRequest)> {
|
||||
if !self.awaiting_invoice.load(Ordering::Acquire) {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
let mut pending_outbound_payments = self.pending_outbound_payments.lock().unwrap();
|
||||
let invoice_requests = pending_outbound_payments
|
||||
.iter_mut()
|
||||
.filter_map(|(payment_id, payment)| {
|
||||
if let PendingOutboundPayment::AwaitingInvoice {
|
||||
retryable_invoice_request, ..
|
||||
} = payment {
|
||||
retryable_invoice_request.take().map(|retryable_invoice_request| {
|
||||
(*payment_id, retryable_invoice_request)
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
self.awaiting_invoice.store(false, Ordering::Release);
|
||||
invoice_requests
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether a payment with the given [`PaymentHash`] and [`PaymentId`] is, in fact, a
|
||||
|
@ -1929,6 +1978,7 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
|
|||
(0, expiration, required),
|
||||
(2, retry_strategy, required),
|
||||
(4, max_total_routing_fee_msat, option),
|
||||
(5, retryable_invoice_request, option),
|
||||
},
|
||||
(7, InvoiceReceived) => {
|
||||
(0, payment_hash, required),
|
||||
|
@ -1959,6 +2009,7 @@ mod tests {
|
|||
use crate::routing::router::{InFlightHtlcs, Path, PaymentParameters, Route, RouteHop, RouteParameters};
|
||||
use crate::sync::{Arc, Mutex, RwLock};
|
||||
use crate::util::errors::APIError;
|
||||
use crate::util::hash_tables::new_hash_map;
|
||||
use crate::util::test_utils;
|
||||
|
||||
use alloc::collections::VecDeque;
|
||||
|
@ -1993,7 +2044,7 @@ mod tests {
|
|||
}
|
||||
#[cfg(feature = "std")]
|
||||
fn do_fails_paying_after_expiration(on_retry: bool) {
|
||||
let outbound_payments = OutboundPayments::new();
|
||||
let outbound_payments = OutboundPayments::new(new_hash_map());
|
||||
let logger = test_utils::TestLogger::new();
|
||||
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger));
|
||||
let scorer = RwLock::new(test_utils::TestScorer::new());
|
||||
|
@ -2037,7 +2088,7 @@ mod tests {
|
|||
do_find_route_error(true);
|
||||
}
|
||||
fn do_find_route_error(on_retry: bool) {
|
||||
let outbound_payments = OutboundPayments::new();
|
||||
let outbound_payments = OutboundPayments::new(new_hash_map());
|
||||
let logger = test_utils::TestLogger::new();
|
||||
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger));
|
||||
let scorer = RwLock::new(test_utils::TestScorer::new());
|
||||
|
@ -2076,7 +2127,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn initial_send_payment_path_failed_evs() {
|
||||
let outbound_payments = OutboundPayments::new();
|
||||
let outbound_payments = OutboundPayments::new(new_hash_map());
|
||||
let logger = test_utils::TestLogger::new();
|
||||
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger));
|
||||
let scorer = RwLock::new(test_utils::TestScorer::new());
|
||||
|
@ -2158,7 +2209,7 @@ mod tests {
|
|||
#[test]
|
||||
fn removes_stale_awaiting_invoice_using_absolute_timeout() {
|
||||
let pending_events = Mutex::new(VecDeque::new());
|
||||
let outbound_payments = OutboundPayments::new();
|
||||
let outbound_payments = OutboundPayments::new(new_hash_map());
|
||||
let payment_id = PaymentId([0; 32]);
|
||||
let absolute_expiry = 100;
|
||||
let tick_interval = 10;
|
||||
|
@ -2167,7 +2218,7 @@ mod tests {
|
|||
assert!(!outbound_payments.has_pending_payments());
|
||||
assert!(
|
||||
outbound_payments.add_new_awaiting_invoice(
|
||||
payment_id, expiration, Retry::Attempts(0), None
|
||||
payment_id, expiration, Retry::Attempts(0), None, None,
|
||||
).is_ok()
|
||||
);
|
||||
assert!(outbound_payments.has_pending_payments());
|
||||
|
@ -2197,14 +2248,14 @@ mod tests {
|
|||
|
||||
assert!(
|
||||
outbound_payments.add_new_awaiting_invoice(
|
||||
payment_id, expiration, Retry::Attempts(0), None
|
||||
payment_id, expiration, Retry::Attempts(0), None, None,
|
||||
).is_ok()
|
||||
);
|
||||
assert!(outbound_payments.has_pending_payments());
|
||||
|
||||
assert!(
|
||||
outbound_payments.add_new_awaiting_invoice(
|
||||
payment_id, expiration, Retry::Attempts(0), None
|
||||
payment_id, expiration, Retry::Attempts(0), None, None,
|
||||
).is_err()
|
||||
);
|
||||
}
|
||||
|
@ -2212,7 +2263,7 @@ mod tests {
|
|||
#[test]
|
||||
fn removes_stale_awaiting_invoice_using_timer_ticks() {
|
||||
let pending_events = Mutex::new(VecDeque::new());
|
||||
let outbound_payments = OutboundPayments::new();
|
||||
let outbound_payments = OutboundPayments::new(new_hash_map());
|
||||
let payment_id = PaymentId([0; 32]);
|
||||
let timer_ticks = 3;
|
||||
let expiration = StaleExpiration::TimerTicks(timer_ticks);
|
||||
|
@ -2220,7 +2271,7 @@ mod tests {
|
|||
assert!(!outbound_payments.has_pending_payments());
|
||||
assert!(
|
||||
outbound_payments.add_new_awaiting_invoice(
|
||||
payment_id, expiration, Retry::Attempts(0), None
|
||||
payment_id, expiration, Retry::Attempts(0), None, None,
|
||||
).is_ok()
|
||||
);
|
||||
assert!(outbound_payments.has_pending_payments());
|
||||
|
@ -2250,14 +2301,14 @@ mod tests {
|
|||
|
||||
assert!(
|
||||
outbound_payments.add_new_awaiting_invoice(
|
||||
payment_id, expiration, Retry::Attempts(0), None
|
||||
payment_id, expiration, Retry::Attempts(0), None, None,
|
||||
).is_ok()
|
||||
);
|
||||
assert!(outbound_payments.has_pending_payments());
|
||||
|
||||
assert!(
|
||||
outbound_payments.add_new_awaiting_invoice(
|
||||
payment_id, expiration, Retry::Attempts(0), None
|
||||
payment_id, expiration, Retry::Attempts(0), None, None,
|
||||
).is_err()
|
||||
);
|
||||
}
|
||||
|
@ -2265,14 +2316,14 @@ mod tests {
|
|||
#[test]
|
||||
fn removes_abandoned_awaiting_invoice() {
|
||||
let pending_events = Mutex::new(VecDeque::new());
|
||||
let outbound_payments = OutboundPayments::new();
|
||||
let outbound_payments = OutboundPayments::new(new_hash_map());
|
||||
let payment_id = PaymentId([0; 32]);
|
||||
let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
|
||||
|
||||
assert!(!outbound_payments.has_pending_payments());
|
||||
assert!(
|
||||
outbound_payments.add_new_awaiting_invoice(
|
||||
payment_id, expiration, Retry::Attempts(0), None
|
||||
payment_id, expiration, Retry::Attempts(0), None, None,
|
||||
).is_ok()
|
||||
);
|
||||
assert!(outbound_payments.has_pending_payments());
|
||||
|
@ -2302,13 +2353,13 @@ mod tests {
|
|||
let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
|
||||
|
||||
let pending_events = Mutex::new(VecDeque::new());
|
||||
let outbound_payments = OutboundPayments::new();
|
||||
let outbound_payments = OutboundPayments::new(new_hash_map());
|
||||
let payment_id = PaymentId([0; 32]);
|
||||
let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
|
||||
|
||||
assert!(
|
||||
outbound_payments.add_new_awaiting_invoice(
|
||||
payment_id, expiration, Retry::Attempts(0), None
|
||||
payment_id, expiration, Retry::Attempts(0), None, None,
|
||||
).is_ok()
|
||||
);
|
||||
assert!(outbound_payments.has_pending_payments());
|
||||
|
@ -2355,7 +2406,7 @@ mod tests {
|
|||
let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
|
||||
|
||||
let pending_events = Mutex::new(VecDeque::new());
|
||||
let outbound_payments = OutboundPayments::new();
|
||||
let outbound_payments = OutboundPayments::new(new_hash_map());
|
||||
let payment_id = PaymentId([0; 32]);
|
||||
let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
|
||||
|
||||
|
@ -2372,7 +2423,7 @@ mod tests {
|
|||
assert!(
|
||||
outbound_payments.add_new_awaiting_invoice(
|
||||
payment_id, expiration, Retry::Attempts(0),
|
||||
Some(invoice.amount_msats() / 100 + 50_000)
|
||||
Some(invoice.amount_msats() / 100 + 50_000), None,
|
||||
).is_ok()
|
||||
);
|
||||
assert!(outbound_payments.has_pending_payments());
|
||||
|
@ -2416,7 +2467,7 @@ mod tests {
|
|||
let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet);
|
||||
|
||||
let pending_events = Mutex::new(VecDeque::new());
|
||||
let outbound_payments = OutboundPayments::new();
|
||||
let outbound_payments = OutboundPayments::new(new_hash_map());
|
||||
let payment_id = PaymentId([0; 32]);
|
||||
let expiration = StaleExpiration::AbsoluteTimeout(Duration::from_secs(100));
|
||||
|
||||
|
@ -2472,7 +2523,7 @@ mod tests {
|
|||
|
||||
assert!(
|
||||
outbound_payments.add_new_awaiting_invoice(
|
||||
payment_id, expiration, Retry::Attempts(0), Some(1234)
|
||||
payment_id, expiration, Retry::Attempts(0), Some(1234), None,
|
||||
).is_ok()
|
||||
);
|
||||
assert!(outbound_payments.has_pending_payments());
|
||||
|
|
|
@ -388,6 +388,8 @@ impl ChannelMessageHandler for ErroringMessageHandler {
|
|||
fn handle_tx_abort(&self, their_node_id: PublicKey, msg: &msgs::TxAbort) {
|
||||
ErroringMessageHandler::push_error(self, their_node_id, msg.channel_id);
|
||||
}
|
||||
|
||||
fn message_received(&self) {}
|
||||
}
|
||||
|
||||
impl Deref for ErroringMessageHandler {
|
||||
|
@ -1616,6 +1618,8 @@ impl<Descriptor: SocketDescriptor, CM: Deref, RM: Deref, OM: Deref, L: Deref, CM
|
|||
let their_node_id = peer_lock.their_node_id.expect("We know the peer's public key by the time we receive messages").0;
|
||||
let logger = WithContext::from(&self.logger, Some(their_node_id), None, None);
|
||||
|
||||
self.message_handler.chan_handler.message_received();
|
||||
|
||||
let message = match self.do_handle_message_holding_peer_lock(peer_lock, message, their_node_id, &logger)? {
|
||||
Some(processed_message) => processed_message,
|
||||
None => return Ok(None),
|
||||
|
|
|
@ -1039,6 +1039,13 @@ impl Writeable for InvoiceRequestContents {
|
|||
}
|
||||
}
|
||||
|
||||
impl Readable for InvoiceRequest {
|
||||
fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
|
||||
let bytes: WithoutLength<Vec<u8>> = Readable::read(reader)?;
|
||||
Self::try_from(bytes.0).map_err(|_| DecodeError::InvalidValue)
|
||||
}
|
||||
}
|
||||
|
||||
/// Valid type range for invoice_request TLV records.
|
||||
pub(super) const INVOICE_REQUEST_TYPES: core::ops::Range<u64> = 80..160;
|
||||
|
||||
|
|
|
@ -942,6 +942,8 @@ impl msgs::ChannelMessageHandler for TestChannelMessageHandler {
|
|||
fn handle_tx_abort(&self, _their_node_id: PublicKey, msg: &msgs::TxAbort) {
|
||||
self.received_msg(wire::Message::TxAbort(msg.clone()));
|
||||
}
|
||||
|
||||
fn message_received(&self) {}
|
||||
}
|
||||
|
||||
impl events::MessageSendEventsProvider for TestChannelMessageHandler {
|
||||
|
|
Loading…
Add table
Reference in a new issue