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

persist payments info to disk #104

Merged
merged 1 commit into from
Jul 25, 2023
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
123 changes: 64 additions & 59 deletions src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::disk;
use crate::disk::{self, INBOUND_PAYMENTS_FNAME, OUTBOUND_PAYMENTS_FNAME};
use crate::hex_utils;
use crate::{
ChannelManager, HTLCStatus, MillisatAmount, NetworkGraph, OnionMessenger, PaymentInfo,
Expand All @@ -16,17 +16,18 @@ use lightning::onion_message::{CustomOnionMessageContents, Destination, OnionMes
use lightning::routing::gossip::NodeId;
use lightning::routing::router::{PaymentParameters, RouteParameters};
use lightning::util::config::{ChannelHandshakeConfig, ChannelHandshakeLimits, UserConfig};
use lightning::util::persist::KVStorePersister;
use lightning::util::ser::{Writeable, Writer};
use lightning_invoice::payment::pay_invoice;
use lightning_invoice::{utils, Currency, Invoice};
use lightning_persister::FilesystemPersister;
use std::env;
use std::io;
use std::io::Write;
use std::net::{SocketAddr, ToSocketAddrs};
use std::ops::Deref;
use std::path::Path;
use std::str::FromStr;
use std::sync::Arc;
use std::sync::{Arc, Mutex};
use std::time::Duration;

pub(crate) struct LdkUserInfo {
Expand Down Expand Up @@ -61,9 +62,9 @@ impl Writeable for UserOnionMessageContents {
pub(crate) async fn poll_for_user_input(
peer_manager: Arc<PeerManager>, channel_manager: Arc<ChannelManager>,
keys_manager: Arc<KeysManager>, network_graph: Arc<NetworkGraph>,
onion_messenger: Arc<OnionMessenger>, inbound_payments: PaymentInfoStorage,
outbound_payments: PaymentInfoStorage, ldk_data_dir: String, network: Network,
logger: Arc<disk::FilesystemLogger>,
onion_messenger: Arc<OnionMessenger>, inbound_payments: Arc<Mutex<PaymentInfoStorage>>,
outbound_payments: Arc<Mutex<PaymentInfoStorage>>, ldk_data_dir: String, network: Network,
logger: Arc<disk::FilesystemLogger>, persister: Arc<FilesystemPersister>,
) {
println!(
"LDK startup successful. Enter \"help\" to view available commands. Press Ctrl-D to quit."
Expand Down Expand Up @@ -157,7 +158,12 @@ pub(crate) async fn poll_for_user_input(
}
};

send_payment(&*channel_manager, &invoice, outbound_payments.clone());
send_payment(
&channel_manager,
&invoice,
&mut outbound_payments.lock().unwrap(),
persister.clone(),
);
}
"keysend" => {
let dest_pubkey = match words.next() {
Expand Down Expand Up @@ -188,11 +194,12 @@ pub(crate) async fn poll_for_user_input(
}
};
keysend(
&*channel_manager,
&channel_manager,
dest_pubkey,
amt_msat,
&*keys_manager,
outbound_payments.clone(),
&mut outbound_payments.lock().unwrap(),
persister.clone(),
);
}
"getinvoice" => {
Expand Down Expand Up @@ -220,15 +227,17 @@ pub(crate) async fn poll_for_user_input(
continue;
}

let mut inbound_payments = inbound_payments.lock().unwrap();
get_invoice(
amt_msat.unwrap(),
Arc::clone(&inbound_payments),
&*channel_manager,
&mut inbound_payments,
&channel_manager,
Arc::clone(&keys_manager),
network,
expiry_secs.unwrap(),
Arc::clone(&logger),
);
persister.persist(INBOUND_PAYMENTS_FNAME, &*inbound_payments).unwrap();
}
"connectpeer" => {
let peer_pubkey_and_ip_addr = words.next();
Expand Down Expand Up @@ -278,9 +287,10 @@ pub(crate) async fn poll_for_user_input(
}
}
"listchannels" => list_channels(&channel_manager, &network_graph),
"listpayments" => {
list_payments(inbound_payments.clone(), outbound_payments.clone())
}
"listpayments" => list_payments(
&inbound_payments.lock().unwrap(),
&outbound_payments.lock().unwrap(),
),
"closechannel" => {
let channel_id_str = words.next();
if channel_id_str.is_none() {
Expand Down Expand Up @@ -527,11 +537,9 @@ fn list_channels(channel_manager: &Arc<ChannelManager>, network_graph: &Arc<Netw
println!("]");
}

fn list_payments(inbound_payments: PaymentInfoStorage, outbound_payments: PaymentInfoStorage) {
let inbound = inbound_payments.lock().unwrap();
let outbound = outbound_payments.lock().unwrap();
fn list_payments(inbound_payments: &PaymentInfoStorage, outbound_payments: &PaymentInfoStorage) {
print!("[");
for (payment_hash, payment_info) in inbound.deref() {
for (payment_hash, payment_info) in &inbound_payments.payments {
println!("");
println!("\t{{");
println!("\t\tamount_millisatoshis: {},", payment_info.amt_msat);
Expand All @@ -549,7 +557,7 @@ fn list_payments(inbound_payments: PaymentInfoStorage, outbound_payments: Paymen
println!("\t}},");
}

for (payment_hash, payment_info) in outbound.deref() {
for (payment_hash, payment_info) in &outbound_payments.payments {
println!("");
println!("\t{{");
println!("\t\tamount_millisatoshis: {},", payment_info.amt_msat);
Expand Down Expand Up @@ -658,41 +666,40 @@ fn open_channel(
}

fn send_payment(
channel_manager: &ChannelManager, invoice: &Invoice, payment_storage: PaymentInfoStorage,
channel_manager: &ChannelManager, invoice: &Invoice,
outbound_payments: &mut PaymentInfoStorage, persister: Arc<FilesystemPersister>,
) {
let status =
match pay_invoice(invoice, Retry::Timeout(Duration::from_secs(10)), channel_manager) {
Ok(_payment_id) => {
let payee_pubkey = invoice.recover_payee_pub_key();
let amt_msat = invoice.amount_milli_satoshis().unwrap();
println!("EVENT: initiated sending {} msats to {}", amt_msat, payee_pubkey);
print!("> ");
HTLCStatus::Pending
}
Err(e) => {
println!("ERROR: failed to send payment: {:?}", e);
print!("> ");
HTLCStatus::Failed
}
};
let payment_hash = PaymentHash(invoice.payment_hash().clone().into_inner());
let payment_secret = Some(invoice.payment_secret().clone());

let mut payments = payment_storage.lock().unwrap();
payments.insert(
let payment_hash = PaymentHash((*invoice.payment_hash()).into_inner());
let payment_secret = Some(*invoice.payment_secret());
outbound_payments.payments.insert(
payment_hash,
PaymentInfo {
preimage: None,
secret: payment_secret,
status,
status: HTLCStatus::Pending,
amt_msat: MillisatAmount(invoice.amount_milli_satoshis()),
},
);
persister.persist(OUTBOUND_PAYMENTS_FNAME, &*outbound_payments).unwrap();
match pay_invoice(invoice, Retry::Timeout(Duration::from_secs(10)), channel_manager) {
Ok(_payment_id) => {
let payee_pubkey = invoice.recover_payee_pub_key();
let amt_msat = invoice.amount_milli_satoshis().unwrap();
println!("EVENT: initiated sending {} msats to {}", amt_msat, payee_pubkey);
print!("> ");
}
Err(e) => {
println!("ERROR: failed to send payment: {:?}", e);
print!("> ");
outbound_payments.payments.get_mut(&payment_hash).unwrap().status = HTLCStatus::Failed;
persister.persist(OUTBOUND_PAYMENTS_FNAME, &*outbound_payments).unwrap();
}
};
}

fn keysend<E: EntropySource>(
channel_manager: &ChannelManager, payee_pubkey: PublicKey, amt_msat: u64, entropy_source: &E,
payment_storage: PaymentInfoStorage,
outbound_payments: &mut PaymentInfoStorage, persister: Arc<FilesystemPersister>,
) {
let payment_preimage = PaymentPreimage(entropy_source.get_secure_random_bytes());
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).into_inner());
Expand All @@ -701,7 +708,17 @@ fn keysend<E: EntropySource>(
payment_params: PaymentParameters::for_keysend(payee_pubkey, 40),
final_value_msat: amt_msat,
};
let status = match channel_manager.send_spontaneous_payment_with_retry(
outbound_payments.payments.insert(
payment_hash,
PaymentInfo {
preimage: None,
secret: None,
status: HTLCStatus::Pending,
amt_msat: MillisatAmount(Some(amt_msat)),
},
);
persister.persist(OUTBOUND_PAYMENTS_FNAME, &*outbound_payments).unwrap();
match channel_manager.send_spontaneous_payment_with_retry(
Some(payment_preimage),
RecipientOnionFields::spontaneous_empty(),
PaymentId(payment_hash.0),
Expand All @@ -711,33 +728,21 @@ fn keysend<E: EntropySource>(
Ok(_payment_hash) => {
println!("EVENT: initiated sending {} msats to {}", amt_msat, payee_pubkey);
print!("> ");
HTLCStatus::Pending
}
Err(e) => {
println!("ERROR: failed to send payment: {:?}", e);
print!("> ");
HTLCStatus::Failed
outbound_payments.payments.get_mut(&payment_hash).unwrap().status = HTLCStatus::Failed;
persister.persist(OUTBOUND_PAYMENTS_FNAME, &*outbound_payments).unwrap();
}
};

let mut payments = payment_storage.lock().unwrap();
payments.insert(
payment_hash,
PaymentInfo {
preimage: None,
secret: None,
status,
amt_msat: MillisatAmount(Some(amt_msat)),
},
);
}

fn get_invoice(
amt_msat: u64, payment_storage: PaymentInfoStorage, channel_manager: &ChannelManager,
amt_msat: u64, inbound_payments: &mut PaymentInfoStorage, channel_manager: &ChannelManager,
keys_manager: Arc<KeysManager>, network: Network, expiry_secs: u32,
logger: Arc<disk::FilesystemLogger>,
) {
let mut payments = payment_storage.lock().unwrap();
let currency = match network {
Network::Bitcoin => Currency::Bitcoin,
Network::Testnet => Currency::BitcoinTestnet,
Expand Down Expand Up @@ -765,7 +770,7 @@ fn get_invoice(
};

let payment_hash = PaymentHash(invoice.payment_hash().clone().into_inner());
payments.insert(
inbound_payments.payments.insert(
payment_hash,
PaymentInfo {
preimage: None,
Expand Down
16 changes: 14 additions & 2 deletions src/disk.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use crate::{cli, NetworkGraph};
use crate::{cli, NetworkGraph, PaymentInfoStorage};
use bitcoin::secp256k1::PublicKey;
use bitcoin::Network;
use chrono::Utc;
use lightning::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringParameters};
use lightning::util::logger::{Logger, Record};
use lightning::util::ser::{ReadableArgs, Writer};
use lightning::util::ser::{Readable, ReadableArgs, Writer};
use std::collections::HashMap;
use std::fs;
use std::fs::File;
Expand All @@ -13,6 +13,9 @@ use std::net::SocketAddr;
use std::path::Path;
use std::sync::Arc;

pub(crate) const INBOUND_PAYMENTS_FNAME: &str = "inbound_payments";
pub(crate) const OUTBOUND_PAYMENTS_FNAME: &str = "outbound_payments";

pub(crate) struct FilesystemLogger {
data_dir: String,
}
Expand Down Expand Up @@ -83,6 +86,15 @@ pub(crate) fn read_network(
NetworkGraph::new(network, logger)
}

pub(crate) fn read_payment_info(path: &Path) -> PaymentInfoStorage {
if let Ok(file) = File::open(path) {
if let Ok(info) = PaymentInfoStorage::read(&mut BufReader::new(file)) {
return info;
}
}
PaymentInfoStorage { payments: HashMap::new() }
}

pub(crate) fn read_scorer(
path: &Path, graph: Arc<NetworkGraph>, logger: Arc<FilesystemLogger>,
) -> ProbabilisticScorer<Arc<NetworkGraph>, Arc<FilesystemLogger>> {
Expand Down
Loading