use lightning::ln::msgs::{ChannelMessageHandler, Init};
use lightning::ln::peer_handler::{PeerManager, MessageHandler, SocketDescriptor, IgnoringMessageHandler};
use lightning::routing::gossip::{NetworkGraph, NodeId, P2PGossipSync};
- use lightning::routing::router::{DefaultRouter, Path, RouteHop};
use lightning::routing::scoring::{ChannelUsage, ScoreUpdate, ScoreLookUp, LockableScore};
+ use lightning::routing::router::{DefaultRouter, Path, RouteHop, CandidateRouteHop};
use lightning::util::config::UserConfig;
use lightning::util::ser::Writeable;
use lightning::util::test_utils;
impl ScoreLookUp for TestScorer {
type ScoreParams = ();
fn channel_penalty_msat(
- &self, _short_channel_id: u64, _source: &NodeId, _target: &NodeId, _usage: ChannelUsage, _score_params: &Self::ScoreParams
+ &self, _candidate: &CandidateRouteHop, _usage: ChannelUsage, _score_params: &Self::ScoreParams
) -> u64 { unimplemented!(); }
}
pub fn rebroadcast_pending_claims(&self) {
let monitors = self.monitors.read().unwrap();
for (_, monitor_holder) in &*monitors {
- let logger = WithChannelMonitor::from(&self.logger, &monitor_holder.monitor);
monitor_holder.monitor.rebroadcast_pending_claims(
- &*self.broadcaster, &*self.fee_estimator, &logger
+ &*self.broadcaster, &*self.fee_estimator, &self.logger
)
}
}
fn filtered_block_connected(&self, header: &Header, txdata: &TransactionData, height: u32) {
log_debug!(self.logger, "New best block {} at height {} provided via block_connected", header.block_hash(), height);
self.process_chain_data(header, Some(height), &txdata, |monitor, txdata| {
- let logger = WithChannelMonitor::from(&self.logger, &monitor);
monitor.block_connected(
- header, txdata, height, &*self.broadcaster, &*self.fee_estimator, &logger)
+ header, txdata, height, &*self.broadcaster, &*self.fee_estimator, &self.logger)
});
}
let monitor_states = self.monitors.read().unwrap();
log_debug!(self.logger, "Latest block {} at height {} removed via block_disconnected", header.block_hash(), height);
for monitor_state in monitor_states.values() {
- let logger = WithChannelMonitor::from(&self.logger, &monitor_state.monitor);
monitor_state.monitor.block_disconnected(
- header, height, &*self.broadcaster, &*self.fee_estimator, &logger);
+ header, height, &*self.broadcaster, &*self.fee_estimator, &self.logger);
}
}
}
fn transactions_confirmed(&self, header: &Header, txdata: &TransactionData, height: u32) {
log_debug!(self.logger, "{} provided transactions confirmed at height {} in block {}", txdata.len(), height, header.block_hash());
self.process_chain_data(header, None, txdata, |monitor, txdata| {
- let logger = WithChannelMonitor::from(&self.logger, &monitor);
monitor.transactions_confirmed(
- header, txdata, height, &*self.broadcaster, &*self.fee_estimator, &logger)
+ header, txdata, height, &*self.broadcaster, &*self.fee_estimator, &self.logger)
});
}
log_debug!(self.logger, "Transaction {} reorganized out of chain", txid);
let monitor_states = self.monitors.read().unwrap();
for monitor_state in monitor_states.values() {
- let logger = WithChannelMonitor::from(&self.logger, &monitor_state.monitor);
- monitor_state.monitor.transaction_unconfirmed(txid, &*self.broadcaster, &*self.fee_estimator, &logger);
+ monitor_state.monitor.transaction_unconfirmed(txid, &*self.broadcaster, &*self.fee_estimator, &self.logger);
}
}
fn best_block_updated(&self, header: &Header, height: u32) {
log_debug!(self.logger, "New best block {} at height {} provided via best_block_updated", header.block_hash(), height);
self.process_chain_data(header, Some(height), &[], |monitor, txdata| {
- let logger = WithChannelMonitor::from(&self.logger, &monitor);
// While in practice there shouldn't be any recursive calls when given empty txdata,
// it's still possible if a chain::Filter implementation returns a transaction.
debug_assert!(txdata.is_empty());
monitor.best_block_updated(
- header, height, &*self.broadcaster, &*self.fee_estimator, &logger)
+ header, height, &*self.broadcaster, &*self.fee_estimator, &self.logger
+ )
});
}
fn update_channel(&self, funding_txo: OutPoint, update: &ChannelMonitorUpdate) -> ChannelMonitorUpdateStatus {
// Update the monitor that watches the channel referred to by the given outpoint.
- let monitors_lock = self.monitors.read().unwrap();
- let monitors = monitors_lock.deref();
+ let monitors = self.monitors.read().unwrap();
match monitors.get(&funding_txo) {
None => {
log_error!(self.logger, "Failed to update channel monitor: no such monitor registered");
ChannelMonitorUpdateStatus::UnrecoverableError => {
// Take the monitors lock for writing so that we poison it and any future
// operations going forward fail immediately.
+ core::mem::drop(pending_monitor_updates);
core::mem::drop(monitors);
let _poison = self.monitors.write().unwrap();
let err_str = "ChannelMonitor[Update] persistence failed unrecoverably. This indicates we cannot continue normal operation and must shut down.";
}
}
-impl<'a, 'b, L: Deref> WithChannelMonitor<'a, L> where L::Target: Logger {
- pub(crate) fn from<S: WriteableEcdsaChannelSigner>(logger: &'a L, monitor: &'b ChannelMonitor<S>) -> Self {
+impl<'a, L: Deref> WithChannelMonitor<'a, L> where L::Target: Logger {
+ pub(crate) fn from<S: WriteableEcdsaChannelSigner>(logger: &'a L, monitor: &ChannelMonitor<S>) -> Self {
+ Self::from_impl(logger, &*monitor.inner.lock().unwrap())
+ }
+
+ pub(crate) fn from_impl<S: WriteableEcdsaChannelSigner>(logger: &'a L, monitor_impl: &ChannelMonitorImpl<S>) -> Self {
+ let peer_id = monitor_impl.counterparty_node_id;
+ let channel_id = Some(monitor_impl.funding_info.0.to_channel_id());
WithChannelMonitor {
- logger,
- peer_id: monitor.get_counterparty_node_id(),
- channel_id: Some(monitor.get_funding_txo().0.to_channel_id()),
+ logger, peer_id, channel_id,
}
}
}
)
where L::Target: Logger
{
- self.inner.lock().unwrap().provide_initial_counterparty_commitment_tx(txid,
+ let mut inner = self.inner.lock().unwrap();
+ let logger = WithChannelMonitor::from_impl(logger, &*inner);
+ inner.provide_initial_counterparty_commitment_tx(txid,
htlc_outputs, commitment_number, their_cur_per_commitment_point, feerate_per_kw,
- to_broadcaster_value_sat, to_countersignatory_value_sat, logger);
+ to_broadcaster_value_sat, to_countersignatory_value_sat, &logger);
}
/// Informs this monitor of the latest counterparty (ie non-broadcastable) commitment transaction.
their_per_commitment_point: PublicKey,
logger: &L,
) where L::Target: Logger {
- self.inner.lock().unwrap().provide_latest_counterparty_commitment_tx(
- txid, htlc_outputs, commitment_number, their_per_commitment_point, logger)
+ let mut inner = self.inner.lock().unwrap();
+ let logger = WithChannelMonitor::from_impl(logger, &*inner);
+ inner.provide_latest_counterparty_commitment_tx(
+ txid, htlc_outputs, commitment_number, their_per_commitment_point, &logger)
}
#[cfg(test)]
F::Target: FeeEstimator,
L::Target: Logger,
{
- self.inner.lock().unwrap().provide_payment_preimage(
- payment_hash, payment_preimage, broadcaster, fee_estimator, logger)
+ let mut inner = self.inner.lock().unwrap();
+ let logger = WithChannelMonitor::from_impl(logger, &*inner);
+ inner.provide_payment_preimage(
+ payment_hash, payment_preimage, broadcaster, fee_estimator, &logger)
}
/// Updates a ChannelMonitor on the basis of some new information provided by the Channel
F::Target: FeeEstimator,
L::Target: Logger,
{
- self.inner.lock().unwrap().update_monitor(updates, broadcaster, fee_estimator, logger)
+ let mut inner = self.inner.lock().unwrap();
+ let logger = WithChannelMonitor::from_impl(logger, &*inner);
+ inner.update_monitor(updates, broadcaster, fee_estimator, &logger)
}
/// Gets the update_id from the latest ChannelMonitorUpdate which was applied to this
/// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager
pub fn get_latest_holder_commitment_txn<L: Deref>(&self, logger: &L) -> Vec<Transaction>
where L::Target: Logger {
- self.inner.lock().unwrap().get_latest_holder_commitment_txn(logger)
+ let mut inner = self.inner.lock().unwrap();
+ let logger = WithChannelMonitor::from_impl(logger, &*inner);
+ inner.get_latest_holder_commitment_txn(&logger)
}
/// Unsafe test-only version of get_latest_holder_commitment_txn used by our test framework
#[cfg(any(test, feature = "unsafe_revoked_tx_signing"))]
pub fn unsafe_get_latest_holder_commitment_txn<L: Deref>(&self, logger: &L) -> Vec<Transaction>
where L::Target: Logger {
- self.inner.lock().unwrap().unsafe_get_latest_holder_commitment_txn(logger)
+ let mut inner = self.inner.lock().unwrap();
+ let logger = WithChannelMonitor::from_impl(logger, &*inner);
+ inner.unsafe_get_latest_holder_commitment_txn(&logger)
}
/// Processes transactions in a newly connected block, which may result in any of the following:
height: u32,
broadcaster: B,
fee_estimator: F,
- logger: L,
+ logger: &L,
) -> Vec<TransactionOutputs>
where
B::Target: BroadcasterInterface,
F::Target: FeeEstimator,
L::Target: Logger,
{
- self.inner.lock().unwrap().block_connected(
- header, txdata, height, broadcaster, fee_estimator, logger)
+ let mut inner = self.inner.lock().unwrap();
+ let logger = WithChannelMonitor::from_impl(logger, &*inner);
+ inner.block_connected(
+ header, txdata, height, broadcaster, fee_estimator, &logger)
}
/// Determines if the disconnected block contained any transactions of interest and updates
height: u32,
broadcaster: B,
fee_estimator: F,
- logger: L,
+ logger: &L,
) where
B::Target: BroadcasterInterface,
F::Target: FeeEstimator,
L::Target: Logger,
{
- self.inner.lock().unwrap().block_disconnected(
- header, height, broadcaster, fee_estimator, logger)
+ let mut inner = self.inner.lock().unwrap();
+ let logger = WithChannelMonitor::from_impl(logger, &*inner);
+ inner.block_disconnected(
+ header, height, broadcaster, fee_estimator, &logger)
}
/// Processes transactions confirmed in a block with the given header and height, returning new
height: u32,
broadcaster: B,
fee_estimator: F,
- logger: L,
+ logger: &L,
) -> Vec<TransactionOutputs>
where
B::Target: BroadcasterInterface,
L::Target: Logger,
{
let bounded_fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
- self.inner.lock().unwrap().transactions_confirmed(
- header, txdata, height, broadcaster, &bounded_fee_estimator, logger)
+ let mut inner = self.inner.lock().unwrap();
+ let logger = WithChannelMonitor::from_impl(logger, &*inner);
+ inner.transactions_confirmed(
+ header, txdata, height, broadcaster, &bounded_fee_estimator, &logger)
}
/// Processes a transaction that was reorganized out of the chain.
txid: &Txid,
broadcaster: B,
fee_estimator: F,
- logger: L,
+ logger: &L,
) where
B::Target: BroadcasterInterface,
F::Target: FeeEstimator,
L::Target: Logger,
{
let bounded_fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
- self.inner.lock().unwrap().transaction_unconfirmed(
- txid, broadcaster, &bounded_fee_estimator, logger);
+ let mut inner = self.inner.lock().unwrap();
+ let logger = WithChannelMonitor::from_impl(logger, &*inner);
+ inner.transaction_unconfirmed(
+ txid, broadcaster, &bounded_fee_estimator, &logger
+ );
}
/// Updates the monitor with the current best chain tip, returning new outputs to watch. See
height: u32,
broadcaster: B,
fee_estimator: F,
- logger: L,
+ logger: &L,
) -> Vec<TransactionOutputs>
where
B::Target: BroadcasterInterface,
L::Target: Logger,
{
let bounded_fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
- self.inner.lock().unwrap().best_block_updated(
- header, height, broadcaster, &bounded_fee_estimator, logger)
+ let mut inner = self.inner.lock().unwrap();
+ let logger = WithChannelMonitor::from_impl(logger, &*inner);
+ inner.best_block_updated(
+ header, height, broadcaster, &bounded_fee_estimator, &logger
+ )
}
/// Returns the set of txids that should be monitored for re-organization out of the chain.
/// invoking this every 30 seconds, or lower if running in an environment with spotty
/// connections, like on mobile.
pub fn rebroadcast_pending_claims<B: Deref, F: Deref, L: Deref>(
- &self, broadcaster: B, fee_estimator: F, logger: L,
+ &self, broadcaster: B, fee_estimator: F, logger: &L,
)
where
B::Target: BroadcasterInterface,
{
let fee_estimator = LowerBoundedFeeEstimator::new(fee_estimator);
let mut inner = self.inner.lock().unwrap();
+ let logger = WithChannelMonitor::from_impl(logger, &*inner);
let current_height = inner.best_block.height;
inner.onchain_tx_handler.rebroadcast_pending_claims(
current_height, &broadcaster, &fee_estimator, &logger,
Ok(())
}
- pub(crate) fn provide_initial_counterparty_commitment_tx<L: Deref>(
+ fn provide_initial_counterparty_commitment_tx<L: Deref>(
&mut self, txid: Txid, htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)>,
commitment_number: u64, their_per_commitment_point: PublicKey, feerate_per_kw: u32,
- to_broadcaster_value: u64, to_countersignatory_value: u64, logger: &L
- )
- where L::Target: Logger
- {
+ to_broadcaster_value: u64, to_countersignatory_value: u64, logger: &WithChannelMonitor<L>,
+ ) where L::Target: Logger {
self.initial_counterparty_commitment_info = Some((their_per_commitment_point.clone(),
feerate_per_kw, to_broadcaster_value, to_countersignatory_value));
their_per_commitment_point, logger);
}
- pub(crate) fn provide_latest_counterparty_commitment_tx<L: Deref>(&mut self, txid: Txid, htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)>, commitment_number: u64, their_per_commitment_point: PublicKey, logger: &L) where L::Target: Logger {
+ fn provide_latest_counterparty_commitment_tx<L: Deref>(
+ &mut self, txid: Txid,
+ htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Box<HTLCSource>>)>,
+ commitment_number: u64, their_per_commitment_point: PublicKey, logger: &WithChannelMonitor<L>,
+ ) where L::Target: Logger {
// TODO: Encrypt the htlc_outputs data with the single-hash of the commitment transaction
// so that a remote monitor doesn't learn anything unless there is a malicious close.
// (only maybe, sadly we cant do the same for local info, as we need to be aware of
/// commitment_tx_infos which contain the payment hash have been revoked.
fn provide_payment_preimage<B: Deref, F: Deref, L: Deref>(
&mut self, payment_hash: &PaymentHash, payment_preimage: &PaymentPreimage, broadcaster: &B,
- fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L)
+ fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &WithChannelMonitor<L>)
where B::Target: BroadcasterInterface,
F::Target: FeeEstimator,
L::Target: Logger,
}
}
- pub(crate) fn broadcast_latest_holder_commitment_txn<B: Deref, L: Deref>(&mut self, broadcaster: &B, logger: &L)
+ fn broadcast_latest_holder_commitment_txn<B: Deref, L: Deref>(&mut self, broadcaster: &B, logger: &WithChannelMonitor<L>)
where B::Target: BroadcasterInterface,
- L::Target: Logger,
+ L::Target: Logger,
{
let commit_txs = self.get_latest_holder_commitment_txn(logger);
let mut txs = vec![];
self.pending_monitor_events.push(MonitorEvent::HolderForceClosed(self.funding_info.0));
}
- pub fn update_monitor<B: Deref, F: Deref, L: Deref>(&mut self, updates: &ChannelMonitorUpdate, broadcaster: &B, fee_estimator: &F, logger: &L) -> Result<(), ()>
+ fn update_monitor<B: Deref, F: Deref, L: Deref>(
+ &mut self, updates: &ChannelMonitorUpdate, broadcaster: &B, fee_estimator: &F, logger: &WithChannelMonitor<L>
+ ) -> Result<(), ()>
where B::Target: BroadcasterInterface,
F::Target: FeeEstimator,
L::Target: Logger,
} else { ret }
}
- pub fn get_latest_update_id(&self) -> u64 {
+ fn get_latest_update_id(&self) -> u64 {
self.latest_update_id
}
- pub fn get_funding_txo(&self) -> &(OutPoint, ScriptBuf) {
+ fn get_funding_txo(&self) -> &(OutPoint, ScriptBuf) {
&self.funding_info
}
- pub fn get_outputs_to_watch(&self) -> &HashMap<Txid, Vec<(u32, ScriptBuf)>> {
+ fn get_outputs_to_watch(&self) -> &HashMap<Txid, Vec<(u32, ScriptBuf)>> {
// If we've detected a counterparty commitment tx on chain, we must include it in the set
// of outputs to watch for spends of, otherwise we're likely to lose user funds. Because
// its trivial to do, double-check that here.
&self.outputs_to_watch
}
- pub fn get_and_clear_pending_monitor_events(&mut self) -> Vec<MonitorEvent> {
+ fn get_and_clear_pending_monitor_events(&mut self) -> Vec<MonitorEvent> {
let mut ret = Vec::new();
mem::swap(&mut ret, &mut self.pending_monitor_events);
ret
ret
}
- pub(crate) fn initial_counterparty_commitment_tx(&mut self) -> Option<CommitmentTransaction> {
+ fn initial_counterparty_commitment_tx(&mut self) -> Option<CommitmentTransaction> {
let (their_per_commitment_point, feerate_per_kw, to_broadcaster_value,
to_countersignatory_value) = self.initial_counterparty_commitment_info?;
let htlc_outputs = vec![];
channel_parameters)
}
- pub(crate) fn counterparty_commitment_txs_from_update(&self, update: &ChannelMonitorUpdate) -> Vec<CommitmentTransaction> {
+ fn counterparty_commitment_txs_from_update(&self, update: &ChannelMonitorUpdate) -> Vec<CommitmentTransaction> {
update.updates.iter().filter_map(|update| {
match update {
&ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { commitment_txid,
}).collect()
}
- pub(crate) fn sign_to_local_justice_tx(
+ fn sign_to_local_justice_tx(
&self, mut justice_tx: Transaction, input_idx: usize, value: u64, commitment_number: u64
) -> Result<Transaction, ()> {
let secret = self.get_secret(commitment_number).ok_or(())?;
self.commitment_secrets.get_secret(idx)
}
- pub(crate) fn get_min_seen_secret(&self) -> u64 {
+ fn get_min_seen_secret(&self) -> u64 {
self.commitment_secrets.get_min_seen_secret()
}
- pub(crate) fn get_cur_counterparty_commitment_number(&self) -> u64 {
+ fn get_cur_counterparty_commitment_number(&self) -> u64 {
self.current_counterparty_commitment_number
}
- pub(crate) fn get_cur_holder_commitment_number(&self) -> u64 {
+ fn get_cur_holder_commitment_number(&self) -> u64 {
self.current_holder_commitment_number
}
}
}
- pub fn get_latest_holder_commitment_txn<L: Deref>(&mut self, logger: &L) -> Vec<Transaction> where L::Target: Logger {
+ fn get_latest_holder_commitment_txn<L: Deref>(
+ &mut self, logger: &WithChannelMonitor<L>,
+ ) -> Vec<Transaction> where L::Target: Logger {
log_debug!(logger, "Getting signed latest holder commitment transaction!");
self.holder_tx_signed = true;
let commitment_tx = self.onchain_tx_handler.get_fully_signed_holder_tx(&self.funding_redeemscript);
#[cfg(any(test,feature = "unsafe_revoked_tx_signing"))]
/// Note that this includes possibly-locktimed-in-the-future transactions!
- fn unsafe_get_latest_holder_commitment_txn<L: Deref>(&mut self, logger: &L) -> Vec<Transaction> where L::Target: Logger {
+ fn unsafe_get_latest_holder_commitment_txn<L: Deref>(
+ &mut self, logger: &WithChannelMonitor<L>
+ ) -> Vec<Transaction> where L::Target: Logger {
log_debug!(logger, "Getting signed copy of latest holder commitment transaction!");
let commitment_tx = self.onchain_tx_handler.get_fully_signed_copy_holder_tx(&self.funding_redeemscript);
let txid = commitment_tx.txid();
holder_transactions
}
- pub fn block_connected<B: Deref, F: Deref, L: Deref>(&mut self, header: &Header, txdata: &TransactionData, height: u32, broadcaster: B, fee_estimator: F, logger: L) -> Vec<TransactionOutputs>
+ fn block_connected<B: Deref, F: Deref, L: Deref>(
+ &mut self, header: &Header, txdata: &TransactionData, height: u32, broadcaster: B,
+ fee_estimator: F, logger: &WithChannelMonitor<L>,
+ ) -> Vec<TransactionOutputs>
where B::Target: BroadcasterInterface,
- F::Target: FeeEstimator,
- L::Target: Logger,
+ F::Target: FeeEstimator,
+ L::Target: Logger,
{
let block_hash = header.block_hash();
self.best_block = BestBlock::new(block_hash, height);
height: u32,
broadcaster: B,
fee_estimator: &LowerBoundedFeeEstimator<F>,
- logger: L,
+ logger: &WithChannelMonitor<L>,
) -> Vec<TransactionOutputs>
where
B::Target: BroadcasterInterface,
if height > self.best_block.height() {
self.best_block = BestBlock::new(block_hash, height);
- self.block_confirmed(height, block_hash, vec![], vec![], vec![], &broadcaster, &fee_estimator, &logger)
+ self.block_confirmed(height, block_hash, vec![], vec![], vec![], &broadcaster, &fee_estimator, logger)
} else if block_hash != self.best_block.block_hash() {
self.best_block = BestBlock::new(block_hash, height);
self.onchain_events_awaiting_threshold_conf.retain(|ref entry| entry.height <= height);
height: u32,
broadcaster: B,
fee_estimator: &LowerBoundedFeeEstimator<F>,
- logger: L,
+ logger: &WithChannelMonitor<L>,
) -> Vec<TransactionOutputs>
where
B::Target: BroadcasterInterface,
break;
}
}
- self.is_resolving_htlc_output(&tx, height, &block_hash, &logger);
+ self.is_resolving_htlc_output(&tx, height, &block_hash, logger);
- self.check_tx_and_push_spendable_outputs(&tx, height, &block_hash, &logger);
+ self.check_tx_and_push_spendable_outputs(&tx, height, &block_hash, logger);
}
}
self.best_block = BestBlock::new(block_hash, height);
}
- self.block_confirmed(height, block_hash, txn_matched, watch_outputs, claimable_outpoints, &broadcaster, &fee_estimator, &logger)
+ self.block_confirmed(height, block_hash, txn_matched, watch_outputs, claimable_outpoints, &broadcaster, &fee_estimator, logger)
}
/// Update state for new block(s)/transaction(s) confirmed. Note that the caller must update
mut claimable_outpoints: Vec<PackageTemplate>,
broadcaster: &B,
fee_estimator: &LowerBoundedFeeEstimator<F>,
- logger: &L,
+ logger: &WithChannelMonitor<L>,
) -> Vec<TransactionOutputs>
where
B::Target: BroadcasterInterface,
watch_outputs
}
- pub fn block_disconnected<B: Deref, F: Deref, L: Deref>(&mut self, header: &Header, height: u32, broadcaster: B, fee_estimator: F, logger: L)
- where B::Target: BroadcasterInterface,
- F::Target: FeeEstimator,
- L::Target: Logger,
+ fn block_disconnected<B: Deref, F: Deref, L: Deref>(
+ &mut self, header: &Header, height: u32, broadcaster: B, fee_estimator: F, logger: &WithChannelMonitor<L>
+ ) where B::Target: BroadcasterInterface,
+ F::Target: FeeEstimator,
+ L::Target: Logger,
{
log_trace!(logger, "Block {} at height {} disconnected", header.block_hash(), height);
txid: &Txid,
broadcaster: B,
fee_estimator: &LowerBoundedFeeEstimator<F>,
- logger: L,
+ logger: &WithChannelMonitor<L>,
) where
B::Target: BroadcasterInterface,
F::Target: FeeEstimator,
false
}
- fn should_broadcast_holder_commitment_txn<L: Deref>(&self, logger: &L) -> bool where L::Target: Logger {
+ fn should_broadcast_holder_commitment_txn<L: Deref>(
+ &self, logger: &WithChannelMonitor<L>
+ ) -> bool where L::Target: Logger {
// There's no need to broadcast our commitment transaction if we've seen one confirmed (even
// with 1 confirmation) as it'll be rejected as duplicate/conflicting.
if self.funding_spend_confirmed.is_some() ||
/// Check if any transaction broadcasted is resolving HTLC output by a success or timeout on a holder
/// or counterparty commitment tx, if so send back the source, preimage if found and payment_hash of resolved HTLC
- fn is_resolving_htlc_output<L: Deref>(&mut self, tx: &Transaction, height: u32, block_hash: &BlockHash, logger: &L) where L::Target: Logger {
+ fn is_resolving_htlc_output<L: Deref>(
+ &mut self, tx: &Transaction, height: u32, block_hash: &BlockHash, logger: &WithChannelMonitor<L>,
+ ) where L::Target: Logger {
'outer_loop: for input in &tx.input {
let mut payment_data = None;
let htlc_claim = HTLCClaim::from_witness(&input.witness);
/// Checks if the confirmed transaction is paying funds back to some address we can assume to
/// own.
fn check_tx_and_push_spendable_outputs<L: Deref>(
- &mut self, tx: &Transaction, height: u32, block_hash: &BlockHash, logger: &L,
+ &mut self, tx: &Transaction, height: u32, block_hash: &BlockHash, logger: &WithChannelMonitor<L>,
) where L::Target: Logger {
for spendable_output in self.get_spendable_outputs(tx) {
let entry = OnchainEventEntry {
L::Target: Logger,
{
fn filtered_block_connected(&self, header: &Header, txdata: &TransactionData, height: u32) {
- self.0.block_connected(header, txdata, height, &*self.1, &*self.2, &WithChannelMonitor::from(&self.3, &self.0));
+ self.0.block_connected(header, txdata, height, &*self.1, &*self.2, &self.3);
}
fn block_disconnected(&self, header: &Header, height: u32) {
- self.0.block_disconnected(header, height, &*self.1, &*self.2, &WithChannelMonitor::from(&self.3, &self.0));
+ self.0.block_disconnected(header, height, &*self.1, &*self.2, &self.3);
}
}
L::Target: Logger,
{
fn transactions_confirmed(&self, header: &Header, txdata: &TransactionData, height: u32) {
- self.0.transactions_confirmed(header, txdata, height, &*self.1, &*self.2, &WithChannelMonitor::from(&self.3, &self.0));
+ self.0.transactions_confirmed(header, txdata, height, &*self.1, &*self.2, &self.3);
}
fn transaction_unconfirmed(&self, txid: &Txid) {
- self.0.transaction_unconfirmed(txid, &*self.1, &*self.2, &WithChannelMonitor::from(&self.3, &self.0));
+ self.0.transaction_unconfirmed(txid, &*self.1, &*self.2, &self.3);
}
fn best_block_updated(&self, header: &Header, height: u32) {
- self.0.best_block_updated(header, height, &*self.1, &*self.2, &WithChannelMonitor::from(&self.3, &self.0));
+ self.0.best_block_updated(header, height, &*self.1, &*self.2, &self.3);
}
fn get_relevant_txids(&self) -> Vec<(Txid, u32, Option<BlockHash>)> {
/// feerate changes between blocks, and ensuring reliability if broadcasting fails. We recommend
/// invoking this every 30 seconds, or lower if running in an environment with spotty
/// connections, like on mobile.
- pub(crate) fn rebroadcast_pending_claims<B: Deref, F: Deref, L: Deref>(
+ pub(super) fn rebroadcast_pending_claims<B: Deref, F: Deref, L: Logger>(
&mut self, current_height: u32, broadcaster: &B, fee_estimator: &LowerBoundedFeeEstimator<F>,
logger: &L,
)
where
B::Target: BroadcasterInterface,
F::Target: FeeEstimator,
- L::Target: Logger,
{
let mut bump_requests = Vec::with_capacity(self.pending_claim_requests.len());
for (claim_id, request) in self.pending_claim_requests.iter() {
///
/// Panics if there are signing errors, because signing operations in reaction to on-chain
/// events are not expected to fail, and if they do, we may lose funds.
- fn generate_claim<F: Deref, L: Deref>(
+ fn generate_claim<F: Deref, L: Logger>(
&mut self, cur_height: u32, cached_request: &PackageTemplate, force_feerate_bump: bool,
fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L,
) -> Option<(u32, u64, OnchainClaim)>
- where
- F::Target: FeeEstimator,
- L::Target: Logger,
+ where F::Target: FeeEstimator,
{
let request_outpoints = cached_request.outpoints();
if request_outpoints.is_empty() {
/// `conf_height` represents the height at which the request was generated. This
/// does not need to equal the current blockchain tip height, which should be provided via
/// `cur_height`, however it must never be higher than `cur_height`.
- pub(crate) fn update_claims_view_from_requests<B: Deref, F: Deref, L: Deref>(
+ pub(super) fn update_claims_view_from_requests<B: Deref, F: Deref, L: Logger>(
&mut self, requests: Vec<PackageTemplate>, conf_height: u32, cur_height: u32,
broadcaster: &B, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L
) where
B::Target: BroadcasterInterface,
F::Target: FeeEstimator,
- L::Target: Logger,
{
log_debug!(logger, "Updating claims view at height {} with {} claim requests", cur_height, requests.len());
let mut preprocessed_requests = Vec::with_capacity(requests.len());
/// `conf_height` represents the height at which the transactions in `txn_matched` were
/// confirmed. This does not need to equal the current blockchain tip height, which should be
/// provided via `cur_height`, however it must never be higher than `cur_height`.
- pub(crate) fn update_claims_view_from_matched_txn<B: Deref, F: Deref, L: Deref>(
+ pub(super) fn update_claims_view_from_matched_txn<B: Deref, F: Deref, L: Logger>(
&mut self, txn_matched: &[&Transaction], conf_height: u32, conf_hash: BlockHash,
cur_height: u32, broadcaster: &B, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L
) where
B::Target: BroadcasterInterface,
F::Target: FeeEstimator,
- L::Target: Logger,
{
log_debug!(logger, "Updating claims view at height {} with {} matched transactions in block {}", cur_height, txn_matched.len(), conf_height);
let mut bump_candidates = HashMap::new();
}
}
- pub(crate) fn transaction_unconfirmed<B: Deref, F: Deref, L: Deref>(
+ pub(super) fn transaction_unconfirmed<B: Deref, F: Deref, L: Logger>(
&mut self,
txid: &Txid,
broadcaster: B,
fee_estimator: &LowerBoundedFeeEstimator<F>,
- logger: L,
+ logger: &L,
) where
B::Target: BroadcasterInterface,
F::Target: FeeEstimator,
- L::Target: Logger,
{
let mut height = None;
for entry in self.onchain_events_awaiting_threshold_conf.iter() {
}
}
- pub(crate) fn block_disconnected<B: Deref, F: Deref, L: Deref>(&mut self, height: u32, broadcaster: B, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: L)
+ pub(super) fn block_disconnected<B: Deref, F: Deref, L: Logger>(&mut self, height: u32, broadcaster: B, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L)
where B::Target: BroadcasterInterface,
- F::Target: FeeEstimator,
- L::Target: Logger,
+ F::Target: FeeEstimator,
{
let mut bump_candidates = HashMap::new();
let onchain_events_awaiting_threshold_conf =
// `height` is the height being disconnected, so our `current_height` is 1 lower.
let current_height = height - 1;
if let Some((new_timer, new_feerate, bump_claim)) = self.generate_claim(
- current_height, &request, true /* force_feerate_bump */, fee_estimator, &&*logger
+ current_height, &request, true /* force_feerate_bump */, fee_estimator, logger
) {
request.set_timer(new_timer);
request.set_feerate(new_feerate);
}
htlcs
}
- pub(crate) fn finalize_malleable_package<L: Deref, Signer: WriteableEcdsaChannelSigner>(
+ pub(crate) fn finalize_malleable_package<L: Logger, Signer: WriteableEcdsaChannelSigner>(
&self, current_height: u32, onchain_handler: &mut OnchainTxHandler<Signer>, value: u64,
destination_script: ScriptBuf, logger: &L
- ) -> Option<Transaction> where L::Target: Logger {
+ ) -> Option<Transaction> {
debug_assert!(self.is_malleable());
let mut bumped_tx = Transaction {
version: 2,
log_debug!(logger, "Finalized transaction {} ready to broadcast", bumped_tx.txid());
Some(bumped_tx)
}
- pub(crate) fn finalize_untractable_package<L: Deref, Signer: WriteableEcdsaChannelSigner>(
+ pub(crate) fn finalize_untractable_package<L: Logger, Signer: WriteableEcdsaChannelSigner>(
&self, onchain_handler: &mut OnchainTxHandler<Signer>, logger: &L,
- ) -> Option<Transaction> where L::Target: Logger {
+ ) -> Option<Transaction> {
debug_assert!(!self.is_malleable());
if let Some((outpoint, outp)) = self.inputs.first() {
if let Some(final_tx) = outp.get_finalized_tx(outpoint, onchain_handler) {
/// Returns value in satoshis to be included as package outgoing output amount and feerate
/// which was used to generate the value. Will not return less than `dust_limit_sats` for the
/// value.
- pub(crate) fn compute_package_output<F: Deref, L: Deref>(
+ pub(crate) fn compute_package_output<F: Deref, L: Logger>(
&self, predicted_weight: u64, dust_limit_sats: u64, force_feerate_bump: bool,
fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L,
) -> Option<(u64, u64)>
- where
- F::Target: FeeEstimator,
- L::Target: Logger,
+ where F::Target: FeeEstimator,
{
debug_assert!(self.malleability == PackageMalleability::Malleable, "The package output is fixed for non-malleable packages");
let input_amounts = self.package_amount();
///
/// [`OnChainSweep`]: crate::chain::chaininterface::ConfirmationTarget::OnChainSweep
/// [`FEERATE_FLOOR_SATS_PER_KW`]: crate::chain::chaininterface::MIN_RELAY_FEE_SAT_PER_1000_WEIGHT
-fn compute_fee_from_spent_amounts<F: Deref, L: Deref>(input_amounts: u64, predicted_weight: u64, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L) -> Option<(u64, u64)>
+fn compute_fee_from_spent_amounts<F: Deref, L: Logger>(input_amounts: u64, predicted_weight: u64, fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L) -> Option<(u64, u64)>
where F::Target: FeeEstimator,
- L::Target: Logger,
{
let sweep_feerate = fee_estimator.bounded_sat_per_1000_weight(ConfirmationTarget::OnChainSweep);
let fee_rate = cmp::min(sweep_feerate, compute_feerate_sat_per_1000_weight(input_amounts / 2, predicted_weight));
/// feerate, or just use the previous feerate otherwise. If a feerate bump did happen, we also
/// verify that those bumping heuristics respect BIP125 rules 3) and 4) and if required adjust the
/// new fee to meet the RBF policy requirement.
-fn feerate_bump<F: Deref, L: Deref>(
+fn feerate_bump<F: Deref, L: Logger>(
predicted_weight: u64, input_amounts: u64, previous_feerate: u64, force_feerate_bump: bool,
fee_estimator: &LowerBoundedFeeEstimator<F>, logger: &L,
) -> Option<(u64, u64)>
where
F::Target: FeeEstimator,
- L::Target: Logger,
{
// If old feerate inferior to actual one given back by Fee Estimator, use it to compute new fee...
let (new_fee, new_feerate) = if let Some((new_fee, new_feerate)) = compute_fee_from_spent_amounts(input_amounts, predicted_weight, fee_estimator, logger) {
use crate::ln::onion_utils::HTLCFailReason;
use crate::chain::BestBlock;
use crate::chain::chaininterface::{FeeEstimator, ConfirmationTarget, LowerBoundedFeeEstimator};
-use crate::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateStep, WithChannelMonitor, LATENCY_GRACE_PERIOD_BLOCKS, CLOSED_CHANNEL_UPDATE_ID};
+use crate::chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateStep, LATENCY_GRACE_PERIOD_BLOCKS, CLOSED_CHANNEL_UPDATE_ID};
use crate::chain::transaction::{OutPoint, TransactionData};
use crate::sign::ecdsa::{EcdsaChannelSigner, WriteableEcdsaChannelSigner};
use crate::sign::{EntropySource, ChannelSigner, SignerProvider, NodeSigner, Recipient};
funding_redeemscript.clone(), self.context.channel_value_satoshis,
obscure_factor,
holder_commitment_tx, best_block, self.context.counterparty_node_id);
- let logger_with_chan_monitor = WithChannelMonitor::from(logger, &channel_monitor);
channel_monitor.provide_initial_counterparty_commitment_tx(
counterparty_initial_bitcoin_tx.txid, Vec::new(),
self.context.cur_counterparty_commitment_transaction_number,
self.context.counterparty_cur_commitment_point.unwrap(),
counterparty_initial_commitment_tx.feerate_per_kw(),
counterparty_initial_commitment_tx.to_broadcaster_value_sat(),
- counterparty_initial_commitment_tx.to_countersignatory_value_sat(), &&logger_with_chan_monitor);
+ counterparty_initial_commitment_tx.to_countersignatory_value_sat(), logger);
assert!(!self.context.channel_state.is_monitor_update_in_progress()); // We have no had any monitor(s) yet to fail update!
if self.context.is_batch_funding() {
funding_redeemscript.clone(), self.context.channel_value_satoshis,
obscure_factor,
holder_commitment_tx, best_block, self.context.counterparty_node_id);
- let logger_with_chan_monitor = WithChannelMonitor::from(logger, &channel_monitor);
channel_monitor.provide_initial_counterparty_commitment_tx(
counterparty_initial_commitment_tx.trust().txid(), Vec::new(),
self.context.cur_counterparty_commitment_transaction_number + 1,
self.context.counterparty_cur_commitment_point.unwrap(), self.context.feerate_per_kw,
counterparty_initial_commitment_tx.to_broadcaster_value_sat(),
- counterparty_initial_commitment_tx.to_countersignatory_value_sat(), &&logger_with_chan_monitor);
+ counterparty_initial_commitment_tx.to_countersignatory_value_sat(), logger);
log_info!(logger, "{} funding_signed for peer for channel {}",
if funding_signed.is_some() { "Generated" } else { "Waiting for signature on" }, &self.context.channel_id());
let mut pending_outbound_blinding_points: Vec<Option<PublicKey>> = Vec::new();
(self.context.pending_outbound_htlcs.len() as u64).write(writer)?;
- for (idx, htlc) in self.context.pending_outbound_htlcs.iter().enumerate() {
+ for htlc in self.context.pending_outbound_htlcs.iter() {
htlc.htlc_id.write(writer)?;
htlc.amount_msat.write(writer)?;
htlc.cltv_expiry.write(writer)?;
reason.write(writer)?;
}
}
- if let Some(skimmed_fee) = htlc.skimmed_fee_msat {
- if pending_outbound_skimmed_fees.is_empty() {
- for _ in 0..idx { pending_outbound_skimmed_fees.push(None); }
- }
- pending_outbound_skimmed_fees.push(Some(skimmed_fee));
- } else if !pending_outbound_skimmed_fees.is_empty() {
- pending_outbound_skimmed_fees.push(None);
- }
+ pending_outbound_skimmed_fees.push(htlc.skimmed_fee_msat);
pending_outbound_blinding_points.push(htlc.blinding_point);
}
let mut holding_cell_skimmed_fees: Vec<Option<u64>> = Vec::new();
let mut holding_cell_blinding_points: Vec<Option<PublicKey>> = Vec::new();
(self.context.holding_cell_htlc_updates.len() as u64).write(writer)?;
- for (idx, update) in self.context.holding_cell_htlc_updates.iter().enumerate() {
+ for update in self.context.holding_cell_htlc_updates.iter() {
match update {
&HTLCUpdateAwaitingACK::AddHTLC {
ref amount_msat, ref cltv_expiry, ref payment_hash, ref source, ref onion_routing_packet,
source.write(writer)?;
onion_routing_packet.write(writer)?;
- if let Some(skimmed_fee) = skimmed_fee_msat {
- if holding_cell_skimmed_fees.is_empty() {
- for _ in 0..idx { holding_cell_skimmed_fees.push(None); }
- }
- holding_cell_skimmed_fees.push(Some(skimmed_fee));
- } else if !holding_cell_skimmed_fees.is_empty() { holding_cell_skimmed_fees.push(None); }
-
+ holding_cell_skimmed_fees.push(skimmed_fee_msat);
holding_cell_blinding_points.push(blinding_point);
},
&HTLCUpdateAwaitingACK::ClaimHTLC { ref payment_preimage, ref htlc_id } => {
}
#[test]
- fn blinding_point_ser() {
- // Ensure that channel blinding points are (de)serialized properly.
+ fn blinding_point_skimmed_fee_ser() {
+ // Ensure that channel blinding points and skimmed fees are (de)serialized properly.
let feeest = LowerBoundedFeeEstimator::new(&TestFeeEstimator{fee_est: 15000});
let secp_ctx = Secp256k1::new();
let seed = [42; 32];
if idx % 2 == 0 {
htlc.blinding_point = Some(test_utils::pubkey(42 + idx as u8));
}
+ if idx % 3 == 0 {
+ htlc.skimmed_fee_msat = Some(1);
+ }
}
chan.context.pending_outbound_htlcs = pending_outbound_htlcs.clone();
holding_cell_htlc_updates.push(dummy_holding_cell_claim_htlc.clone());
} else {
let mut dummy_add = dummy_holding_cell_add_htlc.clone();
- if let HTLCUpdateAwaitingACK::AddHTLC { ref mut blinding_point, .. } = &mut dummy_add {
+ if let HTLCUpdateAwaitingACK::AddHTLC {
+ ref mut blinding_point, ref mut skimmed_fee_msat, ..
+ } = &mut dummy_add {
*blinding_point = Some(test_utils::pubkey(42 + i));
+ *skimmed_fee_msat = Some(42);
} else { panic!() }
holding_cell_htlc_updates.push(dummy_add);
}
// Alternatively, we can fill an outbound HTLC with a HTLCSource::OutboundRoute indicating this is
// our payment, which we can use to decode errors or inform the user that the payment was sent.
-/// Routing info for an inbound HTLC onion.
+/// Information about where a received HTLC('s onion) has indicated the HTLC should go.
#[derive(Clone)] // See Channel::revoke_and_ack for why, tl;dr: Rust bug
pub enum PendingHTLCRouting {
- /// A forwarded HTLC.
+ /// An HTLC which should be forwarded on to another node.
Forward {
- /// BOLT 4 onion packet.
+ /// The onion which should be included in the forwarded HTLC, telling the next hop what to
+ /// do with the HTLC.
onion_packet: msgs::OnionPacket,
- /// The SCID from the onion that we should forward to. This could be a real SCID or a fake one
- /// generated using `get_fake_scid` from the scid_utils::fake_scid module.
+ /// The short channel ID of the channel which we were instructed to forward this HTLC to.
+ ///
+ /// This could be a real on-chain SCID, an SCID alias, or some other SCID which has meaning
+ /// to the receiving node, such as one returned from
+ /// [`ChannelManager::get_intercept_scid`] or [`ChannelManager::get_phantom_scid`].
short_channel_id: u64, // This should be NonZero<u64> eventually when we bump MSRV
/// Set if this HTLC is being forwarded within a blinded path.
blinded: Option<BlindedForward>,
},
- /// An HTLC paid to an invoice (supposedly) generated by us.
- /// At this point, we have not checked that the invoice being paid was actually generated by us,
- /// but rather it's claiming to pay an invoice of ours.
+ /// The onion indicates that this is a payment for an invoice (supposedly) generated by us.
+ ///
+ /// Note that at this point, we have not checked that the invoice being paid was actually
+ /// generated by us, but rather it's claiming to pay an invoice of ours.
Receive {
- /// Payment secret and total msat received.
+ /// Information about the amount the sender intended to pay and (potential) proof that this
+ /// is a payment for an invoice we generated. This proof of payment is is also used for
+ /// linking MPP parts of a larger payment.
payment_data: msgs::FinalOnionHopData,
- /// See [`RecipientOnionFields::payment_metadata`] for more info.
+ /// Additional data which we (allegedly) instructed the sender to include in the onion.
+ ///
+ /// For HTLCs received by LDK, this will ultimately be exposed in
+ /// [`Event::PaymentClaimable::onion_fields`] as
+ /// [`RecipientOnionFields::payment_metadata`].
payment_metadata: Option<Vec<u8>>,
/// CLTV expiry of the received HTLC.
+ ///
/// Used to track when we should expire pending HTLCs that go unclaimed.
incoming_cltv_expiry: u32,
- /// Shared secret derived using a phantom node secret key. If this field is Some, the
- /// payment was sent to a phantom node (one hop beyond the current node), but can be
- /// settled by this node.
+ /// If the onion had forwarding instructions to one of our phantom node SCIDs, this will
+ /// provide the onion shared secret used to decrypt the next level of forwarding
+ /// instructions.
phantom_shared_secret: Option<[u8; 32]>,
- /// See [`RecipientOnionFields::custom_tlvs`] for more info.
+ /// Custom TLVs which were set by the sender.
+ ///
+ /// For HTLCs received by LDK, this will ultimately be exposed in
+ /// [`Event::PaymentClaimable::onion_fields`] as
+ /// [`RecipientOnionFields::custom_tlvs`].
custom_tlvs: Vec<(u64, Vec<u8>)>,
},
- /// Incoming keysend (sender provided the preimage in a TLV).
+ /// The onion indicates that this is for payment to us but which contains the preimage for
+ /// claiming included, and is unrelated to any invoice we'd previously generated (aka a
+ /// "keysend" or "spontaneous" payment).
ReceiveKeysend {
- /// This was added in 0.0.116 and will break deserialization on downgrades.
+ /// Information about the amount the sender intended to pay and possibly a token to
+ /// associate MPP parts of a larger payment.
+ ///
+ /// This will only be filled in if receiving MPP keysend payments is enabled, and it being
+ /// present will cause deserialization to fail on versions of LDK prior to 0.0.116.
payment_data: Option<msgs::FinalOnionHopData>,
/// Preimage for this onion payment. This preimage is provided by the sender and will be
/// used to settle the spontaneous payment.
payment_preimage: PaymentPreimage,
- /// See [`RecipientOnionFields::payment_metadata`] for more info.
+ /// Additional data which we (allegedly) instructed the sender to include in the onion.
+ ///
+ /// For HTLCs received by LDK, this will ultimately bubble back up as
+ /// [`RecipientOnionFields::payment_metadata`].
payment_metadata: Option<Vec<u8>>,
/// CLTV expiry of the received HTLC.
+ ///
/// Used to track when we should expire pending HTLCs that go unclaimed.
incoming_cltv_expiry: u32,
- /// See [`RecipientOnionFields::custom_tlvs`] for more info.
+ /// Custom TLVs which were set by the sender.
+ ///
+ /// For HTLCs received by LDK, these will ultimately bubble back up as
+ /// [`RecipientOnionFields::custom_tlvs`].
custom_tlvs: Vec<(u64, Vec<u8>)>,
},
}
}
}
-/// Full details of an incoming HTLC, including routing info.
+/// Information about an incoming HTLC, including the [`PendingHTLCRouting`] describing where it
+/// should go next.
#[derive(Clone)] // See Channel::revoke_and_ack for why, tl;dr: Rust bug
pub struct PendingHTLCInfo {
/// Further routing details based on whether the HTLC is being forwarded or received.
pub routing: PendingHTLCRouting,
- /// Shared secret from the previous hop.
- /// Used encrypt failure packets in the event that the HTLC needs to be failed backwards.
+ /// The onion shared secret we build with the sender used to decrypt the onion.
+ ///
+ /// This is later used to encrypt failure packets in the event that the HTLC is failed.
pub incoming_shared_secret: [u8; 32],
/// Hash of the payment preimage, to lock the payment until the receiver releases the preimage.
pub payment_hash: PaymentHash,
- /// Amount offered by this HTLC.
- pub incoming_amt_msat: Option<u64>, // Added in 0.0.113
- /// Sender intended amount to forward or receive (actual amount received
- /// may overshoot this in either case)
+ /// Amount received in the incoming HTLC.
+ ///
+ /// This field was added in LDK 0.0.113 and will be `None` for objects written by prior
+ /// versions.
+ pub incoming_amt_msat: Option<u64>,
+ /// The amount the sender indicated should be forwarded on to the next hop or amount the sender
+ /// intended for us to receive for received payments.
+ ///
+ /// If the received amount is less than this for received payments, an intermediary hop has
+ /// attempted to steal some of our funds and we should fail the HTLC (the sender should retry
+ /// it along another path).
+ ///
+ /// Because nodes can take less than their required fees, and because senders may wish to
+ /// improve their own privacy, this amount may be less than [`Self::incoming_amt_msat`] for
+ /// received payments. In such cases, recipients must handle this HTLC as if it had received
+ /// [`Self::outgoing_amt_msat`].
pub outgoing_amt_msat: u64,
- /// Outgoing timelock expiration blockheight.
+ /// The CLTV the sender has indicated we should set on the forwarded HTLC (or has indicated
+ /// should have been set on the received HTLC for received payments).
pub outgoing_cltv_value: u32,
- /// The fee being skimmed off the top of this HTLC. If this is a forward, it'll be the fee we are
- /// skimming. If we're receiving this HTLC, it's the fee that our counterparty skimmed.
+ /// The fee taken for this HTLC in addition to the standard protocol HTLC fees.
+ ///
+ /// If this is a payment for forwarding, this is the fee we are taking before forwarding the
+ /// HTLC.
+ ///
+ /// If this is a received payment, this is the fee that our counterparty took.
+ ///
+ /// This is used to allow LSPs to take fees as a part of payments, without the sender having to
+ /// shoulder them.
pub skimmed_fee_msat: Option<u64>,
}
/// [`internal_closing_signed`]: Self::internal_closing_signed
fn get_channel_update_for_unicast(&self, chan: &Channel<SP>) -> Result<msgs::ChannelUpdate, LightningError> {
let logger = WithChannelContext::from(&self.logger, &chan.context);
- log_trace!(logger, "Attempting to generate channel update for channel {}", log_bytes!(chan.context.channel_id().0));
+ log_trace!(logger, "Attempting to generate channel update for channel {}", chan.context.channel_id());
let short_channel_id = match chan.context.get_short_channel_id().or(chan.context.latest_inbound_scid_alias()) {
None => return Err(LightningError{err: "Channel not yet established".to_owned(), action: msgs::ErrorAction::IgnoreError}),
Some(id) => id,
fn get_channel_update_for_onion(&self, short_channel_id: u64, chan: &Channel<SP>) -> Result<msgs::ChannelUpdate, LightningError> {
let logger = WithChannelContext::from(&self.logger, &chan.context);
- log_trace!(logger, "Generating channel update for channel {}", log_bytes!(chan.context.channel_id().0));
+ log_trace!(logger, "Generating channel update for channel {}", chan.context.channel_id());
let were_node_one = self.our_network_pubkey.serialize()[..] < chan.context.get_counterparty_node_id().serialize()[..];
let enabled = chan.context.is_usable() && match chan.channel_update_status() {
},
hash_map::Entry::Vacant(_) => {
log_debug!(logger, "Sending bogus ChannelReestablish for unknown channel {} to force channel closure",
- log_bytes!(msg.channel_id.0));
+ msg.channel_id);
// Unfortunately, lnd doesn't force close on errors
// (https://github.com/lightningnetwork/lnd/blob/abb1e3463f3a83bbb843d5c399869dbe930ad94f/htlcswitch/link.go#L2119).
// One of the few ways to get an lnd counterparty to force close is by
hash_map::Entry::Occupied(mut entry) => {
let newly_added = entry.get_mut().insert(session_priv_bytes, &path);
log_info!(logger, "{} a pending payment path for {} msat for session priv {} on an existing pending payment with payment hash {}",
- if newly_added { "Added" } else { "Had" }, path_amt, log_bytes!(session_priv_bytes), log_bytes!(htlc.payment_hash.0));
+ if newly_added { "Added" } else { "Had" }, path_amt, log_bytes!(session_priv_bytes), htlc.payment_hash);
},
hash_map::Entry::Vacant(entry) => {
let path_fee = path.fee_msat();
}
}
if let Some(previous_hop_monitor) = args.channel_monitors.get(&claimable_htlc.prev_hop.outpoint) {
- let logger = WithChannelMonitor::from(&args.logger, previous_hop_monitor);
- previous_hop_monitor.provide_payment_preimage(&payment_hash, &payment_preimage, &args.tx_broadcaster, &bounded_fee_estimator, &&logger);
+ previous_hop_monitor.provide_payment_preimage(&payment_hash, &payment_preimage, &args.tx_broadcaster, &bounded_fee_estimator, &args.logger);
}
}
pending_events_read.push_back((events::Event::PaymentClaimed {
-//! Utilities for channelmanager.rs
+//! Utilities to decode payment onions and do contextless validation of incoming payments.
//!
-//! Includes a public [`peel_payment_onion`] function for use by external projects or libraries.
+//! Primarily features [`peel_payment_onion`], which allows the decoding of an onion statelessly
+//! and can be used to predict whether we'd accept a payment.
use bitcoin::hashes::Hash;
use bitcoin::hashes::sha256::Hash as Sha256;
})
}
-/// Peel one layer off an incoming onion, returning [`PendingHTLCInfo`] (either Forward or Receive).
+/// Peel one layer off an incoming onion, returning a [`PendingHTLCInfo`] that contains information
+/// about the intended next-hop for the HTLC.
+///
/// This does all the relevant context-free checks that LDK requires for payment relay or
/// acceptance. If the payment is to be received, and the amount matches the expected amount for
/// a given invoice, this indicates the [`msgs::UpdateAddHTLC`], once fully committed in the
/// [`Event::PaymentClaimable`]: crate::events::Event::PaymentClaimable
pub fn peel_payment_onion<NS: Deref, L: Deref, T: secp256k1::Verification>(
msg: &msgs::UpdateAddHTLC, node_signer: &NS, logger: &L, secp_ctx: &Secp256k1<T>,
- cur_height: u32, accept_mpp_keysend: bool,
+ cur_height: u32, accept_mpp_keysend: bool, allow_skimmed_fees: bool,
) -> Result<PendingHTLCInfo, InboundOnionErr>
where
NS::Target: NodeSigner,
err_data: Vec::new(),
});
}
+
+ // TODO: If this is potentially a phantom payment we should decode the phantom payment
+ // onion here and check it.
+
create_fwd_pending_htlc_info(
msg, next_hop_data, next_hop_hmac, new_packet_bytes, shared_secret,
Some(next_packet_pubkey)
onion_utils::Hop::Receive(received_data) => {
create_recv_pending_htlc_info(
received_data, shared_secret, msg.payment_hash, msg.amount_msat, msg.cltv_expiry,
- None, false, msg.skimmed_fee_msat, cur_height, accept_mpp_keysend,
+ None, allow_skimmed_fees, msg.skimmed_fee_msat, cur_height, accept_mpp_keysend,
)?
}
})
let msg = make_update_add_msg(amount_msat, cltv_expiry, payment_hash, onion);
let logger = test_utils::TestLogger::with_id("bob".to_string());
- let peeled = peel_payment_onion(&msg, &&bob, &&logger, &secp_ctx, cur_height, true)
+ let peeled = peel_payment_onion(&msg, &&bob, &&logger, &secp_ctx, cur_height, true, false)
.map_err(|e| e.msg).unwrap();
let next_onion = match peeled.routing {
};
let msg2 = make_update_add_msg(amount_msat, cltv_expiry, payment_hash, next_onion);
- let peeled2 = peel_payment_onion(&msg2, &&charlie, &&logger, &secp_ctx, cur_height, true)
+ let peeled2 = peel_payment_onion(&msg2, &&charlie, &&logger, &secp_ctx, cur_height, true, false)
.map_err(|e| e.msg).unwrap();
match peeled2.routing {
let mut read_pos = 0;
while read_pos < data.len() {
macro_rules! try_potential_handleerror {
- ($peer: expr, $thing: expr) => {
- match $thing {
+ ($peer: expr, $thing: expr) => {{
+ let res = $thing;
+ let logger = WithContext::from(&self.logger, peer_node_id.map(|(id, _)| id), None);
+ match res {
Ok(x) => x,
Err(e) => {
match e.action {
// re-entrant code and possibly unexpected behavior. The
// message send is optimistic anyway, and in this case
// we immediately disconnect the peer.
- log_debug!(self.logger, "Error handling message{}; disconnecting peer with: {}", OptionalFromDebugger(&peer_node_id), e.err);
+ log_debug!(logger, "Error handling message{}; disconnecting peer with: {}", OptionalFromDebugger(&peer_node_id), e.err);
return Err(PeerHandleError { });
},
msgs::ErrorAction::DisconnectPeerWithWarning { .. } => {
// re-entrant code and possibly unexpected behavior. The
// message send is optimistic anyway, and in this case
// we immediately disconnect the peer.
- log_debug!(self.logger, "Error handling message{}; disconnecting peer with: {}", OptionalFromDebugger(&peer_node_id), e.err);
+ log_debug!(logger, "Error handling message{}; disconnecting peer with: {}", OptionalFromDebugger(&peer_node_id), e.err);
return Err(PeerHandleError { });
},
msgs::ErrorAction::IgnoreAndLog(level) => {
- log_given_level!(self.logger, level, "Error handling message{}; ignoring: {}", OptionalFromDebugger(&peer_node_id), e.err);
+ log_given_level!(logger, level, "Error handling message{}; ignoring: {}", OptionalFromDebugger(&peer_node_id), e.err);
continue
},
msgs::ErrorAction::IgnoreDuplicateGossip => continue, // Don't even bother logging these
msgs::ErrorAction::IgnoreError => {
- log_debug!(self.logger, "Error handling message{}; ignoring: {}", OptionalFromDebugger(&peer_node_id), e.err);
+ log_debug!(logger, "Error handling message{}; ignoring: {}", OptionalFromDebugger(&peer_node_id), e.err);
continue;
},
msgs::ErrorAction::SendErrorMessage { msg } => {
- log_debug!(self.logger, "Error handling message{}; sending error message with: {}", OptionalFromDebugger(&peer_node_id), e.err);
+ log_debug!(logger, "Error handling message{}; sending error message with: {}", OptionalFromDebugger(&peer_node_id), e.err);
self.enqueue_message($peer, &msg);
continue;
},
msgs::ErrorAction::SendWarningMessage { msg, log_level } => {
- log_given_level!(self.logger, log_level, "Error handling message{}; sending warning message with: {}", OptionalFromDebugger(&peer_node_id), e.err);
+ log_given_level!(logger, log_level, "Error handling message{}; sending warning message with: {}", OptionalFromDebugger(&peer_node_id), e.err);
self.enqueue_message($peer, &msg);
continue;
},
}
}
}
- }
+ }}
}
let mut peer_lock = peer_mutex.lock().unwrap();
self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
},
MessageSendEvent::SendStfu { ref node_id, ref msg} => {
- log_debug!(self.logger, "Handling SendStfu event in peer_handler for node {} for channel {}",
+ let logger = WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id));
+ log_debug!(logger, "Handling SendStfu event in peer_handler for node {} for channel {}",
log_pubkey!(node_id),
&msg.channel_id);
self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
}
MessageSendEvent::SendSplice { ref node_id, ref msg} => {
- log_debug!(self.logger, "Handling SendSplice event in peer_handler for node {} for channel {}",
+ let logger = WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id));
+ log_debug!(logger, "Handling SendSplice event in peer_handler for node {} for channel {}",
log_pubkey!(node_id),
&msg.channel_id);
self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
}
MessageSendEvent::SendSpliceAck { ref node_id, ref msg} => {
- log_debug!(self.logger, "Handling SendSpliceAck event in peer_handler for node {} for channel {}",
+ let logger = WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id));
+ log_debug!(logger, "Handling SendSpliceAck event in peer_handler for node {} for channel {}",
log_pubkey!(node_id),
&msg.channel_id);
self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
}
MessageSendEvent::SendSpliceLocked { ref node_id, ref msg} => {
- log_debug!(self.logger, "Handling SendSpliceLocked event in peer_handler for node {} for channel {}",
+ let logger = WithContext::from(&self.logger, Some(*node_id), Some(msg.channel_id));
+ log_debug!(logger, "Handling SendSpliceLocked event in peer_handler for node {} for channel {}",
log_pubkey!(node_id),
&msg.channel_id);
self.enqueue_message(&mut *get_peer_for_forwarding!(node_id), msg);
/// Returns a [`DirectedChannelInfo`] for the channel directed to the given `target` from a
/// returned `source`, or `None` if `target` is not one of the channel's counterparties.
pub fn as_directed_to(&self, target: &NodeId) -> Option<(DirectedChannelInfo, &NodeId)> {
- let (direction, source) = {
+ let (direction, source, outbound) = {
if target == &self.node_one {
- (self.two_to_one.as_ref(), &self.node_two)
+ (self.two_to_one.as_ref(), &self.node_two, false)
} else if target == &self.node_two {
- (self.one_to_two.as_ref(), &self.node_one)
+ (self.one_to_two.as_ref(), &self.node_one, true)
} else {
return None;
}
};
- direction.map(|dir| (DirectedChannelInfo::new(self, dir), source))
+ direction.map(|dir| (DirectedChannelInfo::new(self, dir, outbound), source))
}
/// Returns a [`DirectedChannelInfo`] for the channel directed from the given `source` to a
/// returned `target`, or `None` if `source` is not one of the channel's counterparties.
pub fn as_directed_from(&self, source: &NodeId) -> Option<(DirectedChannelInfo, &NodeId)> {
- let (direction, target) = {
+ let (direction, target, outbound) = {
if source == &self.node_one {
- (self.one_to_two.as_ref(), &self.node_two)
+ (self.one_to_two.as_ref(), &self.node_two, true)
} else if source == &self.node_two {
- (self.two_to_one.as_ref(), &self.node_one)
+ (self.two_to_one.as_ref(), &self.node_one, false)
} else {
return None;
}
};
- direction.map(|dir| (DirectedChannelInfo::new(self, dir), target))
+ direction.map(|dir| (DirectedChannelInfo::new(self, dir, outbound), target))
}
/// Returns a [`ChannelUpdateInfo`] based on the direction implied by the channel_flag.
direction: &'a ChannelUpdateInfo,
htlc_maximum_msat: u64,
effective_capacity: EffectiveCapacity,
+ /// Outbound from the perspective of `node_one`.
+ ///
+ /// If true, the channel is considered to be outbound from `node_one` perspective.
+ /// If false, the channel is considered to be outbound from `node_two` perspective.
+ ///
+ /// [`ChannelInfo::node_one`]
+ /// [`ChannelInfo::node_two`]
+ outbound: bool,
}
impl<'a> DirectedChannelInfo<'a> {
#[inline]
- fn new(channel: &'a ChannelInfo, direction: &'a ChannelUpdateInfo) -> Self {
+ fn new(channel: &'a ChannelInfo, direction: &'a ChannelUpdateInfo, outbound: bool) -> Self {
let mut htlc_maximum_msat = direction.htlc_maximum_msat;
let capacity_msat = channel.capacity_sats.map(|capacity_sats| capacity_sats * 1000);
let effective_capacity = match capacity_msat {
Some(capacity_msat) => {
htlc_maximum_msat = cmp::min(htlc_maximum_msat, capacity_msat);
- EffectiveCapacity::Total { capacity_msat, htlc_maximum_msat: htlc_maximum_msat }
+ EffectiveCapacity::Total { capacity_msat, htlc_maximum_msat }
},
None => EffectiveCapacity::AdvertisedMaxHTLC { amount_msat: htlc_maximum_msat },
};
Self {
- channel, direction, htlc_maximum_msat, effective_capacity
+ channel, direction, htlc_maximum_msat, effective_capacity, outbound
}
}
/// Returns information for the direction.
#[inline]
pub(super) fn direction(&self) -> &'a ChannelUpdateInfo { self.direction }
+
+ /// Returns the `node_id` of the source hop.
+ ///
+ /// Refers to the `node_id` forwarding the payment to the next hop.
+ pub(super) fn source(&self) -> &'a NodeId { if self.outbound { &self.channel.node_one } else { &self.channel.node_two } }
+
+ /// Returns the `node_id` of the target hop.
+ ///
+ /// Refers to the `node_id` receiving the payment from the previous hop.
+ pub(super) fn target(&self) -> &'a NodeId { if self.outbound { &self.channel.node_two } else { &self.channel.node_one } }
}
impl<'a> fmt::Debug for DirectedChannelInfo<'a> {
impl<'a, S: Deref> ScoreLookUp for ScorerAccountingForInFlightHtlcs<'a, S> where S::Target: ScoreLookUp {
type ScoreParams = <S::Target as ScoreLookUp>::ScoreParams;
- fn channel_penalty_msat(&self, short_channel_id: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage, score_params: &Self::ScoreParams) -> u64 {
+ fn channel_penalty_msat(&self, candidate: &CandidateRouteHop, usage: ChannelUsage, score_params: &Self::ScoreParams) -> u64 {
+ let target = match candidate.target() {
+ Some(target) => target,
+ None => return self.scorer.channel_penalty_msat(candidate, usage, score_params),
+ };
+ let short_channel_id = match candidate.short_channel_id() {
+ Some(short_channel_id) => short_channel_id,
+ None => return self.scorer.channel_penalty_msat(candidate, usage, score_params),
+ };
+ let source = candidate.source();
if let Some(used_liquidity) = self.inflight_htlcs.used_liquidity_msat(
- source, target, short_channel_id
+ &source, &target, short_channel_id
) {
let usage = ChannelUsage {
inflight_htlc_msat: usage.inflight_htlc_msat.saturating_add(used_liquidity),
..usage
};
- self.scorer.channel_penalty_msat(short_channel_id, source, target, usage, score_params)
+ self.scorer.channel_penalty_msat(candidate, usage, score_params)
} else {
- self.scorer.channel_penalty_msat(short_channel_id, source, target, usage, score_params)
+ self.scorer.channel_penalty_msat(candidate, usage, score_params)
}
}
}
/// A wrapper around the various hop representations.
///
-/// Used to construct a [`PathBuildingHop`] and to estimate [`EffectiveCapacity`].
+/// Can be used to examine the properties of a hop,
+/// potentially to decide whether to include it in a route.
#[derive(Clone, Debug)]
-enum CandidateRouteHop<'a> {
+pub enum CandidateRouteHop<'a> {
/// A hop from the payer, where the outbound liquidity is known.
FirstHop {
+ /// Channel details of the first hop
+ /// [`ChannelDetails::get_outbound_payment_scid`] is assumed
+ /// to always return `Some(scid)`
+ /// this assumption is checked in [`find_route`] method.
details: &'a ChannelDetails,
+ /// The node id of the payer.
+ ///
+ /// Can be accessed via `source` method.
+ node_id: NodeId
},
- /// A hop found in the [`ReadOnlyNetworkGraph`], where the channel capacity may be unknown.
+ /// A hop found in the [`ReadOnlyNetworkGraph`],
+ /// where the channel capacity may be unknown.
PublicHop {
+ /// channel info of the hop.
info: DirectedChannelInfo<'a>,
+ /// short_channel_id of the channel.
short_channel_id: u64,
},
- /// A hop to the payee found in the BOLT 11 payment invoice, though not necessarily a direct
+ /// A hop to the payee found in the BOLT 11 payment invoice,
+ /// though not necessarily a direct
/// channel.
PrivateHop {
+ /// Hint provides information about a private hop,
+ /// needed while routing through a private
+ /// channel.
hint: &'a RouteHintHop,
+ /// Node id of the next hop in route.
+ target_node_id: NodeId
},
- /// The payee's identity is concealed behind blinded paths provided in a BOLT 12 invoice.
+ /// The payee's identity is concealed behind
+ /// blinded paths provided in a BOLT 12 invoice.
Blinded {
+ /// Hint provides information about a blinded hop,
+ /// needed while routing through a blinded path.
+ /// `BlindedPayInfo` provides information needed about the
+ /// payment while routing through a blinded path.
+ /// `BlindedPath` is the blinded path to the destination.
hint: &'a (BlindedPayInfo, BlindedPath),
+ /// Index of the hint in the original list of blinded hints.
+ /// Provided to uniquely identify a hop as we are
+ /// route building.
hint_idx: usize,
},
- /// Similar to [`Self::Blinded`], but the path here has 1 blinded hop. `BlindedPayInfo` provided
- /// for 1-hop blinded paths is ignored because it is meant to apply to the hops *between* the
- /// introduction node and the destination. Useful for tracking that we need to include a blinded
+ /// Similar to [`Self::Blinded`], but the path here
+ /// has 1 blinded hop. `BlindedPayInfo` provided
+ /// for 1-hop blinded paths is ignored
+ /// because it is meant to apply to the hops *between* the
+ /// introduction node and the destination.
+ /// Useful for tracking that we need to include a blinded
/// path at the end of our [`Route`].
OneHopBlinded {
+ /// Hint provides information about a single blinded hop,
+ /// needed while routing through a one hop blinded path.
+ /// `BlindedPayInfo` is ignored here.
+ /// `BlindedPath` is the blinded path to the destination.
hint: &'a (BlindedPayInfo, BlindedPath),
+ /// Index of the hint in the original list of blinded hints.
+ /// Provided to uniquely identify a hop as we are route building.
hint_idx: usize,
},
}
impl<'a> CandidateRouteHop<'a> {
- fn short_channel_id(&self) -> Option<u64> {
+ /// Returns short_channel_id if known.
+ /// For `FirstHop` we assume [`ChannelDetails::get_outbound_payment_scid`] is always set, this assumption is checked in
+ /// [`find_route`] method.
+ /// For `Blinded` and `OneHopBlinded` we return `None` because next hop is not known.
+ pub fn short_channel_id(&self) -> Option<u64> {
match self {
- CandidateRouteHop::FirstHop { details } => Some(details.get_outbound_payment_scid().unwrap()),
+ CandidateRouteHop::FirstHop { details, .. } => details.get_outbound_payment_scid(),
CandidateRouteHop::PublicHop { short_channel_id, .. } => Some(*short_channel_id),
- CandidateRouteHop::PrivateHop { hint } => Some(hint.short_channel_id),
+ CandidateRouteHop::PrivateHop { hint, .. } => Some(hint.short_channel_id),
CandidateRouteHop::Blinded { .. } => None,
CandidateRouteHop::OneHopBlinded { .. } => None,
}
// NOTE: This may alloc memory so avoid calling it in a hot code path.
fn features(&self) -> ChannelFeatures {
match self {
- CandidateRouteHop::FirstHop { details } => details.counterparty.features.to_context(),
+ CandidateRouteHop::FirstHop { details, .. } => details.counterparty.features.to_context(),
CandidateRouteHop::PublicHop { info, .. } => info.channel().features.clone(),
CandidateRouteHop::PrivateHop { .. } => ChannelFeatures::empty(),
CandidateRouteHop::Blinded { .. } => ChannelFeatures::empty(),
}
}
- fn cltv_expiry_delta(&self) -> u32 {
+ /// Returns cltv_expiry_delta for this hop.
+ pub fn cltv_expiry_delta(&self) -> u32 {
match self {
CandidateRouteHop::FirstHop { .. } => 0,
CandidateRouteHop::PublicHop { info, .. } => info.direction().cltv_expiry_delta as u32,
- CandidateRouteHop::PrivateHop { hint } => hint.cltv_expiry_delta as u32,
+ CandidateRouteHop::PrivateHop { hint, .. } => hint.cltv_expiry_delta as u32,
CandidateRouteHop::Blinded { hint, .. } => hint.0.cltv_expiry_delta as u32,
CandidateRouteHop::OneHopBlinded { .. } => 0,
}
}
- fn htlc_minimum_msat(&self) -> u64 {
+ /// Returns the htlc_minimum_msat for this hop.
+ pub fn htlc_minimum_msat(&self) -> u64 {
match self {
- CandidateRouteHop::FirstHop { details } => details.next_outbound_htlc_minimum_msat,
+ CandidateRouteHop::FirstHop { details, .. } => details.next_outbound_htlc_minimum_msat,
CandidateRouteHop::PublicHop { info, .. } => info.direction().htlc_minimum_msat,
- CandidateRouteHop::PrivateHop { hint } => hint.htlc_minimum_msat.unwrap_or(0),
+ CandidateRouteHop::PrivateHop { hint, .. } => hint.htlc_minimum_msat.unwrap_or(0),
CandidateRouteHop::Blinded { hint, .. } => hint.0.htlc_minimum_msat,
CandidateRouteHop::OneHopBlinded { .. } => 0,
}
}
- fn fees(&self) -> RoutingFees {
+ /// Returns the fees for this hop.
+ pub fn fees(&self) -> RoutingFees {
match self {
CandidateRouteHop::FirstHop { .. } => RoutingFees {
base_msat: 0, proportional_millionths: 0,
},
CandidateRouteHop::PublicHop { info, .. } => info.direction().fees,
- CandidateRouteHop::PrivateHop { hint } => hint.fees,
+ CandidateRouteHop::PrivateHop { hint, .. } => hint.fees,
CandidateRouteHop::Blinded { hint, .. } => {
RoutingFees {
base_msat: hint.0.fee_base_msat,
fn effective_capacity(&self) -> EffectiveCapacity {
match self {
- CandidateRouteHop::FirstHop { details } => EffectiveCapacity::ExactLiquidity {
+ CandidateRouteHop::FirstHop { details, .. } => EffectiveCapacity::ExactLiquidity {
liquidity_msat: details.next_outbound_htlc_limit_msat,
},
CandidateRouteHop::PublicHop { info, .. } => info.effective_capacity(),
- CandidateRouteHop::PrivateHop { hint: RouteHintHop { htlc_maximum_msat: Some(max), .. }} =>
+ CandidateRouteHop::PrivateHop { hint: RouteHintHop { htlc_maximum_msat: Some(max), .. }, .. } =>
EffectiveCapacity::HintMaxHTLC { amount_msat: *max },
- CandidateRouteHop::PrivateHop { hint: RouteHintHop { htlc_maximum_msat: None, .. }} =>
+ CandidateRouteHop::PrivateHop { hint: RouteHintHop { htlc_maximum_msat: None, .. }, .. } =>
EffectiveCapacity::Infinite,
CandidateRouteHop::Blinded { hint, .. } =>
EffectiveCapacity::HintMaxHTLC { amount_msat: hint.0.htlc_maximum_msat },
}
}
- fn id(&self, channel_direction: bool /* src_node_id < target_node_id */) -> CandidateHopId {
+ /// Returns the id of this hop.
+ /// For `Blinded` and `OneHopBlinded` we return `CandidateHopId::Blinded` with `hint_idx` because we don't know the channel id.
+ /// For any other option we return `CandidateHopId::Clear` because we know the channel id and the direction.
+ pub fn id(&self) -> CandidateHopId {
match self {
CandidateRouteHop::Blinded { hint_idx, .. } => CandidateHopId::Blinded(*hint_idx),
CandidateRouteHop::OneHopBlinded { hint_idx, .. } => CandidateHopId::Blinded(*hint_idx),
- _ => CandidateHopId::Clear((self.short_channel_id().unwrap(), channel_direction)),
+ _ => CandidateHopId::Clear((self.short_channel_id().unwrap(), self.source() < self.target().unwrap())),
}
}
fn blinded_path(&self) -> Option<&'a BlindedPath> {
_ => None,
}
}
+ /// Returns the source node id of current hop.
+ ///
+ /// Source node id refers to the hop forwarding the payment.
+ ///
+ /// For `FirstHop` we return payer's node id.
+ pub fn source(&self) -> NodeId {
+ match self {
+ CandidateRouteHop::FirstHop { node_id, .. } => *node_id,
+ CandidateRouteHop::PublicHop { info, .. } => *info.source(),
+ CandidateRouteHop::PrivateHop { hint, .. } => hint.src_node_id.into(),
+ CandidateRouteHop::Blinded { hint, .. } => hint.1.introduction_node_id.into(),
+ CandidateRouteHop::OneHopBlinded { hint, .. } => hint.1.introduction_node_id.into(),
+ }
+ }
+ /// Returns the target node id of this hop, if known.
+ ///
+ /// Target node id refers to the hop receiving the payment.
+ ///
+ /// For `Blinded` and `OneHopBlinded` we return `None` because next hop is blinded.
+ pub fn target(&self) -> Option<NodeId> {
+ match self {
+ CandidateRouteHop::FirstHop { details, .. } => Some(details.counterparty.node_id.into()),
+ CandidateRouteHop::PublicHop { info, .. } => Some(*info.target()),
+ CandidateRouteHop::PrivateHop { target_node_id, .. } => Some(*target_node_id),
+ CandidateRouteHop::Blinded { .. } => None,
+ CandidateRouteHop::OneHopBlinded { .. } => None,
+ }
+ }
}
+/// A wrapper around the various hop id representations.
+///
+/// `CandidateHopId::Clear` is used to identify a hop with a known short channel id and direction.
+/// `CandidateHopId::Blinded` is used to identify a blinded hop `hint_idx`.
#[derive(Clone, Copy, Eq, Hash, Ord, PartialOrd, PartialEq)]
-enum CandidateHopId {
+pub enum CandidateHopId {
/// Contains (scid, src_node_id < target_node_id)
Clear((u64, bool)),
/// Index of the blinded route hint in [`Payee::Blinded::route_hints`].
/// These fee values are useful to choose hops as we traverse the graph "payee-to-payer".
#[derive(Clone)]
struct PathBuildingHop<'a> {
- // Note that this should be dropped in favor of loading it from CandidateRouteHop, but doing so
- // is a larger refactor and will require careful performance analysis.
- node_id: NodeId,
candidate: CandidateRouteHop<'a>,
fee_msat: u64,
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
let mut debug_struct = f.debug_struct("PathBuildingHop");
debug_struct
- .field("node_id", &self.node_id)
+ .field("node_id", &self.candidate.target())
.field("short_channel_id", &self.candidate.short_channel_id())
.field("total_fee_msat", &self.total_fee_msat)
.field("next_hops_fee_msat", &self.next_hops_fee_msat)
let mut num_ignored_htlc_minimum_msat_limit: u32 = 0;
macro_rules! add_entry {
- // Adds entry which goes from $src_node_id to $dest_node_id over the $candidate hop.
+ // Adds entry which goes from $candidate.source() to $candidate.target() over the $candidate hop.
// $next_hops_fee_msat represents the fees paid for using all the channels *after* this one,
// since that value has to be transferred over this channel.
// Returns the contribution amount of $candidate if the channel caused an update to `targets`.
- ( $candidate: expr, $src_node_id: expr, $dest_node_id: expr, $next_hops_fee_msat: expr,
+ ( $candidate: expr, $next_hops_fee_msat: expr,
$next_hops_value_contribution: expr, $next_hops_path_htlc_minimum_msat: expr,
$next_hops_path_penalty_msat: expr, $next_hops_cltv_delta: expr, $next_hops_path_length: expr ) => { {
// We "return" whether we updated the path at the end, and how much we can route via
// practice these cases should be caught earlier:
// - for regular channels at channel announcement (TODO)
// - for first and last hops early in get_route
- if $src_node_id != $dest_node_id {
+ let src_node_id = $candidate.source();
+ if Some(src_node_id) != $candidate.target() {
let scid_opt = $candidate.short_channel_id();
let effective_capacity = $candidate.effective_capacity();
let htlc_maximum_msat = max_htlc_from_capacity(effective_capacity, channel_saturation_pow_half);
// We do this for now, but this is a subject for removal.
if let Some(mut available_value_contribution_msat) = htlc_maximum_msat.checked_sub($next_hops_fee_msat) {
let used_liquidity_msat = used_liquidities
- .get(&$candidate.id($src_node_id < $dest_node_id))
+ .get(&$candidate.id())
.map_or(0, |used_liquidity_msat| {
available_value_contribution_msat = available_value_contribution_msat
.saturating_sub(*used_liquidity_msat);
|scid| payment_params.previously_failed_channels.contains(&scid));
let (should_log_candidate, first_hop_details) = match $candidate {
- CandidateRouteHop::FirstHop { details } => (true, Some(details)),
+ CandidateRouteHop::FirstHop { details, .. } => (true, Some(details)),
CandidateRouteHop::PrivateHop { .. } => (true, None),
CandidateRouteHop::Blinded { .. } => (true, None),
CandidateRouteHop::OneHopBlinded { .. } => (true, None),
);
let path_htlc_minimum_msat = compute_fees_saturating(curr_min, $candidate.fees())
.saturating_add(curr_min);
- let hm_entry = dist.entry($src_node_id);
+ let hm_entry = dist.entry(src_node_id);
let old_entry = hm_entry.or_insert_with(|| {
// If there was previously no known way to access the source node
// (recall it goes payee-to-payer) of short_channel_id, first add a
// semi-dummy record just to compute the fees to reach the source node.
// This will affect our decision on selecting short_channel_id
- // as a way to reach the $dest_node_id.
+ // as a way to reach the $candidate.target() node.
PathBuildingHop {
- node_id: $dest_node_id.clone(),
candidate: $candidate.clone(),
fee_msat: 0,
next_hops_fee_msat: u64::max_value(),
// Ignore hop_use_fee_msat for channel-from-us as we assume all channels-from-us
// will have the same effective-fee
- if $src_node_id != our_node_id {
+ if src_node_id != our_node_id {
// Note that `u64::max_value` means we'll always fail the
// `old_entry.total_fee_msat > total_fee_msat` check below
hop_use_fee_msat = compute_fees_saturating(amount_to_transfer_over_msat, $candidate.fees());
inflight_htlc_msat: used_liquidity_msat,
effective_capacity,
};
- let channel_penalty_msat = scid_opt.map_or(0,
- |scid| scorer.channel_penalty_msat(scid, &$src_node_id, &$dest_node_id,
- channel_usage, score_params));
+ let channel_penalty_msat =
+ scorer.channel_penalty_msat($candidate,
+ channel_usage,
+ score_params);
let path_penalty_msat = $next_hops_path_penalty_msat
.saturating_add(channel_penalty_msat);
let new_graph_node = RouteGraphNode {
- node_id: $src_node_id,
+ node_id: src_node_id,
lowest_fee_to_node: total_fee_msat,
total_cltv_delta: hop_total_cltv_delta,
value_contribution_msat,
path_length_to_node,
};
- // Update the way of reaching $src_node_id with the given short_channel_id (from $dest_node_id),
+ // Update the way of reaching $candidate.source()
+ // with the given short_channel_id (from $candidate.target()),
// if this way is cheaper than the already known
// (considering the cost to "reach" this channel from the route destination,
// the cost of using this channel,
old_entry.next_hops_fee_msat = $next_hops_fee_msat;
old_entry.hop_use_fee_msat = hop_use_fee_msat;
old_entry.total_fee_msat = total_fee_msat;
- old_entry.node_id = $dest_node_id.clone();
old_entry.candidate = $candidate.clone();
old_entry.fee_msat = 0; // This value will be later filled with hop_use_fee_msat of the following channel
old_entry.path_htlc_minimum_msat = path_htlc_minimum_msat;
if !skip_node {
if let Some(first_channels) = first_hop_targets.get(&$node_id) {
for details in first_channels {
- let candidate = CandidateRouteHop::FirstHop { details };
- add_entry!(candidate, our_node_id, $node_id, $fee_to_target_msat,
+ let candidate = CandidateRouteHop::FirstHop { details, node_id: our_node_id };
+ add_entry!(&candidate, $fee_to_target_msat,
$next_hops_value_contribution,
$next_hops_path_htlc_minimum_msat, $next_hops_path_penalty_msat,
$next_hops_cltv_delta, $next_hops_path_length);
info: directed_channel,
short_channel_id: *chan_id,
};
- add_entry!(candidate, *source, $node_id,
+ add_entry!(&candidate,
$fee_to_target_msat,
$next_hops_value_contribution,
$next_hops_path_htlc_minimum_msat,
// place where it could be added.
payee_node_id_opt.map(|payee| first_hop_targets.get(&payee).map(|first_channels| {
for details in first_channels {
- let candidate = CandidateRouteHop::FirstHop { details };
- let added = add_entry!(candidate, our_node_id, payee, 0, path_value_msat,
+ let candidate = CandidateRouteHop::FirstHop { details, node_id: our_node_id };
+ let added = add_entry!(&candidate, 0, path_value_msat,
0, 0u64, 0, 0).is_some();
log_trace!(logger, "{} direct route to payee via {}",
if added { "Added" } else { "Skipped" }, LoggedCandidateHop(&candidate));
CandidateRouteHop::OneHopBlinded { hint, hint_idx }
} else { CandidateRouteHop::Blinded { hint, hint_idx } };
let mut path_contribution_msat = path_value_msat;
- if let Some(hop_used_msat) = add_entry!(candidate, intro_node_id, maybe_dummy_payee_node_id,
+ if let Some(hop_used_msat) = add_entry!(&candidate,
0, path_contribution_msat, 0, 0_u64, 0, 0)
{
path_contribution_msat = hop_used_msat;
sort_first_hop_channels(first_channels, &used_liquidities, recommended_value_msat,
our_node_pubkey);
for details in first_channels {
- let first_hop_candidate = CandidateRouteHop::FirstHop { details };
+ let first_hop_candidate = CandidateRouteHop::FirstHop { details, node_id: our_node_id};
let blinded_path_fee = match compute_fees(path_contribution_msat, candidate.fees()) {
Some(fee) => fee,
None => continue
};
let path_min = candidate.htlc_minimum_msat().saturating_add(
compute_fees_saturating(candidate.htlc_minimum_msat(), candidate.fees()));
- add_entry!(first_hop_candidate, our_node_id, intro_node_id, blinded_path_fee,
+ add_entry!(&first_hop_candidate, blinded_path_fee,
path_contribution_msat, path_min, 0_u64, candidate.cltv_expiry_delta(),
candidate.blinded_path().map_or(1, |bp| bp.blinded_hops.len() as u8));
}
info,
short_channel_id: hop.short_channel_id,
})
- .unwrap_or_else(|| CandidateRouteHop::PrivateHop { hint: hop });
+ .unwrap_or_else(|| CandidateRouteHop::PrivateHop { hint: hop, target_node_id: target });
- if let Some(hop_used_msat) = add_entry!(candidate, source, target,
+ if let Some(hop_used_msat) = add_entry!(&candidate,
aggregate_next_hops_fee_msat, aggregate_path_contribution_msat,
aggregate_next_hops_path_htlc_minimum_msat, aggregate_next_hops_path_penalty_msat,
aggregate_next_hops_cltv_delta, aggregate_next_hops_path_length)
}
let used_liquidity_msat = used_liquidities
- .get(&candidate.id(source < target)).copied()
+ .get(&candidate.id()).copied()
.unwrap_or(0);
let channel_usage = ChannelUsage {
amount_msat: final_value_msat + aggregate_next_hops_fee_msat,
effective_capacity: candidate.effective_capacity(),
};
let channel_penalty_msat = scorer.channel_penalty_msat(
- hop.short_channel_id, &source, &target, channel_usage, score_params
+ &candidate, channel_usage, score_params
);
aggregate_next_hops_path_penalty_msat = aggregate_next_hops_path_penalty_msat
.saturating_add(channel_penalty_msat);
sort_first_hop_channels(first_channels, &used_liquidities,
recommended_value_msat, our_node_pubkey);
for details in first_channels {
- let first_hop_candidate = CandidateRouteHop::FirstHop { details };
- add_entry!(first_hop_candidate, our_node_id, target,
+ let first_hop_candidate = CandidateRouteHop::FirstHop { details, node_id: our_node_id};
+ add_entry!(&first_hop_candidate,
aggregate_next_hops_fee_msat, aggregate_path_contribution_msat,
aggregate_next_hops_path_htlc_minimum_msat, aggregate_next_hops_path_penalty_msat,
aggregate_next_hops_cltv_delta, aggregate_next_hops_path_length);
sort_first_hop_channels(first_channels, &used_liquidities,
recommended_value_msat, our_node_pubkey);
for details in first_channels {
- let first_hop_candidate = CandidateRouteHop::FirstHop { details };
- add_entry!(first_hop_candidate, our_node_id,
- NodeId::from_pubkey(&hop.src_node_id),
+ let first_hop_candidate = CandidateRouteHop::FirstHop { details, node_id: our_node_id};
+ add_entry!(&first_hop_candidate,
aggregate_next_hops_fee_msat,
aggregate_path_contribution_msat,
aggregate_next_hops_path_htlc_minimum_msat,
'path_walk: loop {
let mut features_set = false;
- if let Some(first_channels) = first_hop_targets.get(&ordered_hops.last().unwrap().0.node_id) {
+ let target = ordered_hops.last().unwrap().0.candidate.target().unwrap_or(maybe_dummy_payee_node_id);
+ if let Some(first_channels) = first_hop_targets.get(&target) {
for details in first_channels {
if let Some(scid) = ordered_hops.last().unwrap().0.candidate.short_channel_id() {
if details.get_outbound_payment_scid().unwrap() == scid {
}
}
if !features_set {
- if let Some(node) = network_nodes.get(&ordered_hops.last().unwrap().0.node_id) {
+ if let Some(node) = network_nodes.get(&target) {
if let Some(node_info) = node.announcement_info.as_ref() {
ordered_hops.last_mut().unwrap().1 = node_info.features.clone();
} else {
// save this path for the payment route. Also, update the liquidity
// remaining on the used hops, so that we take them into account
// while looking for more paths.
- if ordered_hops.last().unwrap().0.node_id == maybe_dummy_payee_node_id {
+ if target == maybe_dummy_payee_node_id {
break 'path_walk;
}
- new_entry = match dist.remove(&ordered_hops.last().unwrap().0.node_id) {
+ new_entry = match dist.remove(&target) {
Some(payment_hop) => payment_hop,
// We can't arrive at None because, if we ever add an entry to targets,
// we also fill in the entry in dist (see add_entry!).
// Remember that we used these channels so that we don't rely
// on the same liquidity in future paths.
let mut prevented_redundant_path_selection = false;
- let prev_hop_iter = core::iter::once(&our_node_id)
- .chain(payment_path.hops.iter().map(|(hop, _)| &hop.node_id));
- for (prev_hop, (hop, _)) in prev_hop_iter.zip(payment_path.hops.iter()) {
+ for (hop, _) in payment_path.hops.iter() {
let spent_on_hop_msat = value_contribution_msat + hop.next_hops_fee_msat;
let used_liquidity_msat = used_liquidities
- .entry(hop.candidate.id(*prev_hop < hop.node_id))
+ .entry(hop.candidate.id())
.and_modify(|used_liquidity_msat| *used_liquidity_msat += spent_on_hop_msat)
.or_insert(spent_on_hop_msat);
let hop_capacity = hop.candidate.effective_capacity();
log_trace!(logger,
"Disabling route candidate {} for future path building iterations to avoid duplicates.",
LoggedCandidateHop(victim_candidate));
- *used_liquidities.entry(victim_candidate.id(false)).or_default() = exhausted;
- *used_liquidities.entry(victim_candidate.id(true)).or_default() = exhausted;
+ if let Some(scid) = victim_candidate.short_channel_id() {
+ *used_liquidities.entry(CandidateHopId::Clear((scid, false))).or_default() = exhausted;
+ *used_liquidities.entry(CandidateHopId::Clear((scid, true))).or_default() = exhausted;
+ }
}
// Track the total amount all our collected paths allow to send so that we know
selected_route.sort_unstable_by_key(|path| {
let mut key = [CandidateHopId::Clear((42, true)) ; MAX_PATH_LENGTH_ESTIMATE as usize];
debug_assert!(path.hops.len() <= key.len());
- for (scid, key) in path.hops.iter() .map(|h| h.0.candidate.id(true)).zip(key.iter_mut()) {
+ for (scid, key) in path.hops.iter() .map(|h| h.0.candidate.id()).zip(key.iter_mut()) {
*key = scid;
}
key
});
for idx in 0..(selected_route.len() - 1) {
if idx + 1 >= selected_route.len() { break; }
- if iter_equal(selected_route[idx ].hops.iter().map(|h| (h.0.candidate.id(true), h.0.node_id)),
- selected_route[idx + 1].hops.iter().map(|h| (h.0.candidate.id(true), h.0.node_id))) {
+ if iter_equal(selected_route[idx].hops.iter().map(|h| (h.0.candidate.id(), h.0.candidate.target())),
+ selected_route[idx + 1].hops.iter().map(|h| (h.0.candidate.id(), h.0.candidate.target()))) {
let new_value = selected_route[idx].get_value_msat() + selected_route[idx + 1].get_value_msat();
selected_route[idx].update_value_and_recompute_fees(new_value);
selected_route.remove(idx + 1);
let mut paths = Vec::new();
for payment_path in selected_route {
let mut hops = Vec::with_capacity(payment_path.hops.len());
- let mut prev_hop_node_id = our_node_id;
for (hop, node_features) in payment_path.hops.iter()
.filter(|(h, _)| h.candidate.short_channel_id().is_some())
{
+ let target = hop.candidate.target().expect("target is defined when short_channel_id is defined");
let maybe_announced_channel = if let CandidateRouteHop::PublicHop { .. } = hop.candidate {
// If we sourced the hop from the graph we're sure the target node is announced.
true
- } else if let CandidateRouteHop::FirstHop { details } = hop.candidate {
+ } else if let CandidateRouteHop::FirstHop { details, .. } = hop.candidate {
// If this is a first hop we also know if it's announced.
details.is_public
} else {
// there are announced channels between the endpoints. If so, the hop might be
// referring to any of the announced channels, as its `short_channel_id` might be
// an alias, in which case we don't take any chances here.
- network_graph.node(&hop.node_id).map_or(false, |hop_node|
+ network_graph.node(&target).map_or(false, |hop_node|
hop_node.channels.iter().any(|scid| network_graph.channel(*scid)
- .map_or(false, |c| c.as_directed_from(&prev_hop_node_id).is_some()))
+ .map_or(false, |c| c.as_directed_from(&hop.candidate.source()).is_some()))
)
};
hops.push(RouteHop {
- pubkey: PublicKey::from_slice(hop.node_id.as_slice()).map_err(|_| LightningError{err: format!("Public key {:?} is invalid", &hop.node_id), action: ErrorAction::IgnoreAndLog(Level::Trace)})?,
+ pubkey: PublicKey::from_slice(target.as_slice()).map_err(|_| LightningError{err: format!("Public key {:?} is invalid", &target), action: ErrorAction::IgnoreAndLog(Level::Trace)})?,
node_features: node_features.clone(),
short_channel_id: hop.candidate.short_channel_id().unwrap(),
channel_features: hop.candidate.features(),
cltv_expiry_delta: hop.candidate.cltv_expiry_delta(),
maybe_announced_channel,
});
-
- prev_hop_node_id = hop.node_id;
}
let mut final_cltv_delta = final_cltv_expiry_delta;
let blinded_tail = payment_path.hops.last().and_then(|(h, _)| {
impl ScoreLookUp for HopScorer {
type ScoreParams = ();
- fn channel_penalty_msat(&self, _short_channel_id: u64, source: &NodeId, target: &NodeId,
+ fn channel_penalty_msat(&self, candidate: &CandidateRouteHop,
_usage: ChannelUsage, _score_params: &Self::ScoreParams) -> u64
{
let mut cur_id = self.our_node_id;
for i in 0..self.hop_ids.len() {
if let Some(next_id) = self.hop_ids[i] {
- if cur_id == *source && next_id == *target {
+ if cur_id == candidate.source() && Some(next_id) == candidate.target() {
return 0;
}
cur_id = next_id;
use crate::routing::utxo::UtxoResult;
use crate::routing::router::{get_route, build_route_from_hops_internal, add_random_cltv_offset, default_node_features,
BlindedTail, InFlightHtlcs, Path, PaymentParameters, Route, RouteHint, RouteHintHop, RouteHop, RoutingFees,
- DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, MAX_PATH_LENGTH_ESTIMATE, RouteParameters};
+ DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA, MAX_PATH_LENGTH_ESTIMATE, RouteParameters, CandidateRouteHop};
use crate::routing::scoring::{ChannelUsage, FixedPenaltyScorer, ScoreLookUp, ProbabilisticScorer, ProbabilisticScoringFeeParameters, ProbabilisticScoringDecayParameters};
use crate::routing::test_utils::{add_channel, add_or_update_node, build_graph, build_line_graph, id_to_feature_flags, get_nodes, update_channel};
use crate::chain::transaction::OutPoint;
}
impl ScoreLookUp for BadChannelScorer {
type ScoreParams = ();
- fn channel_penalty_msat(&self, short_channel_id: u64, _: &NodeId, _: &NodeId, _: ChannelUsage, _score_params:&Self::ScoreParams) -> u64 {
- if short_channel_id == self.short_channel_id { u64::max_value() } else { 0 }
+ fn channel_penalty_msat(&self, candidate: &CandidateRouteHop, _: ChannelUsage, _score_params:&Self::ScoreParams) -> u64 {
+ if candidate.short_channel_id() == Some(self.short_channel_id) { u64::max_value() } else { 0 }
}
}
impl ScoreLookUp for BadNodeScorer {
type ScoreParams = ();
- fn channel_penalty_msat(&self, _: u64, _: &NodeId, target: &NodeId, _: ChannelUsage, _score_params:&Self::ScoreParams) -> u64 {
- if *target == self.node_id { u64::max_value() } else { 0 }
+ fn channel_penalty_msat(&self, candidate: &CandidateRouteHop, _: ChannelUsage, _score_params:&Self::ScoreParams) -> u64 {
+ if candidate.target() == Some(self.node_id) { u64::max_value() } else { 0 }
}
}
};
scorer_params.set_manual_penalty(&NodeId::from_pubkey(&nodes[3]), 123);
scorer_params.set_manual_penalty(&NodeId::from_pubkey(&nodes[4]), 456);
- assert_eq!(scorer.channel_penalty_msat(42, &NodeId::from_pubkey(&nodes[3]), &NodeId::from_pubkey(&nodes[4]), usage, &scorer_params), 456);
+ let network_graph = network_graph.read_only();
+ let channels = network_graph.channels();
+ let channel = channels.get(&5).unwrap();
+ let info = channel.as_directed_from(&NodeId::from_pubkey(&nodes[3])).unwrap();
+ let candidate: CandidateRouteHop = CandidateRouteHop::PublicHop {
+ info: info.0,
+ short_channel_id: 5,
+ };
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, &scorer_params), 456);
// Then check we can get a normal route
let payment_params = PaymentParameters::from_node_id(nodes[10], 42);
let route_params = RouteParameters::from_payment_params_and_value(
payment_params, 100);
- let route = get_route(&our_id, &route_params, &network_graph.read_only(), None,
+ let route = get_route(&our_id, &route_params, &network_graph, None,
Arc::clone(&logger), &scorer, &scorer_params, &random_seed_bytes);
assert!(route.is_ok());
// Then check that we can't get a route if we ban an intermediate node.
scorer_params.add_banned(&NodeId::from_pubkey(&nodes[3]));
- let route = get_route(&our_id, &route_params, &network_graph.read_only(), None,
- Arc::clone(&logger), &scorer, &scorer_params, &random_seed_bytes);
+ let route = get_route(&our_id, &route_params, &network_graph, None, Arc::clone(&logger), &scorer, &scorer_params,&random_seed_bytes);
assert!(route.is_err());
// Finally make sure we can route again, when we remove the ban.
scorer_params.remove_banned(&NodeId::from_pubkey(&nodes[3]));
- let route = get_route(&our_id, &route_params, &network_graph.read_only(), None,
- Arc::clone(&logger), &scorer, &scorer_params, &random_seed_bytes);
+ let route = get_route(&our_id, &route_params, &network_graph, None, Arc::clone(&logger), &scorer, &scorer_params,&random_seed_bytes);
assert!(route.is_ok());
}
use crate::ln::msgs::DecodeError;
use crate::routing::gossip::{EffectiveCapacity, NetworkGraph, NodeId};
-use crate::routing::router::Path;
+use crate::routing::router::{Path, CandidateRouteHop};
use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer};
use crate::util::logger::Logger;
use crate::util::time::Time;
/// [`u64::max_value`] is given to indicate sufficient capacity for the invoice's full amount.
/// Thus, implementations should be overflow-safe.
fn channel_penalty_msat(
- &self, short_channel_id: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage, score_params: &Self::ScoreParams
+ &self, candidate: &CandidateRouteHop, usage: ChannelUsage, score_params: &Self::ScoreParams
) -> u64;
}
impl<S: ScoreLookUp, T: Deref<Target=S>> ScoreLookUp for T {
type ScoreParams = S::ScoreParams;
fn channel_penalty_msat(
- &self, short_channel_id: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage, score_params: &Self::ScoreParams
+ &self, candidate: &CandidateRouteHop, usage: ChannelUsage, score_params: &Self::ScoreParams
) -> u64 {
- self.deref().channel_penalty_msat(short_channel_id, source, target, usage, score_params)
+ self.deref().channel_penalty_msat(candidate, usage, score_params)
}
}
#[cfg(c_bindings)]
impl<'a, T: Score> ScoreLookUp for MultiThreadedScoreLockRead<'a, T> {
type ScoreParams = T::ScoreParams;
- fn channel_penalty_msat(&self, short_channel_id: u64, source: &NodeId,
- target: &NodeId, usage: ChannelUsage, score_params: &Self::ScoreParams
+ fn channel_penalty_msat(&self, candidate:&CandidateRouteHop, usage: ChannelUsage, score_params: &Self::ScoreParams
) -> u64 {
- self.0.channel_penalty_msat(short_channel_id, source, target, usage, score_params)
+ self.0.channel_penalty_msat(candidate, usage, score_params)
}
}
impl ScoreLookUp for FixedPenaltyScorer {
type ScoreParams = ();
- fn channel_penalty_msat(&self, _: u64, _: &NodeId, _: &NodeId, _: ChannelUsage, _score_params: &Self::ScoreParams) -> u64 {
+ fn channel_penalty_msat(&self, _: &CandidateRouteHop, _: ChannelUsage, _score_params: &Self::ScoreParams) -> u64 {
self.penalty_msat
}
}
impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, T: Time> ScoreLookUp for ProbabilisticScorerUsingTime<G, L, T> where L::Target: Logger {
type ScoreParams = ProbabilisticScoringFeeParameters;
fn channel_penalty_msat(
- &self, short_channel_id: u64, source: &NodeId, target: &NodeId, usage: ChannelUsage, score_params: &ProbabilisticScoringFeeParameters
+ &self, candidate: &CandidateRouteHop, usage: ChannelUsage, score_params: &ProbabilisticScoringFeeParameters
) -> u64 {
- if let Some(penalty) = score_params.manual_node_penalties.get(target) {
+ let scid = match candidate.short_channel_id() {
+ Some(scid) => scid,
+ None => return 0,
+ };
+ let target = match candidate.target() {
+ Some(target) => target,
+ None => return 0,
+ };
+ let source = candidate.source();
+ if let Some(penalty) = score_params.manual_node_penalties.get(&target) {
return *penalty;
}
let amount_msat = usage.amount_msat.saturating_add(usage.inflight_htlc_msat);
let capacity_msat = usage.effective_capacity.as_msat();
self.channel_liquidities
- .get(&short_channel_id)
+ .get(&scid)
.unwrap_or(&ChannelLiquidity::new())
- .as_directed(source, target, capacity_msat, self.decay_params)
+ .as_directed(&source, &target, capacity_msat, self.decay_params)
.penalty_msat(amount_msat, score_params)
.saturating_add(anti_probing_penalty_msat)
.saturating_add(base_penalty_msat)
use crate::ln::channelmanager;
use crate::ln::msgs::{ChannelAnnouncement, ChannelUpdate, UnsignedChannelAnnouncement, UnsignedChannelUpdate};
use crate::routing::gossip::{EffectiveCapacity, NetworkGraph, NodeId};
- use crate::routing::router::{BlindedTail, Path, RouteHop};
+ use crate::routing::router::{BlindedTail, Path, RouteHop, CandidateRouteHop};
use crate::routing::scoring::{ChannelUsage, ScoreLookUp, ScoreUpdate};
use crate::util::ser::{ReadableArgs, Writeable};
use crate::util::test_utils::{self, TestLogger};
let decay_params = ProbabilisticScoringDecayParameters::default();
let scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
let source = source_node_id();
- let target = target_node_id();
let usage = ChannelUsage {
amount_msat: 1_024,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: 1_000 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
+ let network_graph = network_graph.read_only();
+ let channel = network_graph.channel(42).unwrap();
+ let (info, _) = channel.as_directed_from(&source).unwrap();
+ let candidate = CandidateRouteHop::PublicHop {
+ info,
+ short_channel_id: 42,
+ };
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 0);
let usage = ChannelUsage { amount_msat: 10_240, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 0);
let usage = ChannelUsage { amount_msat: 102_400, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 47);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 47);
let usage = ChannelUsage { amount_msat: 1_023_999, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 2_000);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 2_000);
let usage = ChannelUsage {
amount_msat: 128,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_000 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 58);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 58);
let usage = ChannelUsage { amount_msat: 256, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 125);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 125);
let usage = ChannelUsage { amount_msat: 374, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 198);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 198);
let usage = ChannelUsage { amount_msat: 512, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 300);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 300);
let usage = ChannelUsage { amount_msat: 640, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 425);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 425);
let usage = ChannelUsage { amount_msat: 768, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 602);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 602);
let usage = ChannelUsage { amount_msat: 896, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 902);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 902);
}
#[test]
max_liquidity_offset_history: HistoricalBucketRangeTracker::new(),
});
let source = source_node_id();
- let target = target_node_id();
let usage = ChannelUsage {
amount_msat: 39,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 100, htlc_maximum_msat: 1_000 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
+ let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+ let (info, _) = channel.as_directed_from(&source).unwrap();
+ let candidate = CandidateRouteHop::PublicHop {
+ info,
+ short_channel_id: 42,
+ };
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 0);
let usage = ChannelUsage { amount_msat: 50, ..usage };
- assert_ne!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
- assert_ne!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
+ assert_ne!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 0);
+ assert_ne!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), u64::max_value());
let usage = ChannelUsage { amount_msat: 61, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), u64::max_value());
}
#[test]
..ProbabilisticScoringFeeParameters::zero_penalty()
};
let mut scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
- let sender = sender_node_id();
let source = source_node_id();
let usage = ChannelUsage {
amount_msat: 500,
};
let failed_path = payment_path_for_amount(500);
let successful_path = payment_path_for_amount(200);
+ let channel = &network_graph.read_only().channel(42).unwrap().to_owned();
+ let (info, _) = channel.as_directed_from(&source).unwrap();
+ let candidate = CandidateRouteHop::PublicHop {
+ info,
+ short_channel_id: 41,
+ };
- assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage, ¶ms), 301);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 301);
scorer.payment_path_failed(&failed_path, 41);
- assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage, ¶ms), 301);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 301);
scorer.payment_path_successful(&successful_path);
- assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage, ¶ms), 301);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 301);
}
#[test]
};
let mut scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
let source = source_node_id();
- let target = target_node_id();
let path = payment_path_for_amount(500);
let usage = ChannelUsage {
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 128);
+ let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+ let (info, _) = channel.as_directed_from(&source).unwrap();
+ let candidate = CandidateRouteHop::PublicHop {
+ info,
+ short_channel_id: 42,
+ };
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 128);
let usage = ChannelUsage { amount_msat: 500, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 301);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 301);
let usage = ChannelUsage { amount_msat: 750, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 602);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 602);
scorer.payment_path_failed(&path, 43);
let usage = ChannelUsage { amount_msat: 250, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 0);
let usage = ChannelUsage { amount_msat: 500, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 0);
let usage = ChannelUsage { amount_msat: 750, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 300);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 300);
}
#[test]
};
let mut scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
let source = source_node_id();
- let target = target_node_id();
let path = payment_path_for_amount(500);
let usage = ChannelUsage {
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 128);
+ let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+ let (info, _) = channel.as_directed_from(&source).unwrap();
+ let candidate = CandidateRouteHop::PublicHop {
+ info,
+ short_channel_id: 42,
+ };
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 128);
let usage = ChannelUsage { amount_msat: 500, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 301);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 301);
let usage = ChannelUsage { amount_msat: 750, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 602);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 602);
scorer.payment_path_failed(&path, 42);
let usage = ChannelUsage { amount_msat: 250, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 300);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 300);
let usage = ChannelUsage { amount_msat: 500, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), u64::max_value());
let usage = ChannelUsage { amount_msat: 750, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), u64::max_value());
}
#[test]
let node_a = NodeId::from_pubkey(&pub_a);
let node_b = NodeId::from_pubkey(&pub_b);
let node_c = NodeId::from_pubkey(&pub_c);
- let node_d = NodeId::from_pubkey(&pub_d);
let params = ProbabilisticScoringFeeParameters {
liquidity_penalty_multiplier_msat: 1_000,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &node_a, &node_b, usage, ¶ms), 128);
+ let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+ let (info, _) = channel.as_directed_from(&node_a).unwrap();
+ let candidate = CandidateRouteHop::PublicHop {
+ info,
+ short_channel_id: 42,
+ };
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 128);
// Note that a default liquidity bound is used for B -> C as no channel exists
- assert_eq!(scorer.channel_penalty_msat(43, &node_b, &node_c, usage, ¶ms), 128);
- assert_eq!(scorer.channel_penalty_msat(44, &node_c, &node_d, usage, ¶ms), 128);
+ let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+ let (info, _) = channel.as_directed_from(&node_b).unwrap();
+ let candidate = CandidateRouteHop::PublicHop {
+ info,
+ short_channel_id: 43,
+ };
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 128);
+ let channel = network_graph.read_only().channel(44).unwrap().to_owned();
+ let (info, _) = channel.as_directed_from(&node_c).unwrap();
+ let candidate = CandidateRouteHop::PublicHop {
+ info,
+ short_channel_id: 44,
+ };
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 128);
scorer.payment_path_failed(&Path { hops: path, blinded_tail: None }, 43);
- assert_eq!(scorer.channel_penalty_msat(42, &node_a, &node_b, usage, ¶ms), 80);
+ let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+ let (info, _) = channel.as_directed_from(&node_a).unwrap();
+ let candidate = CandidateRouteHop::PublicHop {
+ info,
+ short_channel_id: 42,
+ };
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 80);
// Note that a default liquidity bound is used for B -> C as no channel exists
- assert_eq!(scorer.channel_penalty_msat(43, &node_b, &node_c, usage, ¶ms), 128);
- assert_eq!(scorer.channel_penalty_msat(44, &node_c, &node_d, usage, ¶ms), 128);
+ let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+ let (info, _) = channel.as_directed_from(&node_b).unwrap();
+ let candidate = CandidateRouteHop::PublicHop {
+ info,
+ short_channel_id: 43,
+ };
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 128);
+ let channel = network_graph.read_only().channel(44).unwrap().to_owned();
+ let (info, _) = channel.as_directed_from(&node_c).unwrap();
+ let candidate = CandidateRouteHop::PublicHop {
+ info,
+ short_channel_id: 44,
+ };
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 128);
}
#[test]
..ProbabilisticScoringFeeParameters::zero_penalty()
};
let mut scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
- let sender = sender_node_id();
let source = source_node_id();
- let target = target_node_id();
- let recipient = recipient_node_id();
let usage = ChannelUsage {
amount_msat: 250,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 },
};
-
- assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage, ¶ms), 128);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 128);
- assert_eq!(scorer.channel_penalty_msat(43, &target, &recipient, usage, ¶ms), 128);
+ let network_graph = network_graph.read_only().channels().clone();
+ let channel_42 = network_graph.get(&42).unwrap();
+ let channel_43 = network_graph.get(&43).unwrap();
+ let (info, _) = channel_42.as_directed_from(&source).unwrap();
+ let candidate_41 = CandidateRouteHop::PublicHop {
+ info,
+ short_channel_id: 41,
+ };
+ let (info, target) = channel_42.as_directed_from(&source).unwrap();
+ let candidate_42 = CandidateRouteHop::PublicHop {
+ info,
+ short_channel_id: 42,
+ };
+ let (info, _) = channel_43.as_directed_from(&target).unwrap();
+ let candidate_43 = CandidateRouteHop::PublicHop {
+ info,
+ short_channel_id: 43,
+ };
+ assert_eq!(scorer.channel_penalty_msat(&candidate_41, usage, ¶ms), 128);
+ assert_eq!(scorer.channel_penalty_msat(&candidate_42, usage, ¶ms), 128);
+ assert_eq!(scorer.channel_penalty_msat(&candidate_43, usage, ¶ms), 128);
scorer.payment_path_successful(&payment_path_for_amount(500));
- assert_eq!(scorer.channel_penalty_msat(41, &sender, &source, usage, ¶ms), 128);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 300);
- assert_eq!(scorer.channel_penalty_msat(43, &target, &recipient, usage, ¶ms), 300);
+ assert_eq!(scorer.channel_penalty_msat(&candidate_41, usage, ¶ms), 128);
+ assert_eq!(scorer.channel_penalty_msat(&candidate_42, usage, ¶ms), 300);
+ assert_eq!(scorer.channel_penalty_msat(&candidate_43, usage, ¶ms), 300);
}
#[test]
};
let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
let source = source_node_id();
- let target = target_node_id();
let usage = ChannelUsage {
amount_msat: 0,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_024 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
+ let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+ let (info, target) = channel.as_directed_from(&source).unwrap();
+ let candidate = CandidateRouteHop::PublicHop {
+ info,
+ short_channel_id: 42,
+ };
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 0);
let usage = ChannelUsage { amount_msat: 1_023, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 2_000);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 2_000);
scorer.payment_path_failed(&payment_path_for_amount(768), 42);
scorer.payment_path_failed(&payment_path_for_amount(128), 43);
// Initial penalties
let usage = ChannelUsage { amount_msat: 128, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 0);
let usage = ChannelUsage { amount_msat: 256, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 93);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 93);
let usage = ChannelUsage { amount_msat: 768, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 1_479);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 1_479);
let usage = ChannelUsage { amount_msat: 896, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), u64::max_value());
// No decay
SinceEpoch::advance(Duration::from_secs(4));
let usage = ChannelUsage { amount_msat: 128, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 0);
let usage = ChannelUsage { amount_msat: 256, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 93);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 93);
let usage = ChannelUsage { amount_msat: 768, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 1_479);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 1_479);
let usage = ChannelUsage { amount_msat: 896, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), u64::max_value());
// Half decay (i.e., three-quarter life)
SinceEpoch::advance(Duration::from_secs(1));
let usage = ChannelUsage { amount_msat: 128, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 22);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 22);
let usage = ChannelUsage { amount_msat: 256, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 106);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 106);
let usage = ChannelUsage { amount_msat: 768, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 921);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 921);
let usage = ChannelUsage { amount_msat: 896, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), u64::max_value());
// One decay (i.e., half life)
SinceEpoch::advance(Duration::from_secs(5));
let usage = ChannelUsage { amount_msat: 64, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 0);
let usage = ChannelUsage { amount_msat: 128, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 34);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 34);
let usage = ChannelUsage { amount_msat: 896, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 1_970);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 1_970);
let usage = ChannelUsage { amount_msat: 960, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), u64::max_value());
// Fully decay liquidity lower bound.
SinceEpoch::advance(Duration::from_secs(10 * 7));
let usage = ChannelUsage { amount_msat: 0, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 0);
let usage = ChannelUsage { amount_msat: 1, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 0);
let usage = ChannelUsage { amount_msat: 1_023, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 2_000);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 2_000);
let usage = ChannelUsage { amount_msat: 1_024, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), u64::max_value());
// Fully decay liquidity upper bound.
SinceEpoch::advance(Duration::from_secs(10));
let usage = ChannelUsage { amount_msat: 0, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 0);
let usage = ChannelUsage { amount_msat: 1_024, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), u64::max_value());
SinceEpoch::advance(Duration::from_secs(10));
let usage = ChannelUsage { amount_msat: 0, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 0);
let usage = ChannelUsage { amount_msat: 1_024, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), u64::max_value());
}
#[test]
};
let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
let source = source_node_id();
- let target = target_node_id();
let usage = ChannelUsage {
amount_msat: 256,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_000 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 125);
+ let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+ let (info, target) = channel.as_directed_from(&source).unwrap();
+ let candidate = CandidateRouteHop::PublicHop {
+ info,
+ short_channel_id: 42,
+ };
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 125);
scorer.payment_path_failed(&payment_path_for_amount(512), 42);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 281);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 281);
// An unchecked right shift 64 bits or more in DirectedChannelLiquidity::decayed_offset_msat
// would cause an overflow.
SinceEpoch::advance(Duration::from_secs(10 * 64));
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 125);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 125);
SinceEpoch::advance(Duration::from_secs(10));
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 125);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 125);
}
#[test]
};
let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
let source = source_node_id();
- let target = target_node_id();
let usage = ChannelUsage {
amount_msat: 512,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_000 },
};
+ let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+ let (info, _) = channel.as_directed_from(&source).unwrap();
+ let candidate = CandidateRouteHop::PublicHop {
+ info,
+ short_channel_id: 42,
+ };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 300);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 300);
// More knowledge gives higher confidence (256, 768), meaning a lower penalty.
scorer.payment_path_failed(&payment_path_for_amount(768), 42);
scorer.payment_path_failed(&payment_path_for_amount(256), 43);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 281);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 281);
// Decaying knowledge gives less confidence (128, 896), meaning a higher penalty.
SinceEpoch::advance(Duration::from_secs(10));
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 291);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 291);
// Reducing the upper bound gives more confidence (128, 832) that the payment amount (512)
// is closer to the upper bound, meaning a higher penalty.
scorer.payment_path_successful(&payment_path_for_amount(64));
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 331);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 331);
// Increasing the lower bound gives more confidence (256, 832) that the payment amount (512)
// is closer to the lower bound, meaning a lower penalty.
scorer.payment_path_failed(&payment_path_for_amount(256), 43);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 245);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 245);
// Further decaying affects the lower bound more than the upper bound (128, 928).
SinceEpoch::advance(Duration::from_secs(10));
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 280);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 280);
}
#[test]
};
let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
let source = source_node_id();
- let target = target_node_id();
let usage = ChannelUsage {
amount_msat: 500,
inflight_htlc_msat: 0,
};
scorer.payment_path_failed(&payment_path_for_amount(500), 42);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
+ let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+ let (info, _) = channel.as_directed_from(&source).unwrap();
+ let candidate = CandidateRouteHop::PublicHop {
+ info,
+ short_channel_id: 42,
+ };
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), u64::max_value());
SinceEpoch::advance(Duration::from_secs(10));
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 473);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 473);
scorer.payment_path_failed(&payment_path_for_amount(250), 43);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 300);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 300);
let mut serialized_scorer = Vec::new();
scorer.write(&mut serialized_scorer).unwrap();
let mut serialized_scorer = io::Cursor::new(&serialized_scorer);
let deserialized_scorer =
<ProbabilisticScorer>::read(&mut serialized_scorer, (decay_params, &network_graph, &logger)).unwrap();
- assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 300);
+ assert_eq!(deserialized_scorer.channel_penalty_msat(&candidate, usage, ¶ms), 300);
}
#[test]
};
let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
let source = source_node_id();
- let target = target_node_id();
let usage = ChannelUsage {
amount_msat: 500,
inflight_htlc_msat: 0,
};
scorer.payment_path_failed(&payment_path_for_amount(500), 42);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
+ let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+ let (info, _) = channel.as_directed_from(&source).unwrap();
+ let candidate = CandidateRouteHop::PublicHop {
+ info,
+ short_channel_id: 42,
+ };
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), u64::max_value());
let mut serialized_scorer = Vec::new();
scorer.write(&mut serialized_scorer).unwrap();
let mut serialized_scorer = io::Cursor::new(&serialized_scorer);
let deserialized_scorer =
<ProbabilisticScorer>::read(&mut serialized_scorer, (decay_params, &network_graph, &logger)).unwrap();
- assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 473);
+ assert_eq!(deserialized_scorer.channel_penalty_msat(&candidate, usage, ¶ms), 473);
scorer.payment_path_failed(&payment_path_for_amount(250), 43);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 300);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 300);
SinceEpoch::advance(Duration::from_secs(10));
- assert_eq!(deserialized_scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 370);
+ assert_eq!(deserialized_scorer.channel_penalty_msat(&candidate, usage, ¶ms), 370);
}
#[test]
let params = ProbabilisticScoringFeeParameters::default();
let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
let source = source_node_id();
- let target = target_node_id();
let usage = ChannelUsage {
amount_msat: 100_000_000,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 950_000_000, htlc_maximum_msat: 1_000 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 11497);
+ let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+ let (info, _) = channel.as_directed_from(&source).unwrap();
+ let candidate = CandidateRouteHop::PublicHop {
+ info,
+ short_channel_id: 42,
+ };
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 11497);
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 7408);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 7408);
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 2_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 6151);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 6151);
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 3_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 5427);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 5427);
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 4_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 4955);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 4955);
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 5_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 4736);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 4736);
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 6_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 4484);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 4484);
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 7_450_000_000, htlc_maximum_msat: 1_000 }, ..usage
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 4484);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 4484);
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 7_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 4263);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 4263);
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 8_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 4263);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 4263);
let usage = ChannelUsage {
effective_capacity: EffectiveCapacity::Total { capacity_msat: 9_950_000_000, htlc_maximum_msat: 1_000 }, ..usage
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 4044);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 4044);
}
#[test]
let logger = TestLogger::new();
let network_graph = network_graph(&logger);
let source = source_node_id();
- let target = target_node_id();
let usage = ChannelUsage {
amount_msat: 128,
inflight_htlc_msat: 0,
..ProbabilisticScoringFeeParameters::zero_penalty()
};
let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 58);
+ let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+ let (info, _) = channel.as_directed_from(&source).unwrap();
+ let candidate = CandidateRouteHop::PublicHop {
+ info,
+ short_channel_id: 42,
+ };
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 58);
let params = ProbabilisticScoringFeeParameters {
base_penalty_msat: 500, liquidity_penalty_multiplier_msat: 1_000,
anti_probing_penalty_msat: 0, ..ProbabilisticScoringFeeParameters::zero_penalty()
};
let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 558);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 558);
let params = ProbabilisticScoringFeeParameters {
base_penalty_msat: 500, liquidity_penalty_multiplier_msat: 1_000,
};
let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 558 + 128);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 558 + 128);
}
#[test]
let logger = TestLogger::new();
let network_graph = network_graph(&logger);
let source = source_node_id();
- let target = target_node_id();
let usage = ChannelUsage {
amount_msat: 512_000,
inflight_htlc_msat: 0,
..ProbabilisticScoringFeeParameters::zero_penalty()
};
let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 300);
+ let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+ let (info, _) = channel.as_directed_from(&source).unwrap();
+ let candidate = CandidateRouteHop::PublicHop {
+ info,
+ short_channel_id: 42,
+ };
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 300);
let params = ProbabilisticScoringFeeParameters {
liquidity_penalty_multiplier_msat: 1_000,
..ProbabilisticScoringFeeParameters::zero_penalty()
};
let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 337);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 337);
}
#[test]
let logger = TestLogger::new();
let network_graph = network_graph(&logger);
let source = source_node_id();
- let target = target_node_id();
let usage = ChannelUsage {
amount_msat: u64::max_value(),
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Infinite,
};
-
let params = ProbabilisticScoringFeeParameters {
liquidity_penalty_multiplier_msat: 40_000,
..ProbabilisticScoringFeeParameters::zero_penalty()
};
let decay_params = ProbabilisticScoringDecayParameters::zero_penalty();
+ let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+ let (info, _) = channel.as_directed_from(&source).unwrap();
+ let candidate = CandidateRouteHop::PublicHop {
+ info,
+ short_channel_id: 42,
+ };
let scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 80_000);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 80_000);
}
#[test]
};
let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
let source = source_node_id();
- let target = target_node_id();
let usage = ChannelUsage {
amount_msat: 750,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_000, htlc_maximum_msat: 1_000 },
};
- assert_ne!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
+ let network_graph = network_graph.read_only();
+ let channel = network_graph.channel(42).unwrap();
+ let (info, _) = channel.as_directed_from(&source).unwrap();
+ let candidate = CandidateRouteHop::PublicHop {
+ info,
+ short_channel_id: 42,
+ };
+ assert_ne!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), u64::max_value());
let usage = ChannelUsage { inflight_htlc_msat: 251, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), u64::max_value());
}
#[test]
let params = ProbabilisticScoringFeeParameters::default();
let scorer = ProbabilisticScorer::new(ProbabilisticScoringDecayParameters::default(), &network_graph, &logger);
let source = source_node_id();
- let target = target_node_id();
let base_penalty_msat = params.base_penalty_msat;
let usage = ChannelUsage {
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::ExactLiquidity { liquidity_msat: 1_000 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), base_penalty_msat);
+ let network_graph = network_graph.read_only();
+ let channel = network_graph.channel(42).unwrap();
+ let (info, _) = channel.as_directed_from(&source).unwrap();
+ let candidate = CandidateRouteHop::PublicHop {
+ info,
+ short_channel_id: 42,
+ };
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), base_penalty_msat);
let usage = ChannelUsage { amount_msat: 1_000, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), base_penalty_msat);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), base_penalty_msat);
let usage = ChannelUsage { amount_msat: 1_001, ..usage };
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), u64::max_value());
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), u64::max_value());
}
- #[test]
fn remembers_historical_failures() {
let logger = TestLogger::new();
let network_graph = network_graph(&logger);
};
let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
let source = source_node_id();
- let target = target_node_id();
let usage = ChannelUsage {
amount_msat: 100,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_024 },
};
+ let network_graph = network_graph.read_only();
+ let channel = network_graph.channel(42).unwrap();
+ let (info, target) = channel.as_directed_from(&source).unwrap();
+ let candidate = CandidateRouteHop::PublicHop {
+ info,
+ short_channel_id: 42,
+ };
// With no historical data the normal liquidity penalty calculation is used.
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 168);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 168);
assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
- None);
+ None);
assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, 42, ¶ms),
- None);
+ None);
scorer.payment_path_failed(&payment_path_for_amount(1), 42);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 2048);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage_1, ¶ms), 249);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 2048);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage_1, ¶ms), 249);
// The "it failed" increment is 32, where the probability should lie several buckets into
// the first octile.
assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
// Even after we tell the scorer we definitely have enough available liquidity, it will
// still remember that there was some failure in the past, and assign a non-0 penalty.
scorer.payment_path_failed(&payment_path_for_amount(1000), 43);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 105);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 105);
// The first points should be decayed just slightly and the last bucket has a new point.
assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
Some(([31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0],
// Advance the time forward 16 half-lives (which the docs claim will ensure all data is
// gone), and check that we're back to where we started.
SinceEpoch::advance(Duration::from_secs(10 * 16));
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 168);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 168);
// Once fully decayed we still have data, but its all-0s. In the future we may remove the
// data entirely instead.
assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_024 },
};
scorer.payment_path_failed(&payment_path_for_amount(1), 42);
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 2050);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 2050);
usage.inflight_htlc_msat = 0;
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 866);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 866);
let usage = ChannelUsage {
amount_msat: 1,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::AdvertisedMaxHTLC { amount_msat: 0 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &target, &source, usage, ¶ms), 2048);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 2048);
// Advance to decay all liquidity offsets to zero.
SinceEpoch::advance(Duration::from_secs(60 * 60 * 10));
let logger = TestLogger::new();
let network_graph = network_graph(&logger);
let source = source_node_id();
- let target = target_node_id();
let params = ProbabilisticScoringFeeParameters {
anti_probing_penalty_msat: 500,
..ProbabilisticScoringFeeParameters::zero_penalty()
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: 1_000 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
+ let network_graph = network_graph.read_only();
+ let channel = network_graph.channel(42).unwrap();
+ let (info, _) = channel.as_directed_from(&source).unwrap();
+ let candidate = CandidateRouteHop::PublicHop {
+ info,
+ short_channel_id: 42,
+ };
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 0);
// Check we receive anti-probing penalty for htlc_maximum_msat == channel_capacity.
let usage = ChannelUsage {
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: 1_024_000 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 500);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 500);
// Check we receive anti-probing penalty for htlc_maximum_msat == channel_capacity/2.
let usage = ChannelUsage {
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: 512_000 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 500);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 500);
// Check we receive no anti-probing penalty for htlc_maximum_msat == channel_capacity/2 - 1.
let usage = ChannelUsage {
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024_000, htlc_maximum_msat: 511_999 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 0);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 0);
}
#[test]
let decay_params = ProbabilisticScoringDecayParameters::default();
let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
let source = source_node_id();
- let target = target_node_id();
let usage = ChannelUsage {
amount_msat: 512,
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat: 1_024, htlc_maximum_msat: 1_000 },
};
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 300);
+ let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+ let (info, target) = channel.as_directed_from(&source).unwrap();
+ let candidate = CandidateRouteHop::PublicHop {
+ info,
+ short_channel_id: 42,
+ };
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 300);
let mut path = payment_path_for_amount(768);
let recipient_hop = path.hops.pop().unwrap();
let mut scorer = ProbabilisticScorer::new(decay_params, &network_graph, &logger);
let source = source_node_id();
- let target = target_node_id();
let mut amount_msat = 10_000_000;
let usage = ChannelUsage {
inflight_htlc_msat: 0,
effective_capacity: EffectiveCapacity::Total { capacity_msat, htlc_maximum_msat: capacity_msat },
};
+ let channel = network_graph.read_only().channel(42).unwrap().to_owned();
+ let (info, target) = channel.as_directed_from(&source).unwrap();
+ let candidate = CandidateRouteHop::PublicHop {
+ info,
+ short_channel_id: 42,
+ };
// With no historical data the normal liquidity penalty calculation is used, which results
// in a success probability of ~75%.
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms), 1269);
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms), 1269);
assert_eq!(scorer.historical_estimated_channel_liquidity_probabilities(42, &target),
None);
assert_eq!(scorer.historical_estimated_payment_success_probability(42, &target, 42, ¶ms),
scorer.payment_path_failed(&payment_path_for_amount(amount_msat), 42);
// The penalty should be the maximum penalty, as the payment we're scoring is now in the
// same bucket which is the only maximum datapoint.
- assert_eq!(scorer.channel_penalty_msat(42, &source, &target, usage, ¶ms),
+ assert_eq!(scorer.channel_penalty_msat(&candidate, usage, ¶ms),
2048 + 2048 * amount_msat / super::AMOUNT_PENALTY_DIVISOR);
// The "it failed" increment is 32, which we should apply to the first upper-bound (between
// 6k sats and 12k sats).
use crate::chain::channelmonitor;
use crate::chain::channelmonitor::MonitorEvent;
use crate::chain::transaction::OutPoint;
+use crate::routing::router::CandidateRouteHop;
use crate::sign;
use crate::events;
use crate::events::bump_transaction::{WalletSource, Utxo};
// Since the path is reversed, the last element in our iteration is the first
// hop.
if idx == path.hops.len() - 1 {
- scorer.channel_penalty_msat(hop.short_channel_id, &NodeId::from_pubkey(payer), &NodeId::from_pubkey(&hop.pubkey), usage, &Default::default());
+ let first_hops = match first_hops {
+ Some(hops) => hops,
+ None => continue,
+ };
+ if first_hops.len() == 0 {
+ continue;
+ }
+ let idx = if first_hops.len() > 1 { route.paths.iter().position(|p| p == path).unwrap_or(0) } else { 0 };
+ let candidate = CandidateRouteHop::FirstHop {
+ details: first_hops[idx],
+ node_id: NodeId::from_pubkey(payer)
+ };
+ scorer.channel_penalty_msat(&candidate, usage, &());
} else {
- let curr_hop_path_idx = path.hops.len() - 1 - idx;
- scorer.channel_penalty_msat(hop.short_channel_id, &NodeId::from_pubkey(&path.hops[curr_hop_path_idx - 1].pubkey), &NodeId::from_pubkey(&hop.pubkey), usage, &Default::default());
+ let network_graph = self.network_graph.read_only();
+ let channel = match network_graph.channel(hop.short_channel_id) {
+ Some(channel) => channel,
+ None => continue,
+ };
+ let channel = match channel.as_directed_to(&NodeId::from_pubkey(&hop.pubkey)) {
+ Some(channel) => channel,
+ None => panic!("Channel directed to {} was not found", hop.pubkey),
+ };
+ let candidate = CandidateRouteHop::PublicHop {
+ info: channel.0,
+ short_channel_id: hop.short_channel_id,
+ };
+ scorer.channel_penalty_msat(&candidate, usage, &());
}
}
}
impl ScoreLookUp for TestScorer {
type ScoreParams = ();
fn channel_penalty_msat(
- &self, short_channel_id: u64, _source: &NodeId, _target: &NodeId, usage: ChannelUsage, _score_params: &Self::ScoreParams
+ &self, candidate: &CandidateRouteHop, usage: ChannelUsage, _score_params: &Self::ScoreParams
) -> u64 {
+ let short_channel_id = match candidate.short_channel_id() {
+ Some(scid) => scid,
+ None => return 0,
+ };
if let Some(scorer_expectations) = self.scorer_expectations.borrow_mut().as_mut() {
match scorer_expectations.pop_front() {
Some((scid, expectation)) => {
--- /dev/null
+## Bug fixes
+
+* In LDK versions 0.0.116 through 0.0.118, in rare cases where skimmed fees are present on shutdown
+ the `ChannelManager` may fail to deserialize on startup.