Skip to content

Commit

Permalink
Merge pull request #8 from lndk-org/offer-handling
Browse files Browse the repository at this point in the history
Offers: use offer handler to respond with an BOLT 12 invoice
  • Loading branch information
orbitalturtle authored Jan 18, 2024
2 parents e0dd855 + add68ad commit 1624431
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 16 deletions.
2 changes: 1 addition & 1 deletion src/disk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ impl Logger for FilesystemLogger {
.unwrap();
}
}
#[allow(dead_code)]

pub(crate) fn persist_channel_peer(path: &Path, peer_info: &str) -> std::io::Result<()> {
let mut file = fs::OpenOptions::new().create(true).append(true).open(path)?;
file.write_all(format!("{}\n", peer_info).as_bytes())
Expand Down
17 changes: 6 additions & 11 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ pub(crate) type OnionMessengerType = OnionMessenger<
Arc<KeysManager>,
Arc<FilesystemLogger>,
Arc<DefaultMessageRouter>,
IgnoringMessageHandler,
Arc<OnionMessageHandler>,
Arc<OnionMessageHandler>,
>;

Expand Down Expand Up @@ -370,8 +370,6 @@ async fn handle_ldk_events(
hex_utils::hex_str(&counterparty_node_id.serialize()),
);
}
print!("> ");
io::stdout().flush().unwrap();
}
Event::PaymentPathSuccessful { .. } => {}
Event::PaymentPathFailed { .. } => {}
Expand Down Expand Up @@ -500,8 +498,6 @@ async fn handle_ldk_events(
channel_id,
hex_utils::hex_str(&counterparty_node_id.serialize()),
);
print!("> ");
io::stdout().flush().unwrap();
}
Event::ChannelReady {
ref channel_id,
Expand All @@ -514,8 +510,6 @@ async fn handle_ldk_events(
channel_id,
hex_utils::hex_str(&counterparty_node_id.serialize()),
);
print!("> ");
io::stdout().flush().unwrap();
}
Event::ChannelClosed {
channel_id,
Expand All @@ -530,8 +524,6 @@ async fn handle_ldk_events(
counterparty_node_id.map(|id| format!("{}", id)).unwrap_or("".to_owned()),
reason
);
print!("> ");
io::stdout().flush().unwrap();
}
Event::DiscardFunding { .. } => {
// A "real" node should probably "lock" the UTXOs spent in funding transactions until
Expand Down Expand Up @@ -790,17 +782,20 @@ pub async fn start_ldk(args: config::LdkUserInfo, test_name: &str) -> node_api::
Arc::new(P2PGossipSync::new(Arc::clone(&network_graph), None, Arc::clone(&logger)));

// Step 15: Initialize the PeerManager
let channel_manager: Arc<ChannelManager> = Arc::new(channel_manager);
let onion_message_handler = Arc::new(OnionMessageHandler {
messages: Arc::new(Mutex::new(VecDeque::new())),
logger: Arc::clone(&logger),
keys_manager: Arc::clone(&keys_manager),
channel_manager: channel_manager.clone(),
node_id: channel_manager.get_our_node_id(),
});
let channel_manager: Arc<ChannelManager> = Arc::new(channel_manager);
let onion_messenger: Arc<OnionMessengerType> = Arc::new(OnionMessenger::new(
Arc::clone(&keys_manager),
Arc::clone(&keys_manager),
Arc::clone(&logger),
Arc::new(DefaultMessageRouter {}),
IgnoringMessageHandler {},
Arc::clone(&onion_message_handler),
Arc::clone(&onion_message_handler),
));
let mut ephemeral_bytes = [0; 32];
Expand Down
51 changes: 49 additions & 2 deletions src/node_api.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::disk::FilesystemLogger;
use crate::disk::{persist_channel_peer, FilesystemLogger};
use crate::onion::{OnionMessageHandler, UserOnionMessageContents};
use crate::{
BitcoindClient, ChainMonitor, ChannelManager, NetworkGraph, OnionMessengerType,
Expand All @@ -8,14 +8,18 @@ use crate::{
use bitcoin::secp256k1::{PublicKey, Secp256k1};
use bitcoin::Network;
use lightning::blinded_path::BlindedPath;
use lightning::ln::ChannelId;
use lightning::offers::offer::{Offer, OfferBuilder, Quantity};
use lightning::offers::parse::Bolt12SemanticError;
use lightning::onion_message::{Destination, OnionMessagePath};
use lightning::routing::router::DefaultRouter;
use lightning::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringFeeParameters};
use lightning::sign::KeysManager;
use lightning::util::config::{ChannelHandshakeConfig, ChannelHandshakeLimits, UserConfig};
use lightning::util::errors::APIError;
use lightning_persister::fs_store::FilesystemStore;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::path::Path;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, RwLock};
use std::time::{Duration, SystemTime};
Expand All @@ -33,7 +37,7 @@ pub(crate) type Scorer = ProbabilisticScorer<Arc<NetworkGraph>, Arc<FilesystemLo

pub struct Node {
pub(crate) logger: Arc<FilesystemLogger>,
pub(crate) bitcoind_client: Arc<BitcoindClient>,
pub bitcoind_client: Arc<BitcoindClient>,
pub(crate) persister: Arc<FilesystemStore>,
pub(crate) chain_monitor: Arc<ChainMonitor>,
pub(crate) keys_manager: Arc<KeysManager>,
Expand Down Expand Up @@ -115,6 +119,32 @@ impl Node {
}
}

pub async fn open_channel(
&self, peer_pubkey: PublicKey, peer_addr: SocketAddr, chan_amt_sat: u64, push_msat: u64,
announce_channel: bool,
) -> Result<ChannelId, APIError> {
if self.connect_to_peer(peer_pubkey, peer_addr).await.is_err() {
return Err(APIError::ChannelUnavailable { err: "Cannot connect to peer".to_string() });
};

let peer_pubkey_and_ip_addr = peer_pubkey.to_string() + "@" + &peer_addr.to_string();
match open_channel(
peer_pubkey,
chan_amt_sat,
push_msat,
announce_channel,
false, // Without anchors for simplicity.
self.channel_manager.clone(),
) {
Ok(channel_id) => {
let peer_data_path = format!("{:?}/channel_peer_data", self.ldk_data_dir);
let _ = persist_channel_peer(Path::new(&peer_data_path), &peer_pubkey_and_ip_addr);
Ok(channel_id)
}
Err(e) => Err(e),
}
}

pub async fn send_onion_message(
&self, mut intermediate_nodes: Vec<PublicKey>, tlv_type: u64, data: Vec<u8>,
) -> Result<(), ()> {
Expand Down Expand Up @@ -178,3 +208,20 @@ impl Node {
}
}
}

fn open_channel(
peer_pubkey: PublicKey, channel_amt_sat: u64, push_msat: u64, announced_channel: bool,
with_anchors: bool, channel_manager: Arc<ChannelManager>,
) -> Result<ChannelId, APIError> {
let config = UserConfig {
channel_handshake_limits: ChannelHandshakeLimits { ..Default::default() },
channel_handshake_config: ChannelHandshakeConfig {
announced_channel,
negotiate_anchors_zero_fee_htlc_tx: with_anchors,
..Default::default()
},
..Default::default()
};

channel_manager.create_channel(peer_pubkey, channel_amt_sat, push_msat, 0, Some(config))
}
89 changes: 87 additions & 2 deletions src/onion.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
use crate::disk::FilesystemLogger;
use crate::ChannelManager;
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::Hash;
use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1};
use core::convert::Infallible;
use lightning::blinded_path::payment::{PaymentConstraints, ReceiveTlvs};
use lightning::blinded_path::BlindedPath;
use lightning::io::Read;
use lightning::ln::msgs::DecodeError;
use lightning::log_info;
use lightning::ln::{PaymentHash, PaymentPreimage};
use lightning::onion_message::{
CustomOnionMessageHandler, OnionMessageContents, PendingOnionMessage,
CustomOnionMessageHandler, OffersMessage, OffersMessageHandler, OnionMessageContents,
PendingOnionMessage,
};
use lightning::sign::KeysManager;
use lightning::util::logger::Logger;
use lightning::util::ser::{Writeable, Writer};
use lightning::{log_error, log_info};
use std::collections::VecDeque;
use std::sync::{Arc, Mutex};

Expand All @@ -33,6 +43,9 @@ impl Writeable for UserOnionMessageContents {
pub struct OnionMessageHandler {
pub messages: Arc<Mutex<VecDeque<UserOnionMessageContents>>>,
pub(crate) logger: Arc<FilesystemLogger>,
pub(crate) keys_manager: Arc<KeysManager>,
pub(crate) channel_manager: Arc<ChannelManager>,
pub(crate) node_id: PublicKey,
}

impl CustomOnionMessageHandler for OnionMessageHandler {
Expand All @@ -56,3 +69,75 @@ impl CustomOnionMessageHandler for OnionMessageHandler {
vec![]
}
}

impl OffersMessageHandler for OnionMessageHandler {
fn handle_message(&self, message: OffersMessage) -> Option<OffersMessage> {
log_info!(self.logger, "Received a new offers message!");
match message {
OffersMessage::InvoiceRequest(ref invoice_request) => {
// Create a test preimage/secret for the payment. Since these are just for our tests
// the values here don't really matter that much.
let payment_preimage = PaymentPreimage([0; 32]);
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).into_inner());
// TODO: Not sure if we'll need to set these None values when the payment is actually made.
let payment_secret = self
.channel_manager
.create_inbound_payment_for_hash(payment_hash, None, 7200, None)
.unwrap();

// Reminder that to keep things simple for our tests, we assume we're only connected to zero or one channel
// for now.
let chans = self.channel_manager.list_channels();
let htlc_minimum_msat =
if chans.len() == 0 { 1 } else { chans[0].inbound_htlc_minimum_msat.unwrap() };

let payee_tlvs = ReceiveTlvs {
payment_secret,
payment_constraints: PaymentConstraints {
max_cltv_expiry: u32::max_value(),
htlc_minimum_msat,
},
};

let secp_ctx = Secp256k1::new();
let blinded_path = BlindedPath::one_hop_for_payment(
self.node_id,
payee_tlvs,
&*self.keys_manager,
&secp_ctx,
)
.unwrap();

let secret_key = self.keys_manager.get_node_secret_key();
let keys = KeyPair::from_secret_key(&secp_ctx, &secret_key);
let pubkey = PublicKey::from(keys);
let wpubkey_hash =
bitcoin::util::key::PublicKey::new(pubkey).wpubkey_hash().unwrap();

return Some(OffersMessage::Invoice(
invoice_request
.respond_with(vec![blinded_path], payment_hash)
.unwrap()
.relative_expiry(3600)
//.allow_mpp()
.fallback_v0_p2wpkh(&wpubkey_hash)
.build()
.unwrap()
.sign::<_, Infallible>(|message| {
Ok(secp_ctx
.sign_schnorr_no_aux_rand(message.as_ref().as_digest(), &keys))
})
.expect("failed verifying signature"),
));
}
_ => {
log_error!(self.logger, "Unsupported offers message type");
return None;
}
};
}

fn release_pending_messages(&self) -> Vec<PendingOnionMessage<OffersMessage>> {
vec![]
}
}

0 comments on commit 1624431

Please sign in to comment.