Skip to content

Commit

Permalink
multi: send offer payment
Browse files Browse the repository at this point in the history
  • Loading branch information
orbitalturtle committed Feb 2, 2024
1 parent 155db86 commit 9f60165
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 19 deletions.
55 changes: 46 additions & 9 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ mod rate_limit;
use crate::lnd::{
features_support_onion_messages, get_lnd_client, string_to_network, LndCfg, LndNodeSigner,
};
use crate::lndk_offers::{connect_to_peer, validate_amount, OfferError};
use crate::lndk_offers::{connect_to_peer, pay_invoice, validate_amount, OfferError};
use crate::onion_messenger::MessengerUtilities;
use bitcoin::network::constants::Network;
use bitcoin::secp256k1::{Error as Secp256k1Error, PublicKey, Secp256k1};
use home::home_dir;
use lightning::blinded_path::BlindedPath;
use lightning::ln::inbound_payment::ExpandedKey;
use lightning::ln::peer_handler::IgnoringMessageHandler;
use lightning::offers::invoice::Bolt12Invoice;
use lightning::offers::invoice_error::InvoiceError;
use lightning::offers::offer::Offer;
use lightning::onion_message::messenger::{
Expand All @@ -34,6 +35,7 @@ use std::collections::HashMap;
use std::str::FromStr;
use std::sync::{Mutex, Once};
use tokio::sync::mpsc::{Receiver, Sender};
use tokio::time::{sleep, Duration};
use tonic_lnd::lnrpc::GetInfoRequest;
use tonic_lnd::Client;
use triggered::{Listener, Trigger};
Expand Down Expand Up @@ -191,6 +193,7 @@ enum OfferState {

pub struct OfferHandler {
active_offers: Mutex<HashMap<String, OfferState>>,
active_invoices: Mutex<Vec<Bolt12Invoice>>,
pending_messages: Mutex<Vec<PendingOnionMessage<OffersMessage>>>,
pub messenger_utils: MessengerUtilities,
expanded_key: ExpandedKey,
Expand All @@ -206,6 +209,7 @@ impl OfferHandler {

OfferHandler {
active_offers: Mutex::new(HashMap::new()),
active_invoices: Mutex::new(Vec::new()),
pending_messages: Mutex::new(Vec::new()),
messenger_utils: MessengerUtilities::new(),
expanded_key,
Expand Down Expand Up @@ -263,14 +267,40 @@ impl OfferHandler {
reply_path,
};

let mut pending_messages = self.pending_messages.lock().unwrap();
pending_messages.push(pending_message);
std::mem::drop(pending_messages);
{
let mut pending_messages = self.pending_messages.lock().unwrap();
pending_messages.push(pending_message);

let mut active_offers = self.active_offers.lock().unwrap();
active_offers.insert(offer.to_string().clone(), OfferState::InvoiceRequestSent);
}

let mut active_offers = self.active_offers.lock().unwrap();
active_offers.insert(offer.to_string(), OfferState::InvoiceRequestSent);
let invoice = self.wait_for_invoice().await;
let payment_hash = invoice.payment_hash();
let path_info = invoice.payment_paths()[0].clone();

pay_invoice(
client,
path_info.1,
path_info.0.cltv_expiry_delta,
path_info.0.fee_base_msat,
payment_hash.0,
amount.unwrap(),
)
.await
}

Ok(())
// wait_for_invoice waits for the offer creator to respond with an invoice.
pub async fn wait_for_invoice(&self) -> Bolt12Invoice {
loop {
{
let mut active_invoices = self.active_invoices.lock().unwrap();
if active_invoices.len() == 1 {
return active_invoices.pop().unwrap();
}
}
sleep(Duration::from_secs(2)).await;
}
}
}

Expand All @@ -287,13 +317,20 @@ impl OffersMessageHandler for OfferHandler {
match invoice.verify(&self.expanded_key, secp_ctx) {
// TODO: Eventually we can use the returned payment id below to check if we
// already processed an invoice for this payment.
Ok(_payment_id) => Some(OffersMessage::Invoice(invoice)),
Ok(_payment_id) => {
let mut active_invoices = self.active_invoices.lock().unwrap();
active_invoices.push(invoice.clone());
Some(OffersMessage::Invoice(invoice))
}
Err(()) => Some(OffersMessage::InvoiceError(InvoiceError::from_string(
String::from("invoice verification failure"),
))),
}
}
OffersMessage::InvoiceError(_error) => None,
OffersMessage::InvoiceError(error) => {
log::error!("Invoice error received: {}", error);
None
}
}
}

Expand Down
71 changes: 61 additions & 10 deletions tests/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,26 +112,78 @@ async fn test_lndk_forwards_onion_message() {
}

#[tokio::test(flavor = "multi_thread")]
// Here we test the beginning of the BOLT 12 offers flow. We show that lndk successfully builds an
// invoice_request and sends it.
async fn test_lndk_send_invoice_request() {
let test_name = "lndk_send_invoice_request";
let (_bitcoind, mut lnd, ldk1, ldk2, lndk_dir) =
// Here we test that we're able to fully pay an offer.
async fn test_lndk_pay_offer() {
let test_name = "lndk_pay_offer";
let (bitcoind, mut lnd, ldk1, ldk2, lndk_dir) =
common::setup_test_infrastructure(test_name).await;

// Here we'll produce a little network path:
// Here we'll produce a little network of channels:
//
// ldk1 <-> ldk2 <-> lnd
// ldk1 <- ldk2 <- lnd
//
// ldk1 will be the offer creator, which will build a blinded route from ldk2 to ldk1.
let (pubkey, _) = ldk1.get_node_info();
let (pubkey, addr) = ldk1.get_node_info();
let (pubkey_2, addr_2) = ldk2.get_node_info();
let lnd_info = lnd.get_info().await;
let lnd_pubkey = PublicKey::from_str(&lnd_info.identity_pubkey).unwrap();

ldk1.connect_to_peer(pubkey_2, addr_2).await.unwrap();
lnd.connect_to_peer(pubkey_2, addr_2).await;

let ldk2_fund_addr = ldk2.bitcoind_client.get_new_address().await;
let lnd_fund_addr = lnd.new_address().await.address;

// We need to convert funding addresses to the form that the bitcoincore_rpc library recognizes.
let ldk2_addr_string = ldk2_fund_addr.to_string();
let ldk2_addr = bitcoind::bitcoincore_rpc::bitcoin::Address::from_str(&ldk2_addr_string)
.unwrap()
.require_network(RpcNetwork::Regtest)
.unwrap();
let lnd_addr = bitcoind::bitcoincore_rpc::bitcoin::Address::from_str(&lnd_fund_addr)
.unwrap()
.require_network(RpcNetwork::Regtest)
.unwrap();
let lnd_network_addr = lnd
.address
.replace("localhost", "127.0.0.1")
.replace("https://", "");

// Fund both of these nodes, open the channels, and synchronize the network.
bitcoind
.node
.client
.generate_to_address(6, &lnd_addr)
.unwrap();

lnd.wait_for_chain_sync().await;

ldk2.open_channel(pubkey, addr, 200000, 0, false)
.await
.unwrap();

lnd.wait_for_graph_sync().await;

ldk2.open_channel(
lnd_pubkey,
SocketAddr::from_str(&lnd_network_addr).unwrap(),
200000,
10000000,
true,
)
.await
.unwrap();

lnd.wait_for_graph_sync().await;

bitcoind
.node
.client
.generate_to_address(20, &ldk2_addr)
.unwrap();

lnd.wait_for_chain_sync().await;

let path_pubkeys = vec![pubkey_2, pubkey];
let expiration = SystemTime::now() + Duration::from_secs(24 * 60 * 60);
let offer = ldk1
Expand All @@ -145,7 +197,6 @@ async fn test_lndk_send_invoice_request() {
.await
.expect("should create offer");

// Now we'll spin up lndk, which should forward the invoice request to ldk2.
let (shutdown, listener) = triggered::trigger();
let lnd_cfg = lndk::lnd::LndCfg::new(
lnd.address.clone(),
Expand All @@ -171,9 +222,9 @@ async fn test_lndk_send_invoice_request() {
signals,
};

let messenger_utils = MessengerUtilities::new();
let client = lnd.client.clone().unwrap();
let blinded_path = offer.paths()[0].clone();
let messenger_utils = MessengerUtilities::new();
let secp_ctx = Secp256k1::new();
let reply_path =
BlindedPath::new_for_message(&[pubkey_2, lnd_pubkey], &messenger_utils, &secp_ctx).unwrap();
Expand Down

0 comments on commit 9f60165

Please sign in to comment.