Skip to content

Commit 2a93cdd

Browse files
committed
Add coinbase-derived fee estimation
1 parent 32f088a commit 2a93cdd

File tree

1 file changed

+80
-3
lines changed

1 file changed

+80
-3
lines changed

src/chain/cbf.rs

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
// http://opensource.org/licenses/MIT>, at your option. You may not use this file except in
66
// accordance with one or both of these licenses.
77

8+
use std::collections::HashMap;
89
use std::net::SocketAddr;
910
use std::sync::{Arc, Mutex, RwLock};
10-
use std::time::Duration;
11+
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
1112

1213
use bip157::{BlockHash, Builder, Client, Event, Info, Requester, TrustedPeer, Warning};
1314
use bitcoin::{Script, Transaction, Txid};
@@ -16,7 +17,10 @@ use lightning::util::ser::Writeable;
1617
use tokio::sync::mpsc;
1718

1819
use crate::config::{CbfSyncConfig, Config};
19-
use crate::fee_estimator::OnchainFeeEstimator;
20+
use crate::fee_estimator::{
21+
apply_post_estimation_adjustments, get_all_conf_targets, OnchainFeeEstimator,
22+
};
23+
use crate::io::utils::write_node_metrics;
2024
use crate::logger::{log_bytes, log_debug, log_error, log_info, log_trace, LdkLogger, Logger};
2125
use crate::runtime::Runtime;
2226
use crate::types::{ChainMonitor, ChannelManager, DynStore, Sweeper, Wallet};
@@ -226,8 +230,69 @@ impl CbfChainSource {
226230
}
227231

228232
/// Estimate fee rates from recent block data.
233+
// NOTE: This is a single-block fee estimation. A multi-block lookback with
234+
// per-target percentile selection is added later.
229235
pub(crate) async fn update_fee_rate_estimates(&self) -> Result<(), Error> {
230-
log_error!(self.logger, "Fee rate estimation via CBF is not yet implemented.");
236+
let requester = self.requester()?;
237+
238+
let tip_hash = match *self.latest_tip.lock().unwrap() {
239+
Some(hash) => hash,
240+
None => {
241+
log_debug!(self.logger, "No tip available yet for fee rate estimation, skipping.");
242+
return Ok(());
243+
},
244+
};
245+
246+
let now = Instant::now();
247+
248+
let base_fee_rate = tokio::time::timeout(
249+
Duration::from_secs(
250+
self.sync_config.timeouts_config.fee_rate_cache_update_timeout_secs,
251+
),
252+
requester.average_fee_rate(tip_hash),
253+
)
254+
.await
255+
.map_err(|e| {
256+
log_error!(self.logger, "Updating fee rate estimates timed out: {}", e);
257+
Error::FeerateEstimationUpdateTimeout
258+
})?
259+
.map_err(|e| {
260+
log_error!(self.logger, "Failed to retrieve fee rate estimate: {:?}", e);
261+
Error::FeerateEstimationUpdateFailed
262+
})?;
263+
264+
let confirmation_targets = get_all_conf_targets();
265+
let mut new_fee_rate_cache = HashMap::with_capacity(confirmation_targets.len());
266+
267+
for target in confirmation_targets {
268+
let adjusted_fee_rate = apply_post_estimation_adjustments(target, base_fee_rate);
269+
new_fee_rate_cache.insert(target, adjusted_fee_rate);
270+
271+
log_trace!(
272+
self.logger,
273+
"Fee rate estimation updated for {:?}: {} sats/kwu",
274+
target,
275+
adjusted_fee_rate.to_sat_per_kwu(),
276+
);
277+
}
278+
279+
self.fee_estimator.set_fee_rate_cache(new_fee_rate_cache);
280+
281+
log_debug!(
282+
self.logger,
283+
"Fee rate cache update finished in {}ms.",
284+
now.elapsed().as_millis()
285+
);
286+
287+
update_node_metrics_timestamp(
288+
&self.node_metrics,
289+
&*self.kv_store,
290+
&*self.logger,
291+
|m, t| {
292+
m.latest_fee_rate_cache_update_timestamp = t;
293+
},
294+
)?;
295+
231296
Ok(())
232297
}
233298

@@ -293,3 +358,15 @@ impl CbfChainSource {
293358
log_error!(self.logger, "CBF register_output is not yet implemented.");
294359
}
295360
}
361+
362+
/// Record the current timestamp in a `NodeMetrics` field and persist the metrics.
363+
fn update_node_metrics_timestamp(
364+
node_metrics: &RwLock<NodeMetrics>, kv_store: &DynStore, logger: &Logger,
365+
setter: impl FnOnce(&mut NodeMetrics, Option<u64>),
366+
) -> Result<(), Error> {
367+
let unix_time_secs_opt = SystemTime::now().duration_since(UNIX_EPOCH).ok().map(|d| d.as_secs());
368+
let mut locked = node_metrics.write().unwrap();
369+
setter(&mut locked, unix_time_secs_opt);
370+
write_node_metrics(&*locked, kv_store, logger)?;
371+
Ok(())
372+
}

0 commit comments

Comments
 (0)