mirror of
https://github.com/lndk-org/lndk.git
synced 2026-08-13 12:33:05 +02:00
offers: better peer selection for offers
The current approach uses random peers. These random peers may be temporary connections, so that if someone tries to pay and offer, the selected peers may not be connected anymore, and the payment will fail. Instead of choosing random peers, this commit will select public open channels, creating long-lived offers. It tries to choose three different channels at most. In order for these channels to be valid, the node has to have onion support, and the node has to be connect to more public channels. This new logic is only for creating offers. The current logic of choosing random peers is better for outgoing payments and remains as such.
This commit is contained in:
parent
109c2ac154
commit
b4cbe4e770
7 changed files with 387 additions and 24 deletions
|
|
@ -28,8 +28,8 @@ use tonic::{Code, Status};
|
|||
use tonic_lnd::lnrpc::AddInvoiceResponse;
|
||||
use tonic_lnd::lnrpc::PayReq;
|
||||
use tonic_lnd::lnrpc::{
|
||||
FeeLimit, GetInfoResponse, HtlcAttempt, ListPeersResponse, NodeInfo, Payment,
|
||||
QueryRoutesResponse, Route,
|
||||
FeeLimit, GetInfoResponse, HtlcAttempt, ListChannelsResponse, ListPeersResponse, NodeInfo,
|
||||
Payment, QueryRoutesResponse, Route,
|
||||
};
|
||||
use tonic_lnd::signrpc::KeyLocator;
|
||||
use tonic_lnd::tonic::Status as LndStatus;
|
||||
|
|
@ -562,6 +562,7 @@ pub trait PeerConnector {
|
|||
pub_key: String,
|
||||
include_channels: bool,
|
||||
) -> Result<NodeInfo, LndStatus>;
|
||||
async fn list_active_public_channels(&mut self) -> Result<ListChannelsResponse, LndStatus>;
|
||||
}
|
||||
|
||||
/// InvoicePayer provides a layer of abstraction over the LND API for paying for a BOLT 12 invoice.
|
||||
|
|
|
|||
|
|
@ -181,6 +181,7 @@ mod tests {
|
|||
async fn list_peers(&mut self) -> Result<tonic_lnd::lnrpc::ListPeersResponse, tonic_lnd::tonic::Status>;
|
||||
async fn connect_peer(&mut self, node_id: String, addr: String) -> Result<(), tonic_lnd::tonic::Status>;
|
||||
async fn get_node_info(&mut self, pub_key: String, include_channels: bool) -> Result<tonic_lnd::lnrpc::NodeInfo, tonic_lnd::tonic::Status>;
|
||||
async fn list_active_public_channels(&mut self) -> Result<tonic_lnd::lnrpc::ListChannelsResponse, tonic_lnd::tonic::Status>;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@ use tonic_lnd::routerrpc::TrackPaymentRequest;
|
|||
use tonic_lnd::tonic::Status;
|
||||
use tonic_lnd::LightningClient;
|
||||
use tonic_lnd::{
|
||||
lnrpc::{ListPeersRequest, ListPeersResponse, NodeInfo},
|
||||
lnrpc::{
|
||||
ListChannelsRequest, ListChannelsResponse, ListPeersRequest, ListPeersResponse, NodeInfo,
|
||||
},
|
||||
signrpc::{KeyLocator, SignMessageReq},
|
||||
Client,
|
||||
};
|
||||
|
|
@ -58,6 +60,19 @@ impl PeerConnector for LightningClient {
|
|||
|
||||
self.get_node_info(req).await.map(|resp| resp.into_inner())
|
||||
}
|
||||
|
||||
async fn list_active_public_channels(&mut self) -> Result<ListChannelsResponse, Status> {
|
||||
let list_req = ListChannelsRequest {
|
||||
active_only: true,
|
||||
inactive_only: false,
|
||||
public_only: true,
|
||||
private_only: false,
|
||||
..Default::default()
|
||||
};
|
||||
self.list_channels(list_req)
|
||||
.await
|
||||
.map(|resp| resp.into_inner())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
|
@ -264,6 +279,7 @@ pub(super) mod tests {
|
|||
async fn list_peers(&mut self) -> Result<tonic_lnd::lnrpc::ListPeersResponse, Status>;
|
||||
async fn get_node_info(&mut self, pub_key: String, include_channels: bool) -> Result<NodeInfo, Status>;
|
||||
async fn connect_peer(&mut self, node_id: String, addr: String) -> Result<(), Status>;
|
||||
async fn list_active_public_channels(&mut self) -> Result<ListChannelsResponse, Status>;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -291,6 +307,7 @@ pub(super) mod tests {
|
|||
async fn list_peers(&mut self) -> Result<tonic_lnd::lnrpc::ListPeersResponse, Status>;
|
||||
async fn get_node_info(&mut self, pub_key: String, include_channels: bool) -> Result<NodeInfo, Status>;
|
||||
async fn connect_peer(&mut self, node_id: String, addr: String) -> Result<(), Status>;
|
||||
async fn list_active_public_channels(&mut self) -> Result<ListChannelsResponse, Status>;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ use crate::{
|
|||
offers::handler::{CreateOfferParams, SendPaymentParams},
|
||||
onion_messenger::MessengerUtilities,
|
||||
};
|
||||
use std::collections::HashSet;
|
||||
|
||||
use super::{validate_amount, OfferError};
|
||||
|
||||
|
|
@ -190,13 +191,33 @@ pub(super) async fn create_offer(
|
|||
let secp_ctx = Secp256k1::new();
|
||||
|
||||
let message_context = MessageContext::Offers(OffersContext::InvoiceRequest { nonce });
|
||||
let path = create_reply_path(creator, node_id, message_context, entropy_source).await?;
|
||||
let paths =
|
||||
create_reply_path_for_offer_creation(creator, node_id, message_context, entropy_source)
|
||||
.await?;
|
||||
|
||||
let mut builder =
|
||||
OfferBuilder::deriving_signing_pubkey(node_id, expanded_key, nonce, &secp_ctx)
|
||||
.amount_msats(args.amount_msats)
|
||||
.chain(args.chain)
|
||||
.path(path);
|
||||
.chain(args.chain);
|
||||
|
||||
builder = if let Some(path) = paths.first() {
|
||||
builder.path(path.clone())
|
||||
} else {
|
||||
builder
|
||||
};
|
||||
|
||||
builder = if let Some(path) = paths.get(1) {
|
||||
builder.path(path.clone())
|
||||
} else {
|
||||
builder
|
||||
};
|
||||
|
||||
builder = if let Some(path) = paths.get(2) {
|
||||
builder.path(path.clone())
|
||||
} else {
|
||||
builder
|
||||
};
|
||||
|
||||
builder = match args.description {
|
||||
Some(description) => builder.description(description),
|
||||
None => builder,
|
||||
|
|
@ -219,6 +240,7 @@ pub(super) async fn create_offer(
|
|||
}
|
||||
None => builder,
|
||||
};
|
||||
|
||||
let offer = builder.build().map_err(OfferError::CreateOfferFailure)?;
|
||||
Ok(offer)
|
||||
}
|
||||
|
|
@ -253,15 +275,99 @@ pub(super) async fn create_invoice_info_from_request(
|
|||
payment_paths,
|
||||
})
|
||||
}
|
||||
/// create_reply_path creates a blinded path to provide to the offer node when requesting an
|
||||
/// invoice so they know where to send the invoice back to. We try to find a peer that we're
|
||||
/// connected to with the necessary requirements to form a blinded path. The peer needs two
|
||||
|
||||
/// create_reply_path_for_offer_creation creates blinded paths to provide to the offer.
|
||||
/// We try to find at least one active public channel and at most three active public channels that
|
||||
/// can act as blinded paths.
|
||||
///
|
||||
/// Otherwise we create a blinded path directly to ourselves.
|
||||
pub async fn create_reply_path_for_offer_creation(
|
||||
mut connector: impl PeerConnector + std::marker::Send + 'static,
|
||||
node_id: PublicKey,
|
||||
message_context: MessageContext,
|
||||
messenger_utils: &MessengerUtilities,
|
||||
) -> Result<Vec<BlindedMessagePath>, OfferError> {
|
||||
// Find introduction channels for our blinded paths.
|
||||
let current_channels = connector.list_active_public_channels().await.map_err(|e| {
|
||||
error!("Could not lookup current channels: {e}.");
|
||||
OfferError::ListChannelsFailure(e)
|
||||
})?;
|
||||
|
||||
let mut intro_channels = HashSet::new();
|
||||
for channel in current_channels.channels.iter() {
|
||||
let pubkey = channel.remote_pubkey.clone();
|
||||
if intro_channels.len() > 3 {
|
||||
break;
|
||||
}
|
||||
match connector.get_node_info(pubkey, true).await {
|
||||
Ok(node_info) => match node_info.node {
|
||||
Some(node) => {
|
||||
if node_info.channels.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let onion_support = features_support_onion_messages(&node.features);
|
||||
let other_channels = node_info
|
||||
.channels
|
||||
.into_iter()
|
||||
.filter(|peer_channel| peer_channel.channel_id != channel.chan_id)
|
||||
.collect::<Vec<tonic_lnd::lnrpc::ChannelEdge>>();
|
||||
if !other_channels.is_empty() && onion_support {
|
||||
let pubkey = PublicKey::from_str(&channel.remote_pubkey).unwrap();
|
||||
intro_channels.insert(pubkey);
|
||||
}
|
||||
}
|
||||
None => continue,
|
||||
},
|
||||
Err(_) => continue,
|
||||
}
|
||||
}
|
||||
|
||||
let secp_ctx = Secp256k1::new();
|
||||
if intro_channels.is_empty() {
|
||||
debug!(
|
||||
"Failed to create a blinded path for the offer.
|
||||
No active public channels were found on which to build a path."
|
||||
);
|
||||
let path =
|
||||
BlindedMessagePath::one_hop(node_id, message_context, messenger_utils, &secp_ctx)
|
||||
.map_err(|_| {
|
||||
error!("Could not create blinded path.");
|
||||
OfferError::BuildBlindedPathFailure
|
||||
})?;
|
||||
Ok(vec![path])
|
||||
} else {
|
||||
let mut paths = vec![];
|
||||
for node in intro_channels {
|
||||
let nodes = vec![lightning::blinded_path::message::MessageForwardNode {
|
||||
node_id: node,
|
||||
short_channel_id: None,
|
||||
}];
|
||||
let path = BlindedMessagePath::new(
|
||||
&nodes,
|
||||
node_id,
|
||||
message_context.clone(),
|
||||
messenger_utils,
|
||||
&secp_ctx,
|
||||
)
|
||||
.map_err(|_| {
|
||||
error!("Could not create blinded path.");
|
||||
OfferError::BuildBlindedPathFailure
|
||||
})?;
|
||||
paths.push(path)
|
||||
}
|
||||
Ok(paths)
|
||||
}
|
||||
}
|
||||
|
||||
/// create_reply_path_for_outgoing_payments creates a blinded path to provide to the offer node when
|
||||
/// requesting an invoice so they know where to send the invoice back to. We try to find a peer that
|
||||
/// we're connected to with the necessary requirements to form a blinded path. The peer needs two
|
||||
/// things:
|
||||
/// 1) Onion messaging support.
|
||||
/// 2) To be an advertised node with at least one public channel.
|
||||
///
|
||||
/// Otherwise we create a blinded path directly to ourselves.
|
||||
pub async fn create_reply_path(
|
||||
pub async fn create_reply_path_for_outgoing_payments(
|
||||
mut connector: impl PeerConnector + std::marker::Send + 'static,
|
||||
node_id: PublicKey,
|
||||
message_context: MessageContext,
|
||||
|
|
@ -349,7 +455,7 @@ pub async fn send_invoice_request(
|
|||
|
||||
let pubkey = PublicKey::from_str(&info.identity_pubkey).unwrap();
|
||||
let message_context = MessageContext::Offers(offer_context);
|
||||
let reply_path = create_reply_path(
|
||||
let reply_path = create_reply_path_for_outgoing_payments(
|
||||
client.lightning().clone(),
|
||||
pubkey,
|
||||
message_context,
|
||||
|
|
@ -526,7 +632,8 @@ mod tests {
|
|||
use tonic_lnd::{
|
||||
lnrpc::{
|
||||
AddInvoiceResponse, ChannelEdge, GetInfoResponse, HtlcAttempt, LightningNode,
|
||||
ListPeersResponse, NodeAddress, NodeInfo, PayReq, QueryRoutesResponse, Route,
|
||||
ListChannelsResponse, ListPeersResponse, NodeAddress, NodeInfo, PayReq,
|
||||
QueryRoutesResponse, Route,
|
||||
},
|
||||
tonic::Status,
|
||||
};
|
||||
|
|
@ -687,7 +794,24 @@ mod tests {
|
|||
|
||||
let receiver_node_id = PublicKey::from_str(&get_pubkeys()[0]).unwrap();
|
||||
let message_context = get_message_context();
|
||||
let response = create_reply_path(
|
||||
let response = create_reply_path_for_outgoing_payments(
|
||||
connector_mock,
|
||||
receiver_node_id,
|
||||
message_context,
|
||||
&MessengerUtilities::new([42; 32]),
|
||||
)
|
||||
.await;
|
||||
assert!(response.is_ok());
|
||||
|
||||
let mut connector_mock = MockTestPeerConnector::new();
|
||||
|
||||
connector_mock
|
||||
.expect_list_active_public_channels()
|
||||
.returning(|| Ok(ListChannelsResponse { channels: vec![] }));
|
||||
|
||||
let receiver_node_id = PublicKey::from_str(&get_pubkeys()[0]).unwrap();
|
||||
let message_context = get_message_context();
|
||||
let response = create_reply_path_for_offer_creation(
|
||||
connector_mock,
|
||||
receiver_node_id,
|
||||
message_context,
|
||||
|
|
@ -698,7 +822,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_reply_path() {
|
||||
async fn test_reply_path_for_outgoing_payments() {
|
||||
let mut connector_mock = MockTestPeerConnector::new();
|
||||
|
||||
connector_mock.expect_list_peers().returning(|| {
|
||||
|
|
@ -729,7 +853,54 @@ mod tests {
|
|||
let receiver_node_id = PublicKey::from_str(&get_pubkeys()[0]).unwrap();
|
||||
|
||||
let message_context = get_message_context();
|
||||
let response = create_reply_path(
|
||||
let response = create_reply_path_for_outgoing_payments(
|
||||
connector_mock,
|
||||
receiver_node_id,
|
||||
message_context,
|
||||
&MessengerUtilities::new([42; 32]),
|
||||
)
|
||||
.await;
|
||||
assert!(response.is_ok());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_reply_path_for_offer_creation() {
|
||||
let mut connector_mock = MockTestPeerConnector::new();
|
||||
|
||||
connector_mock
|
||||
.expect_list_active_public_channels()
|
||||
.returning(|| {
|
||||
let channel = tonic_lnd::lnrpc::Channel {
|
||||
remote_pubkey: get_pubkeys()[0].clone(),
|
||||
..Default::default()
|
||||
};
|
||||
Ok(ListChannelsResponse {
|
||||
channels: vec![channel],
|
||||
})
|
||||
});
|
||||
|
||||
connector_mock.expect_get_node_info().returning(|_, _| {
|
||||
let node_addr = NodeAddress {
|
||||
network: String::from("regtest"),
|
||||
addr: String::from("127.0.0.1"),
|
||||
};
|
||||
let node = Some(LightningNode {
|
||||
addresses: vec![node_addr],
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let node_info = NodeInfo {
|
||||
node,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
Ok(node_info)
|
||||
});
|
||||
|
||||
let receiver_node_id = PublicKey::from_str(&get_pubkeys()[0]).unwrap();
|
||||
|
||||
let message_context = get_message_context();
|
||||
let response = create_reply_path_for_offer_creation(
|
||||
connector_mock,
|
||||
receiver_node_id,
|
||||
message_context,
|
||||
|
|
@ -749,7 +920,24 @@ mod tests {
|
|||
|
||||
let receiver_node_id = PublicKey::from_str(&get_pubkeys()[0]).unwrap();
|
||||
let message_context = get_message_context();
|
||||
let response = create_reply_path(
|
||||
let response = create_reply_path_for_outgoing_payments(
|
||||
connector_mock,
|
||||
receiver_node_id,
|
||||
message_context,
|
||||
&MessengerUtilities::new([42; 32]),
|
||||
)
|
||||
.await;
|
||||
assert!(response.is_err());
|
||||
|
||||
let mut connector_mock = MockTestPeerConnector::new();
|
||||
|
||||
connector_mock
|
||||
.expect_list_active_public_channels()
|
||||
.returning(|| Err(Status::unknown("unknown error")));
|
||||
|
||||
let receiver_node_id = PublicKey::from_str(&get_pubkeys()[0]).unwrap();
|
||||
let message_context = get_message_context();
|
||||
let response = create_reply_path_for_offer_creation(
|
||||
connector_mock,
|
||||
receiver_node_id,
|
||||
message_context,
|
||||
|
|
@ -760,7 +948,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_reply_path_not_advertised() {
|
||||
async fn test_create_reply_path_for_outgoing_payments_not_advertised() {
|
||||
// First lets test that if we're only connected to one peer. It has onion support, but the
|
||||
// node isn't advertised, meaning it has no public channels. This should return
|
||||
// a blinded path with only one hop.
|
||||
|
|
@ -786,7 +974,7 @@ mod tests {
|
|||
|
||||
let receiver_node_id = PublicKey::from_str(&get_pubkeys()[0]).unwrap();
|
||||
let message_context = get_message_context();
|
||||
let response = create_reply_path(
|
||||
let response = create_reply_path_for_outgoing_payments(
|
||||
connector_mock,
|
||||
receiver_node_id,
|
||||
message_context,
|
||||
|
|
@ -851,7 +1039,7 @@ mod tests {
|
|||
|
||||
let receiver_node_id = PublicKey::from_str(&get_pubkeys()[0]).unwrap();
|
||||
let message_context = get_message_context();
|
||||
let response = create_reply_path(
|
||||
let response = create_reply_path_for_outgoing_payments(
|
||||
connector_mock,
|
||||
receiver_node_id,
|
||||
message_context,
|
||||
|
|
@ -864,6 +1052,124 @@ mod tests {
|
|||
assert!(hops.len() == 2);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_reply_path_for_offer_creation_not_advertised() {
|
||||
// First lets test that the one that we are trying to connect to, does not have public
|
||||
// channels This should return a blinded path with only one hop.
|
||||
let mut connector_mock = MockTestPeerConnector::new();
|
||||
|
||||
connector_mock
|
||||
.expect_list_active_public_channels()
|
||||
.returning(|| Ok(ListChannelsResponse { channels: vec![] }));
|
||||
|
||||
connector_mock.expect_get_node_info().returning(|_, _| {
|
||||
let node_addr = NodeAddress {
|
||||
network: String::from("regtest"),
|
||||
addr: String::from("127.0.0.1"),
|
||||
};
|
||||
let node = Some(LightningNode {
|
||||
addresses: vec![node_addr],
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let node_info = NodeInfo {
|
||||
node,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
Ok(node_info)
|
||||
});
|
||||
|
||||
let receiver_node_id = PublicKey::from_str(&get_pubkeys()[0]).unwrap();
|
||||
let message_context = get_message_context();
|
||||
let response = create_reply_path_for_offer_creation(
|
||||
connector_mock,
|
||||
receiver_node_id,
|
||||
message_context,
|
||||
&MessengerUtilities::new([42; 32]),
|
||||
)
|
||||
.await;
|
||||
assert!(response.is_ok());
|
||||
let reply_path = response.unwrap();
|
||||
let hops = reply_path.first().unwrap().blinded_hops();
|
||||
assert!(hops.len() == 1);
|
||||
|
||||
// Now let's test that we are connected to two channels.
|
||||
// One channel isn't advertised (i.e. it has no public channels). But the second is. This
|
||||
// should succeed.
|
||||
let mut connector_mock = MockTestPeerConnector::new();
|
||||
|
||||
// Only second channel is advertised.
|
||||
connector_mock
|
||||
.expect_list_active_public_channels()
|
||||
.returning(|| {
|
||||
let keys = get_pubkeys();
|
||||
|
||||
let channel2 = tonic_lnd::lnrpc::Channel {
|
||||
remote_pubkey: keys[1].clone(),
|
||||
chan_id: 10,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
Ok(ListChannelsResponse {
|
||||
channels: vec![channel2],
|
||||
})
|
||||
});
|
||||
|
||||
connector_mock.expect_get_node_info().returning(|_, _| {
|
||||
let node_addr = NodeAddress {
|
||||
network: String::from("regtest"),
|
||||
addr: String::from("127.0.0.1"),
|
||||
};
|
||||
let mut features = std::collections::HashMap::new();
|
||||
// Add onion message feature (feature bit 38/39)
|
||||
features.insert(
|
||||
38,
|
||||
tonic_lnd::lnrpc::Feature {
|
||||
name: "onion_messages_optional".to_string(),
|
||||
is_required: false,
|
||||
is_known: true,
|
||||
},
|
||||
);
|
||||
let node = Some(LightningNode {
|
||||
addresses: vec![node_addr],
|
||||
features,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let node_info = NodeInfo {
|
||||
node,
|
||||
channels: vec![
|
||||
ChannelEdge {
|
||||
channel_id: 10,
|
||||
..Default::default()
|
||||
},
|
||||
ChannelEdge {
|
||||
channel_id: 20,
|
||||
..Default::default()
|
||||
},
|
||||
],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
Ok(node_info)
|
||||
});
|
||||
|
||||
let receiver_node_id = PublicKey::from_str(&get_pubkeys()[0]).unwrap();
|
||||
let message_context = get_message_context();
|
||||
let response = create_reply_path_for_offer_creation(
|
||||
connector_mock,
|
||||
receiver_node_id,
|
||||
message_context,
|
||||
&MessengerUtilities::new([42; 32]),
|
||||
)
|
||||
.await;
|
||||
assert!(response.is_ok());
|
||||
let reply_path = response.unwrap();
|
||||
let hops = reply_path.first().unwrap().blinded_hops();
|
||||
assert!(hops.len() == 2);
|
||||
}
|
||||
|
||||
fn get_blinded_payment_path() -> BlindedPaymentPath {
|
||||
let entropy_source = MessengerUtilities::new([42; 32]);
|
||||
let secp_ctx = Secp256k1::new();
|
||||
|
|
@ -1080,6 +1386,36 @@ mod tests {
|
|||
Ok(ListPeersResponse { peers: vec![peer] })
|
||||
});
|
||||
|
||||
creator_mock
|
||||
.expect_list_active_public_channels()
|
||||
.returning(|| {
|
||||
let channel = tonic_lnd::lnrpc::Channel {
|
||||
remote_pubkey: get_pubkeys()[0].clone(),
|
||||
..Default::default()
|
||||
};
|
||||
Ok(ListChannelsResponse {
|
||||
channels: vec![channel],
|
||||
})
|
||||
});
|
||||
|
||||
creator_mock.expect_get_node_info().returning(|_, _| {
|
||||
let node_addr = NodeAddress {
|
||||
network: String::from("regtest"),
|
||||
addr: String::from("127.0.0.1"),
|
||||
};
|
||||
let node = Some(LightningNode {
|
||||
addresses: vec![node_addr],
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let node_info = NodeInfo {
|
||||
node,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
Ok(node_info)
|
||||
});
|
||||
|
||||
creator_mock
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@ mod lnd_requests;
|
|||
mod parse;
|
||||
|
||||
pub(crate) use lnd_requests::connect_to_peer_with_retry;
|
||||
pub use lnd_requests::create_reply_path;
|
||||
pub use lnd_requests::create_reply_path_for_offer_creation;
|
||||
pub use lnd_requests::create_reply_path_for_outgoing_payments;
|
||||
pub use parse::{decode, get_destination, validate_amount};
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -69,6 +70,8 @@ pub enum OfferError {
|
|||
ParseInvoiceFailure(DecodeError),
|
||||
/// Failed to encode invoice.
|
||||
EncodeInvoiceFailure(BitcoinIoError),
|
||||
/// Cannot list channels.
|
||||
ListChannelsFailure(LndStatus),
|
||||
}
|
||||
|
||||
impl OfferError {
|
||||
|
|
@ -98,6 +101,7 @@ impl OfferError {
|
|||
OfferError::InvoiceTimeout(_) => "INVOICE_TIMEOUT",
|
||||
OfferError::IntroductionNodeNotFound => "INTRODUCTION_NODE_NOT_FOUND",
|
||||
OfferError::GetChannelInfo(_) => "GET_CHANNEL_INFO",
|
||||
OfferError::ListChannelsFailure(_) => "LIST_CHANNELS_FAILURE",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -173,6 +177,7 @@ impl Display for OfferError {
|
|||
OfferError::EncodeInvoiceFailure(e) => {
|
||||
write!(f, "Failed to encode invoice to hex format. Error: {e:?}")
|
||||
}
|
||||
OfferError::ListChannelsFailure(e) => write!(f, "Error listing channels: {e:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1135,6 +1135,7 @@ mod tests {
|
|||
async fn list_peers(&mut self) -> Result<tonic_lnd::lnrpc::ListPeersResponse, Status>;
|
||||
async fn get_node_info(&mut self, pub_key: String, include_channels: bool) -> Result<tonic_lnd::lnrpc::NodeInfo, Status>;
|
||||
async fn connect_peer(&mut self, node_id: String, addr: String) -> Result<(), Status>;
|
||||
async fn list_active_public_channels(&mut self) -> Result<tonic_lnd::lnrpc::ListChannelsResponse, Status>;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ use lightning::offers::offer::Quantity;
|
|||
use lightning::onion_message::messenger::Destination;
|
||||
use lndk::lnd::validate_lnd_creds;
|
||||
use lndk::offers::handler::{CreateOfferParams, OfferHandler, PayOfferParams};
|
||||
use lndk::offers::{create_reply_path, OfferError};
|
||||
use lndk::offers::{create_reply_path_for_outgoing_payments, OfferError};
|
||||
use lndk::onion_messenger::MessengerUtilities;
|
||||
use lndk::{setup_logger, LifecycleSignals};
|
||||
use std::net::SocketAddr;
|
||||
|
|
@ -498,7 +498,7 @@ async fn test_reply_path_unannounced_peers() {
|
|||
// private channel and as such, is an unadvertised node. Because of that, create_reply_path
|
||||
// should not use ldk2 as an introduction node and should return a reply path directly to
|
||||
// itself.
|
||||
let reply_path = create_reply_path(
|
||||
let reply_path = create_reply_path_for_outgoing_payments(
|
||||
lnd.client.clone().unwrap().lightning().clone(),
|
||||
lnd_pubkey,
|
||||
offer_context,
|
||||
|
|
@ -541,7 +541,7 @@ async fn test_reply_path_announced_peers() {
|
|||
// public channel and as such, is indeed an advertised node. Because of this, we make sure
|
||||
// create_reply_path produces a path of length two with ldk2 as the introduction node, as we
|
||||
// expected.
|
||||
let reply_path = create_reply_path(
|
||||
let reply_path = create_reply_path_for_outgoing_payments(
|
||||
lnd.client.clone().unwrap().lightning().clone(),
|
||||
lnd_pubkey,
|
||||
offer_context,
|
||||
|
|
@ -1116,16 +1116,18 @@ async fn test_receive_payment_from_offer() {
|
|||
expiry: None,
|
||||
};
|
||||
|
||||
lnd.wait_for_addresses_to_sync(ldk1_pubkey).await;
|
||||
|
||||
let offer = handler.create_offer(create_offer_params).await;
|
||||
assert!(offer.is_ok());
|
||||
let offer = offer.unwrap();
|
||||
lnd.wait_for_addresses_to_sync(ldk1_pubkey).await;
|
||||
|
||||
select! {
|
||||
val = messenger.run(lndk_cfg, Arc::clone(&handler)) => {
|
||||
panic!("lndk should not have completed first {:?}", val);
|
||||
},
|
||||
res = pay_offer_and_wait_for_payment(&ldk1, offer, lnd.client.clone().unwrap()) => {
|
||||
log::info!("res: {:?}", res);
|
||||
assert!(res.is_ok());
|
||||
shutdown.trigger();
|
||||
ldk1.stop().await;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue