Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Offers: use offer handler to respond with an BOLT 12 invoice #8

Merged
merged 3 commits into from
Jan 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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![]
}
}