# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
-lightning = { version = "0.0.116", features = ["max_level_trace"] }
-lightning-block-sync = { version = "0.0.116", features = [ "rpc-client" ] }
-lightning-invoice = { version = "0.24.0" }
-lightning-net-tokio = { version = "0.0.116" }
-lightning-persister = { version = "0.0.116" }
-lightning-background-processor = { version = "0.0.116", features = [ "futures" ] }
-lightning-rapid-gossip-sync = { version = "0.0.116" }
+lightning = { version = "0.0.117", features = ["max_level_trace"] }
+lightning-block-sync = { version = "0.0.117", features = [ "rpc-client" ] }
+lightning-invoice = { version = "0.25.0" }
+lightning-net-tokio = { version = "0.0.117" }
+lightning-persister = { version = "0.0.117" }
+lightning-background-processor = { version = "0.0.117", features = [ "futures" ] }
+lightning-rapid-gossip-sync = { version = "0.0.117" }
base64 = "0.13.0"
bitcoin = "0.29.0"
use crate::cli::LdkUserInfo;
use bitcoin::network::constants::Network;
-use lightning::ln::msgs::NetAddress;
+use lightning::ln::msgs::SocketAddress;
use std::collections::HashMap;
use std::env;
use std::fs;
-use std::net::IpAddr;
use std::path::{Path, PathBuf};
use std::str::FromStr;
let mut ldk_announced_listen_addr = Vec::new();
loop {
match env::args().skip(arg_idx + 1).next().as_ref() {
- Some(s) => match IpAddr::from_str(s) {
- Ok(IpAddr::V4(a)) => {
- ldk_announced_listen_addr
- .push(NetAddress::IPv4 { addr: a.octets(), port: ldk_peer_listening_port });
+ Some(s) => match SocketAddress::from_str(s) {
+ Ok(sa) => {
+ ldk_announced_listen_addr.push(sa);
arg_idx += 1;
}
- Ok(IpAddr::V6(a)) => {
- ldk_announced_listen_addr
- .push(NetAddress::IPv6 { addr: a.octets(), port: ldk_peer_listening_port });
- arg_idx += 1;
- }
- Err(_) => panic!("Failed to parse announced-listen-addr into an IP address"),
+ Err(_) => panic!("Failed to parse announced-listen-addr into a socket address"),
},
None => break,
}
use bitcoin::network::constants::Network;
use bitcoin::secp256k1::PublicKey;
use lightning::ln::channelmanager::{PaymentId, RecipientOnionFields, Retry};
-use lightning::ln::msgs::NetAddress;
-use lightning::ln::{PaymentHash, PaymentPreimage};
+use lightning::ln::msgs::SocketAddress;
+use lightning::ln::{ChannelId, PaymentHash, PaymentPreimage};
use lightning::onion_message::OnionMessagePath;
use lightning::onion_message::{CustomOnionMessageContents, Destination, OnionMessageContents};
use lightning::routing::gossip::NodeId;
use lightning::routing::router::{PaymentParameters, RouteParameters};
use lightning::sign::{EntropySource, KeysManager};
use lightning::util::config::{ChannelHandshakeConfig, ChannelHandshakeLimits, UserConfig};
-use lightning::util::persist::KVStorePersister;
+use lightning::util::persist::KVStore;
use lightning::util::ser::{Writeable, Writer};
use lightning_invoice::payment::pay_invoice;
use lightning_invoice::{utils, Bolt11Invoice, Currency};
-use lightning_persister::FilesystemPersister;
+use lightning_persister::fs_store::FilesystemStore;
use std::env;
use std::io;
use std::io::Write;
pub(crate) bitcoind_rpc_host: String,
pub(crate) ldk_storage_dir_path: String,
pub(crate) ldk_peer_listening_port: u16,
- pub(crate) ldk_announced_listen_addr: Vec<NetAddress>,
+ pub(crate) ldk_announced_listen_addr: Vec<SocketAddress>,
pub(crate) ldk_announced_node_name: [u8; 32],
pub(crate) network: Network,
}
keys_manager: Arc<KeysManager>, network_graph: Arc<NetworkGraph>,
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>,
+ logger: Arc<disk::FilesystemLogger>, persister: Arc<FilesystemStore>,
) {
println!(
"LDK startup successful. Enter \"help\" to view available commands. Press Ctrl-D to quit."
expiry_secs.unwrap(),
Arc::clone(&logger),
);
- persister.persist(INBOUND_PAYMENTS_FNAME, &*inbound_payments).unwrap();
+ persister
+ .write("", "", INBOUND_PAYMENTS_FNAME, &inbound_payments.encode())
+ .unwrap();
}
"connectpeer" => {
let peer_pubkey_and_ip_addr = words.next();
for chan_info in channel_manager.list_channels() {
println!("");
println!("\t{{");
- println!("\t\tchannel_id: {},", hex_utils::hex_str(&chan_info.channel_id[..]));
+ println!("\t\tchannel_id: {},", chan_info.channel_id);
if let Some(funding_txo) = chan_info.funding_txo {
println!("\t\tfunding_txid: {},", funding_txo.txid);
}
}
println!("\t\tis_channel_ready: {},", chan_info.is_channel_ready);
println!("\t\tchannel_value_satoshis: {},", chan_info.channel_value_satoshis);
- println!("\t\tlocal_balance_msat: {},", chan_info.balance_msat);
+ println!("\t\toutbound_capacity_msat: {},", chan_info.outbound_capacity_msat);
if chan_info.is_usable {
println!("\t\tavailable_balance_for_send_msat: {},", chan_info.outbound_capacity_msat);
println!("\t\tavailable_balance_for_recv_msat: {},", chan_info.inbound_capacity_msat);
fn send_payment(
channel_manager: &ChannelManager, invoice: &Bolt11Invoice,
- outbound_payments: &mut PaymentInfoStorage, persister: Arc<FilesystemPersister>,
+ outbound_payments: &mut PaymentInfoStorage, persister: Arc<FilesystemStore>,
) {
let payment_hash = PaymentHash((*invoice.payment_hash()).into_inner());
let payment_secret = Some(*invoice.payment_secret());
amt_msat: MillisatAmount(invoice.amount_milli_satoshis()),
},
);
- persister.persist(OUTBOUND_PAYMENTS_FNAME, &*outbound_payments).unwrap();
+ persister.write("", "", OUTBOUND_PAYMENTS_FNAME, &outbound_payments.encode()).unwrap();
match pay_invoice(invoice, Retry::Timeout(Duration::from_secs(10)), channel_manager) {
Ok(_payment_id) => {
let payee_pubkey = invoice.recover_payee_pub_key();
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();
+ persister.write("", "", OUTBOUND_PAYMENTS_FNAME, &outbound_payments.encode()).unwrap();
}
};
}
fn keysend<E: EntropySource>(
channel_manager: &ChannelManager, payee_pubkey: PublicKey, amt_msat: u64, entropy_source: &E,
- outbound_payments: &mut PaymentInfoStorage, persister: Arc<FilesystemPersister>,
+ outbound_payments: &mut PaymentInfoStorage, persister: Arc<FilesystemStore>,
) {
let payment_preimage = PaymentPreimage(entropy_source.get_secure_random_bytes());
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).into_inner());
- let route_params = RouteParameters {
- payment_params: PaymentParameters::for_keysend(payee_pubkey, 40, false),
- final_value_msat: amt_msat,
- };
+ let route_params = RouteParameters::from_payment_params_and_value(
+ PaymentParameters::for_keysend(payee_pubkey, 40, false),
+ amt_msat,
+ );
outbound_payments.payments.insert(
payment_hash,
PaymentInfo {
amt_msat: MillisatAmount(Some(amt_msat)),
},
);
- persister.persist(OUTBOUND_PAYMENTS_FNAME, &*outbound_payments).unwrap();
+ persister.write("", "", OUTBOUND_PAYMENTS_FNAME, &outbound_payments.encode()).unwrap();
match channel_manager.send_spontaneous_payment_with_retry(
Some(payment_preimage),
RecipientOnionFields::spontaneous_empty(),
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();
+ persister.write("", "", OUTBOUND_PAYMENTS_FNAME, &outbound_payments.encode()).unwrap();
}
};
}
fn close_channel(
channel_id: [u8; 32], counterparty_node_id: PublicKey, channel_manager: Arc<ChannelManager>,
) {
- match channel_manager.close_channel(&channel_id, &counterparty_node_id) {
+ match channel_manager.close_channel(&ChannelId(channel_id), &counterparty_node_id) {
Ok(()) => println!("EVENT: initiating channel close"),
Err(e) => println!("ERROR: failed to close channel: {:?}", e),
}
fn force_close_channel(
channel_id: [u8; 32], counterparty_node_id: PublicKey, channel_manager: Arc<ChannelManager>,
) {
- match channel_manager.force_close_broadcasting_latest_txn(&channel_id, &counterparty_node_id) {
+ match channel_manager
+ .force_close_broadcasting_latest_txn(&ChannelId(channel_id), &counterparty_node_id)
+ {
Ok(()) => println!("EVENT: initiating channel force-close"),
Err(e) => println!("ERROR: failed to force-close channel: {:?}", e),
}
};
use lightning::ln::msgs::DecodeError;
use lightning::ln::peer_handler::{IgnoringMessageHandler, MessageHandler, SimpleArcPeerManager};
-use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
+use lightning::ln::{ChannelId, PaymentHash, PaymentPreimage, PaymentSecret};
use lightning::onion_message::{DefaultMessageRouter, SimpleArcOnionMessenger};
use lightning::routing::gossip;
use lightning::routing::gossip::{NodeId, P2PGossipSync};
use lightning::routing::scoring::ProbabilisticScoringFeeParameters;
use lightning::sign::{EntropySource, InMemorySigner, KeysManager, SpendableOutputDescriptor};
use lightning::util::config::UserConfig;
-use lightning::util::persist::KVStorePersister;
+use lightning::util::persist::{self, read_channel_monitors, KVStore};
use lightning::util::ser::{Readable, ReadableArgs, Writeable, Writer};
use lightning::{chain, impl_writeable_tlv_based, impl_writeable_tlv_based_enum};
use lightning_background_processor::{process_events_async, GossipSync};
use lightning_block_sync::SpvClient;
use lightning_block_sync::UnboundedCache;
use lightning_net_tokio::SocketDescriptor;
-use lightning_persister::FilesystemPersister;
+use lightning_persister::fs_store::FilesystemStore;
use rand::{thread_rng, Rng};
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::io::Write;
use std::path::Path;
use std::sync::atomic::{AtomicBool, Ordering};
-use std::sync::{Arc, Mutex};
+use std::sync::{Arc, Mutex, RwLock};
use std::time::{Duration, SystemTime};
pub(crate) const PENDING_SPENDABLE_OUTPUT_DIR: &'static str = "pending_spendable_outputs";
Arc<BitcoindClient>,
Arc<BitcoindClient>,
Arc<FilesystemLogger>,
- Arc<FilesystemPersister>,
+ Arc<FilesystemStore>,
>;
pub(crate) type PeerManager = SimpleArcPeerManager<
ChainMonitor,
BitcoindClient,
BitcoindClient,
- BitcoindClient,
+ Arc<BitcoindClient>,
FilesystemLogger,
>;
channel_manager: &Arc<ChannelManager>, bitcoind_client: &BitcoindClient,
network_graph: &NetworkGraph, keys_manager: &KeysManager,
bump_tx_event_handler: &BumpTxEventHandler, inbound_payments: Arc<Mutex<PaymentInfoStorage>>,
- outbound_payments: Arc<Mutex<PaymentInfoStorage>>, persister: &Arc<FilesystemPersister>,
+ outbound_payments: Arc<Mutex<PaymentInfoStorage>>, persister: &Arc<FilesystemStore>,
network: Network, event: Event,
) {
match event {
};
channel_manager.claim_funds(payment_preimage.unwrap());
}
- Event::PaymentClaimed { payment_hash, purpose, amount_msat, receiver_node_id: _ } => {
+ Event::PaymentClaimed {
+ payment_hash,
+ purpose,
+ amount_msat,
+ receiver_node_id: _,
+ htlcs: _,
+ sender_intended_total_msat: _,
+ } => {
println!(
"\nEVENT: claimed payment from payment hash {} of {} millisatoshis",
hex_utils::hex_str(&payment_hash.0),
});
}
}
- persister.persist(INBOUND_PAYMENTS_FNAME, &*inbound).unwrap();
+ persister.write("", "", INBOUND_PAYMENTS_FNAME, &inbound.encode()).unwrap();
}
Event::PaymentSent { payment_preimage, payment_hash, fee_paid_msat, .. } => {
let mut outbound = outbound_payments.lock().unwrap();
io::stdout().flush().unwrap();
}
}
- persister.persist(OUTBOUND_PAYMENTS_FNAME, &*outbound).unwrap();
+ persister.write("", "", OUTBOUND_PAYMENTS_FNAME, &outbound.encode()).unwrap();
}
Event::OpenChannelRequest {
ref temporary_channel_id, ref counterparty_node_id, ..
if let Err(e) = res {
print!(
"\nEVENT: Failed to accept inbound channel ({}) from {}: {:?}",
- hex_utils::hex_str(&temporary_channel_id[..]),
+ temporary_channel_id,
hex_utils::hex_str(&counterparty_node_id.serialize()),
e,
);
} else {
print!(
"\nEVENT: Accepted inbound channel ({}) from {}",
- hex_utils::hex_str(&temporary_channel_id[..]),
+ temporary_channel_id,
hex_utils::hex_str(&counterparty_node_id.serialize()),
);
}
let payment = outbound.payments.get_mut(&payment_hash).unwrap();
payment.status = HTLCStatus::Failed;
}
- persister.persist(OUTBOUND_PAYMENTS_FNAME, &*outbound).unwrap();
+ persister.write("", "", OUTBOUND_PAYMENTS_FNAME, &outbound.encode()).unwrap();
}
Event::PaymentForwarded {
prev_channel_id,
let nodes = read_only_network_graph.nodes();
let channels = channel_manager.list_channels();
- let node_str = |channel_id: &Option<[u8; 32]>| match channel_id {
+ let node_str = |channel_id: &Option<ChannelId>| match channel_id {
None => String::new(),
Some(channel_id) => match channels.iter().find(|c| c.channel_id == *channel_id) {
None => String::new(),
}
},
};
- let channel_str = |channel_id: &Option<[u8; 32]>| {
+ let channel_str = |channel_id: &Option<ChannelId>| {
channel_id
- .map(|channel_id| format!(" with channel {}", hex_utils::hex_str(&channel_id)))
+ .map(|channel_id| format!(" with channel {}", channel_id))
.unwrap_or_default()
};
let from_prev_str =
forwarding_channel_manager.process_pending_htlc_forwards();
});
}
- Event::SpendableOutputs { outputs } => {
+ Event::SpendableOutputs { outputs, channel_id: _ } => {
// SpendableOutputDescriptors, of which outputs is a vec of, are critical to keep track
// of! While a `StaticOutput` descriptor is just an output to a static, well-known key,
// other descriptors are not currently ever regenerated for you by LDK. Once we return
let key = hex_utils::hex_str(&keys_manager.get_secure_random_bytes());
// Note that if the type here changes our read code needs to change as well.
let output: SpendableOutputDescriptor = output;
- persister
- .persist(&format!("{}/{}", PENDING_SPENDABLE_OUTPUT_DIR, key), &output)
- .unwrap();
+ persister.write(PENDING_SPENDABLE_OUTPUT_DIR, "", &key, &output.encode()).unwrap();
}
}
Event::ChannelPending { channel_id, counterparty_node_id, .. } => {
println!(
"\nEVENT: Channel {} with peer {} is pending awaiting funding lock-in!",
- hex_utils::hex_str(&channel_id),
+ channel_id,
hex_utils::hex_str(&counterparty_node_id.serialize()),
);
print!("> ");
} => {
println!(
"\nEVENT: Channel {} with peer {} is ready to be used!",
- hex_utils::hex_str(channel_id),
+ channel_id,
hex_utils::hex_str(&counterparty_node_id.serialize()),
);
print!("> ");
io::stdout().flush().unwrap();
}
- Event::ChannelClosed { channel_id, reason, user_channel_id: _ } => {
+ Event::ChannelClosed {
+ channel_id,
+ reason,
+ user_channel_id: _,
+ counterparty_node_id,
+ channel_capacity_sats: _,
+ } => {
println!(
- "\nEVENT: Channel {} closed due to: {:?}",
- hex_utils::hex_str(&channel_id),
+ "\nEVENT: Channel {} with counterparty {} closed due to: {:?}",
+ channel_id,
+ counterparty_node_id.map(|id| format!("{}", id)).unwrap_or("".to_owned()),
reason
);
print!("> ");
let broadcaster = bitcoind_client.clone();
// Step 4: Initialize Persist
- let persister = Arc::new(FilesystemPersister::new(ldk_data_dir.clone()));
+ let persister = Arc::new(FilesystemStore::new(ldk_data_dir.clone().into()));
// Step 5: Initialize the ChainMonitor
let chain_monitor: Arc<ChainMonitor> = Arc::new(chainmonitor::ChainMonitor::new(
// Step 7: Read ChannelMonitor state from disk
let mut channelmonitors =
- persister.read_channelmonitors(keys_manager.clone(), keys_manager.clone()).unwrap();
+ read_channel_monitors(Arc::clone(&persister), keys_manager.clone(), keys_manager.clone())
+ .unwrap();
// Step 8: Poll for the best chain tip, which may be used by the channel manager & spv client
let polled_chain_tip = init::validate_best_block_header(bitcoind_client.as_ref())
Arc::new(disk::read_network(Path::new(&network_graph_path), args.network, logger.clone()));
let scorer_path = format!("{}/scorer", ldk_data_dir.clone());
- let scorer = Arc::new(Mutex::new(disk::read_scorer(
+ let scorer = Arc::new(RwLock::new(disk::read_scorer(
Path::new(&scorer_path),
Arc::clone(&network_graph),
Arc::clone(&logger),
let funding_outpoint = item.2;
assert_eq!(
chain_monitor.watch_channel(funding_outpoint, channel_monitor),
- ChannelMonitorUpdateStatus::Completed
+ Ok(ChannelMonitorUpdateStatus::Completed)
);
}
.into_iter()
.filter_map(|p| match p {
RecentPaymentDetails::Pending { payment_hash, .. } => Some(payment_hash),
- RecentPaymentDetails::Fulfilled { payment_hash } => payment_hash,
- RecentPaymentDetails::Abandoned { payment_hash } => Some(payment_hash),
+ RecentPaymentDetails::Fulfilled { payment_hash, .. } => payment_hash,
+ RecentPaymentDetails::Abandoned { payment_hash, .. } => Some(payment_hash),
+ RecentPaymentDetails::AwaitingInvoice { payment_id: _ } => todo!(),
})
.collect::<Vec<PaymentHash>>();
for (payment_hash, payment_info) in outbound_payments
payment_info.status = HTLCStatus::Failed;
}
}
- persister.persist(OUTBOUND_PAYMENTS_FNAME, &*outbound_payments.lock().unwrap()).unwrap();
+ persister
+ .write("", "", OUTBOUND_PAYMENTS_FNAME, &outbound_payments.lock().unwrap().encode())
+ .unwrap();
// Step 18: Handle LDK Events
let channel_manager_event_listener = Arc::clone(&channel_manager);
};
// Step 19: Persist ChannelManager and NetworkGraph
- let persister = Arc::new(FilesystemPersister::new(ldk_data_dir.clone()));
+ let persister = Arc::new(FilesystemStore::new(ldk_data_dir.clone().into()));
// Step 20: Background Processing
let (bp_exit, bp_exit_check) = tokio::sync::watch::channel(());
// Regularly reconnect to channel peers.
let connect_cm = Arc::clone(&channel_manager);
let connect_pm = Arc::clone(&peer_manager);
- let peer_data_path = format!("{}/channel_peer_data", ldk_data_dir.clone());
+ let peer_data_path = format!("{}/channel_peer_data", ldk_data_dir);
let stop_connect = Arc::clone(&stop_listen_connect);
tokio::spawn(async move {
let mut interval = tokio::time::interval(Duration::from_secs(1));
peer_manager.disconnect_all_peers();
if let Err(e) = bg_res {
- let persist_res = persister.persist("manager", &*channel_manager).unwrap();
+ let persist_res = persister
+ .write(
+ persist::CHANNEL_MANAGER_PERSISTENCE_PRIMARY_NAMESPACE,
+ persist::CHANNEL_MANAGER_PERSISTENCE_SECONDARY_NAMESPACE,
+ persist::CHANNEL_MANAGER_PERSISTENCE_KEY,
+ &channel_manager.encode(),
+ )
+ .unwrap();
use lightning::util::logger::Logger;
lightning::log_error!(
&*logger,
use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator};
use lightning::sign::{EntropySource, KeysManager, SpendableOutputDescriptor};
use lightning::util::logger::Logger;
-use lightning::util::persist::KVStorePersister;
-use lightning::util::ser::{Readable, WithoutLength};
+use lightning::util::persist::KVStore;
+use lightning::util::ser::{Readable, WithoutLength, Writeable};
+
+use lightning_persister::fs_store::FilesystemStore;
use bitcoin::secp256k1::Secp256k1;
use bitcoin::{LockTime, PackedLockTime};
use crate::BitcoindClient;
use crate::ChannelManager;
use crate::FilesystemLogger;
-use crate::FilesystemPersister;
/// If we have any pending claimable outputs, we should slowly sweep them to our Bitcoin Core
/// wallet. We technically don't need to do this - they're ours to spend when we want and can just
/// we don't do that here either.
pub(crate) async fn periodic_sweep(
ldk_data_dir: String, keys_manager: Arc<KeysManager>, logger: Arc<FilesystemLogger>,
- persister: Arc<FilesystemPersister>, bitcoind_client: Arc<BitcoindClient>,
+ persister: Arc<FilesystemStore>, bitcoind_client: Arc<BitcoindClient>,
channel_manager: Arc<ChannelManager>,
) {
// Regularly claim outputs which are exclusively spendable by us and send them to Bitcoin Core.
if !outputs.is_empty() {
let key = hex_utils::hex_str(&keys_manager.get_secure_random_bytes());
persister
- .persist(&format!("spendable_outputs/{}", key), &WithoutLength(&outputs))
+ .write("spendable_outputs", "", &key, &WithoutLength(&outputs).encode())
.unwrap();
fs::remove_dir_all(&processing_spendables_dir).unwrap();
}