From: Elias Rohrer Date: Fri, 7 Jul 2023 12:44:07 +0000 (+0200) Subject: Upgrade to LDK 0.0.116 X-Git-Url: http://git.bitcoin.ninja/index.cgi?p=ldk-sample;a=commitdiff_plain;h=8098135a88a92f6ce50747cddea97b13c8296593 Upgrade to LDK 0.0.116 --- diff --git a/Cargo.lock b/Cargo.lock index 86335ed..cb154ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -257,18 +257,18 @@ checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "lightning" -version = "0.0.115" +version = "0.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e009e1c0c21f66378b491bb40f548682138c63e09db6f3a05af59f8804bb9f4a" +checksum = "90a0f2155316f1570446a0447c993480673f840748c8ed25bbc59dfc442ac770" dependencies = [ "bitcoin", ] [[package]] name = "lightning-background-processor" -version = "0.0.115" +version = "0.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "721b05b9848a09d5b943915449b5ffb31e24708007763640cf9d79b124a17e19" +checksum = "398b68a96cceb3c1227504bd5faeb74f26c3233447bc10cc1cb2c67e01b51556" dependencies = [ "bitcoin", "lightning", @@ -277,9 +277,9 @@ dependencies = [ [[package]] name = "lightning-block-sync" -version = "0.0.115" +version = "0.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c60cf241b3c219ee865aad91eab85a879b23c1756a335a5a311790ad6c1c3d2" +checksum = "d94c276dbe2a777d58ed6ececca96006247a4717c00ac4cdfff62d76852be783" dependencies = [ "bitcoin", "chunked_transfer", @@ -289,9 +289,9 @@ dependencies = [ [[package]] name = "lightning-invoice" -version = "0.23.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4e44b0e2822c8811470137d2339fdfe67a699b3248bb1606d1d02eb6a1e9f0a" +checksum = "1788c0158526ec27a502043c2911ea6ea58fdc656bdf8749484942c07b790d23" dependencies = [ "bech32 0.9.1", "bitcoin", @@ -303,9 +303,9 @@ dependencies = [ [[package]] name = "lightning-net-tokio" -version = "0.0.115" +version = "0.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4561ec5d4df2dd410a8b80955791fcfb007ef9210395db6e914b9527397b868c" +checksum = "366c0ae225736cbc03555bd5fb4b44b2e8fe2ca3c868ec53a4b325c38b2ab2bd" dependencies = [ "bitcoin", "lightning", @@ -314,9 +314,9 @@ dependencies = [ [[package]] name = "lightning-persister" -version = "0.0.115" +version = "0.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c52ed57ec33fb945f464b7e91b5df49f49fec649e1b44909f3ce517e96b0449a" +checksum = "93caaafeb42115b70119619c2420e362cce776670427fc4ced3e6df77b41c0b6" dependencies = [ "bitcoin", "libc", @@ -326,9 +326,9 @@ dependencies = [ [[package]] name = "lightning-rapid-gossip-sync" -version = "0.0.115" +version = "0.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd84d74a9b3892db22a60ac11dfc12e76b257b3174db6743e818ecc24834f3be" +checksum = "8a07af5814234924e623bca499e003fca1864024d5bd984e752230f73a131584" dependencies = [ "bitcoin", "lightning", diff --git a/Cargo.toml b/Cargo.toml index aa842bf..0e95be6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,13 +8,13 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -lightning = { version = "0.0.115", features = ["max_level_trace"] } -lightning-block-sync = { version = "0.0.115", features = [ "rpc-client" ] } -lightning-invoice = { version = "0.23" } -lightning-net-tokio = { version = "0.0.115" } -lightning-persister = { version = "0.0.115" } -lightning-background-processor = { version = "0.0.115", features = [ "futures" ] } -lightning-rapid-gossip-sync = { version = "0.0.115" } +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" } base64 = "0.13.0" bitcoin = "0.29.0" diff --git a/src/bitcoind_client.rs b/src/bitcoind_client.rs index e7a3cec..228f27a 100644 --- a/src/bitcoind_client.rs +++ b/src/bitcoind_client.rs @@ -1,4 +1,6 @@ -use crate::convert::{BlockchainInfo, FeeResponse, FundedTx, NewAddress, RawTx, SignedTx}; +use crate::convert::{ + BlockchainInfo, FeeResponse, FundedTx, MempoolMinFeeResponse, NewAddress, RawTx, SignedTx, +}; use crate::disk::FilesystemLogger; use base64; use bitcoin::blockdata::transaction::Transaction; @@ -32,6 +34,7 @@ pub struct BitcoindClient { #[derive(Clone, Eq, Hash, PartialEq)] pub enum Target { + MempoolMinimum, Background, Normal, HighPriority, @@ -75,6 +78,7 @@ impl BitcoindClient { "Failed to make initial call to bitcoind - please check your RPC user/password and access settings") })?; let mut fees: HashMap = HashMap::new(); + fees.insert(Target::MempoolMinimum, AtomicU32::new(MIN_FEERATE)); fees.insert(Target::Background, AtomicU32::new(MIN_FEERATE)); fees.insert(Target::Normal, AtomicU32::new(2000)); fees.insert(Target::HighPriority, AtomicU32::new(5000)); @@ -102,6 +106,16 @@ impl BitcoindClient { ) { handle.spawn(async move { loop { + let mempoolmin_estimate = { + let resp = rpc_client + .call_method::("getmempoolinfo", &vec![]) + .await + .unwrap(); + match resp.feerate_sat_per_kw { + Some(feerate) => std::cmp::max(feerate, MIN_FEERATE), + None => MIN_FEERATE, + } + }; let background_estimate = { let background_conf_target = serde_json::json!(144); let background_estimate_mode = serde_json::json!("ECONOMICAL"); @@ -151,6 +165,9 @@ impl BitcoindClient { } }; + fees.get(&Target::MempoolMinimum) + .unwrap() + .store(mempoolmin_estimate, Ordering::Release); fees.get(&Target::Background) .unwrap() .store(background_estimate, Ordering::Release); @@ -238,6 +255,9 @@ impl BitcoindClient { impl FeeEstimator for BitcoindClient { fn get_est_sat_per_1000_weight(&self, confirmation_target: ConfirmationTarget) -> u32 { match confirmation_target { + ConfirmationTarget::MempoolMinimum => { + self.fees.get(&Target::MempoolMinimum).unwrap().load(Ordering::Acquire) + } ConfirmationTarget::Background => { self.fees.get(&Target::Background).unwrap().load(Ordering::Acquire) } @@ -252,29 +272,33 @@ impl FeeEstimator for BitcoindClient { } impl BroadcasterInterface for BitcoindClient { - fn broadcast_transaction(&self, tx: &Transaction) { - let bitcoind_rpc_client = self.bitcoind_rpc_client.clone(); - let tx_serialized = encode::serialize_hex(tx); - let tx_json = serde_json::json!(tx_serialized); - let logger = Arc::clone(&self.logger); - self.handle.spawn(async move { - // This may error due to RL calling `broadcast_transaction` with the same transaction - // multiple times, but the error is safe to ignore. - match bitcoind_rpc_client - .call_method::("sendrawtransaction", &vec![tx_json]) - .await - { - Ok(_) => {} - Err(e) => { - let err_str = e.get_ref().unwrap().to_string(); - log_error!(logger, - "Warning, failed to broadcast a transaction, this is likely okay but may indicate an error: {}\nTransaction: {}", - err_str, - tx_serialized); - print!("Warning, failed to broadcast a transaction, this is likely okay but may indicate an error: {}\n> ", err_str); - } - } - }); + fn broadcast_transactions(&self, txs: &[&Transaction]) { + // TODO: Rather than calling `sendrawtransaction` in a a loop, we should probably use + // `submitpackage` once it becomes available. + for tx in txs { + let bitcoind_rpc_client = Arc::clone(&self.bitcoind_rpc_client); + let tx_serialized = encode::serialize_hex(tx); + let tx_json = serde_json::json!(tx_serialized); + let logger = Arc::clone(&self.logger); + self.handle.spawn(async move { + // This may error due to RL calling `broadcast_transactions` with the same transaction + // multiple times, but the error is safe to ignore. + match bitcoind_rpc_client + .call_method::("sendrawtransaction", &vec![tx_json]) + .await + { + Ok(_) => {} + Err(e) => { + let err_str = e.get_ref().unwrap().to_string(); + log_error!(logger, + "Warning, failed to broadcast a transaction, this is likely okay but may indicate an error: {}\nTransaction: {}", + err_str, + tx_serialized); + print!("Warning, failed to broadcast a transaction, this is likely okay but may indicate an error: {}\n> ", err_str); + } + } + }); + } } } diff --git a/src/cli.rs b/src/cli.rs index f896721..8e96fd8 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -8,18 +8,19 @@ use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hashes::Hash; use bitcoin::network::constants::Network; use bitcoin::secp256k1::PublicKey; -use lightning::chain::keysinterface::{EntropySource, KeysManager}; use lightning::ln::channelmanager::{PaymentId, RecipientOnionFields, Retry}; use lightning::ln::msgs::NetAddress; use lightning::ln::{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::ser::{Writeable, Writer}; use lightning_invoice::payment::pay_invoice; -use lightning_invoice::{utils, Currency, Invoice}; +use lightning_invoice::{utils, Bolt11Invoice, Currency}; use lightning_persister::FilesystemPersister; use std::env; use std::io; @@ -150,7 +151,7 @@ pub(crate) async fn poll_for_user_input( continue; } - let invoice = match Invoice::from_str(invoice_str.unwrap()) { + let invoice = match Bolt11Invoice::from_str(invoice_str.unwrap()) { Ok(inv) => inv, Err(e) => { println!("ERROR: invalid invoice: {:?}", e); @@ -387,7 +388,7 @@ pub(crate) async fn poll_for_user_input( ); continue; } - let mut node_pks = Vec::new(); + let mut intermediate_nodes = Vec::new(); let mut errored = false; for pk_str in path_pks_str.unwrap().split(",") { let node_pubkey_vec = match hex_utils::to_vec(pk_str) { @@ -406,7 +407,7 @@ pub(crate) async fn poll_for_user_input( break; } }; - node_pks.push(node_pubkey); + intermediate_nodes.push(node_pubkey); } if errored { continue; @@ -425,10 +426,10 @@ pub(crate) async fn poll_for_user_input( continue; } }; - let destination_pk = node_pks.pop().unwrap(); + let destination = Destination::Node(intermediate_nodes.pop().unwrap()); + let message_path = OnionMessagePath { intermediate_nodes, destination }; match onion_messenger.send_onion_message( - &node_pks, - Destination::Node(destination_pk), + message_path, OnionMessageContents::Custom(UserOnionMessageContents { tlv_type, data }), None, ) { @@ -666,7 +667,7 @@ fn open_channel( } fn send_payment( - channel_manager: &ChannelManager, invoice: &Invoice, + channel_manager: &ChannelManager, invoice: &Bolt11Invoice, outbound_payments: &mut PaymentInfoStorage, persister: Arc, ) { let payment_hash = PaymentHash((*invoice.payment_hash()).into_inner()); @@ -705,7 +706,7 @@ fn keysend( let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).into_inner()); let route_params = RouteParameters { - payment_params: PaymentParameters::for_keysend(payee_pubkey, 40), + payment_params: PaymentParameters::for_keysend(payee_pubkey, 40, false), final_value_msat: amt_msat, }; outbound_payments.payments.insert( diff --git a/src/convert.rs b/src/convert.rs index 6403923..84b033e 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -74,6 +74,31 @@ impl TryInto for JsonResponse { } } +pub struct MempoolMinFeeResponse { + pub feerate_sat_per_kw: Option, + pub errored: bool, +} + +impl TryInto for JsonResponse { + type Error = std::io::Error; + fn try_into(self) -> std::io::Result { + let errored = !self.0["errors"].is_null(); + assert_eq!(self.0["maxmempool"].as_u64(), Some(300000000)); + Ok(MempoolMinFeeResponse { + errored, + feerate_sat_per_kw: match self.0["mempoolminfee"].as_f64() { + // Bitcoin Core gives us a feerate in BTC/KvB, which we need to convert to + // satoshis/KW. Thus, we first multiply by 10^8 to get satoshis, then divide by 4 + // to convert virtual-bytes into weight units. + Some(feerate_btc_per_kvbyte) => { + Some((feerate_btc_per_kvbyte * 100_000_000.0 / 4.0).round() as u32) + } + None => None, + }, + }) + } +} + pub struct BlockchainInfo { pub latest_height: usize, pub latest_blockhash: BlockHash, diff --git a/src/disk.rs b/src/disk.rs index 77cefd6..a69fdef 100644 --- a/src/disk.rs +++ b/src/disk.rs @@ -2,7 +2,7 @@ use crate::{cli, NetworkGraph, PaymentInfoStorage}; use bitcoin::secp256k1::PublicKey; use bitcoin::Network; use chrono::Utc; -use lightning::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringParameters}; +use lightning::routing::scoring::{ProbabilisticScorer, ProbabilisticScoringDecayParameters}; use lightning::util::logger::{Logger, Record}; use lightning::util::ser::{Readable, ReadableArgs, Writer}; use std::collections::HashMap; @@ -98,7 +98,7 @@ pub(crate) fn read_payment_info(path: &Path) -> PaymentInfoStorage { pub(crate) fn read_scorer( path: &Path, graph: Arc, logger: Arc, ) -> ProbabilisticScorer, Arc> { - let params = ProbabilisticScoringParameters::default(); + let params = ProbabilisticScoringDecayParameters::default(); if let Ok(file) = File::open(path) { let args = (params.clone(), Arc::clone(&graph), Arc::clone(&logger)); if let Ok(scorer) = ProbabilisticScorer::read(&mut BufReader::new(file), args) { diff --git a/src/main.rs b/src/main.rs index a526efc..2012921 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,9 +14,6 @@ use bitcoin::network::constants::Network; use bitcoin::BlockHash; use bitcoin_bech32::WitnessProgram; use disk::{INBOUND_PAYMENTS_FNAME, OUTBOUND_PAYMENTS_FNAME}; -use lightning::chain::keysinterface::{ - EntropySource, InMemorySigner, KeysManager, SpendableOutputDescriptor, -}; use lightning::chain::{chainmonitor, ChannelMonitorUpdateStatus}; use lightning::chain::{Filter, Watch}; use lightning::events::{Event, PaymentFailureReason, PaymentPurpose}; @@ -27,10 +24,12 @@ use lightning::ln::channelmanager::{ use lightning::ln::msgs::DecodeError; use lightning::ln::peer_handler::{IgnoringMessageHandler, MessageHandler, SimpleArcPeerManager}; use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret}; -use lightning::onion_message::SimpleArcOnionMessenger; +use lightning::onion_message::{DefaultMessageRouter, SimpleArcOnionMessenger}; use lightning::routing::gossip; use lightning::routing::gossip::{NodeId, P2PGossipSync}; use lightning::routing::router::DefaultRouter; +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::ser::{Readable, ReadableArgs, Writeable, Writer}; @@ -207,6 +206,7 @@ async fn handle_ldk_events( via_user_channel_id: _, claim_deadline: _, onion_fields: _, + counterparty_skimmed_fee_msat: _, } => { println!( "\nEVENT: received payment from payment hash {} of {} millisatoshis", @@ -429,6 +429,7 @@ async fn handle_ldk_events( // the funding transaction either confirms, or this event is generated. } Event::HTLCIntercepted { .. } => {} + Event::BumpTransaction(_) => {} } } @@ -553,11 +554,13 @@ async fn start_ldk() { ))); // Step 10: Create Router + let scoring_fee_params = ProbabilisticScoringFeeParameters::default(); let router = Arc::new(DefaultRouter::new( network_graph.clone(), logger.clone(), keys_manager.get_secure_random_bytes(), scorer.clone(), + scoring_fee_params, )); // Step 11: Initialize the ChannelManager @@ -602,6 +605,7 @@ async fn start_ldk() { keys_manager.clone(), user_config, chain_params, + cur.as_secs() as u32, ); (polled_best_block_hash, fresh_channel_manager) } @@ -667,6 +671,8 @@ async fn start_ldk() { Arc::clone(&keys_manager), Arc::clone(&keys_manager), Arc::clone(&logger), + Arc::new(DefaultMessageRouter {}), + IgnoringMessageHandler {}, IgnoringMessageHandler {}, )); let mut ephemeral_bytes = [0; 32]; @@ -676,13 +682,13 @@ async fn start_ldk() { chan_handler: channel_manager.clone(), route_handler: gossip_sync.clone(), onion_message_handler: onion_messenger.clone(), + custom_message_handler: IgnoringMessageHandler {}, }; let peer_manager: Arc = Arc::new(PeerManager::new( lightning_msg_handler, current_time.try_into().unwrap(), &ephemeral_bytes, logger.clone(), - IgnoringMessageHandler {}, Arc::clone(&keys_manager), )); @@ -888,6 +894,7 @@ async fn start_ldk() { Arc::clone(&logger), Arc::clone(&persister), Arc::clone(&bitcoind_client), + Arc::clone(&channel_manager), )); // Start the CLI. diff --git a/src/sweep.rs b/src/sweep.rs index 2a168a7..a9ad6bd 100644 --- a/src/sweep.rs +++ b/src/sweep.rs @@ -5,15 +5,17 @@ use std::time::Duration; use std::{fs, io}; use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator}; -use lightning::chain::keysinterface::{EntropySource, KeysManager, SpendableOutputDescriptor}; +use lightning::sign::{EntropySource, KeysManager, SpendableOutputDescriptor}; use lightning::util::logger::Logger; use lightning::util::persist::KVStorePersister; use lightning::util::ser::{Readable, WithoutLength}; use bitcoin::secp256k1::Secp256k1; +use bitcoin::{LockTime, PackedLockTime}; use crate::hex_utils; use crate::BitcoindClient; +use crate::ChannelManager; use crate::FilesystemLogger; use crate::FilesystemPersister; @@ -28,6 +30,7 @@ use crate::FilesystemPersister; pub(crate) async fn periodic_sweep( ldk_data_dir: String, keys_manager: Arc, logger: Arc, persister: Arc, bitcoind_client: Arc, + channel_manager: Arc, ) { // Regularly claim outputs which are exclusively spendable by us and send them to Bitcoin Core. // Note that if you more tightly integrate your wallet with LDK you may not need to do this - @@ -106,16 +109,23 @@ pub(crate) async fn periodic_sweep( let output_descriptors = &outputs.iter().map(|a| a).collect::>(); let tx_feerate = bitcoind_client.get_est_sat_per_1000_weight(ConfirmationTarget::Background); + + // We set nLockTime to the current height to discourage fee sniping. + let cur_height = channel_manager.current_best_block().height(); + let locktime: PackedLockTime = + LockTime::from_height(cur_height).map_or(PackedLockTime::ZERO, |l| l.into()); + if let Ok(spending_tx) = keys_manager.spend_spendable_outputs( output_descriptors, Vec::new(), destination_address.script_pubkey(), tx_feerate, + Some(locktime), &Secp256k1::new(), ) { // Note that, most likely, we've already sweeped this set of outputs // and they're already confirmed on-chain, so this broadcast will fail. - bitcoind_client.broadcast_transaction(&spending_tx); + bitcoind_client.broadcast_transactions(&[&spending_tx]); } else { lightning::log_error!( logger,