claimable_height: u32,
/// The payment hash whose preimage our counterparty needs to claim this HTLC.
payment_hash: PaymentHash,
+ /// Whether this HTLC represents a payment which was sent outbound from us. Otherwise it
+ /// represents an HTLC which was forwarded (and should, thus, have a corresponding inbound
+ /// edge on another channel).
+ outbound_payment: bool,
},
/// HTLCs which we received from our counterparty which are claimable with a preimage which we
/// do not currently have. This will only be claimable if we receive the preimage from the node
}
impl Balance {
- /// The amount claimable, in satoshis. This excludes balances that we are unsure if we are able
- /// to claim, this is because we are waiting for a preimage or for a timeout to expire. For more
- /// information on these balances see [`Balance::MaybeTimeoutClaimableHTLC`] and
+ /// The amount claimable, in satoshis.
+ ///
+ /// For outbound payments, this excludes the balance from the possible HTLC timeout.
+ ///
+ /// For forwarded payments, this includes the balance from the possible HTLC timeout as
+ /// (to be conservative) that balance does not include routing fees we'd earn if we'd claim
+ /// the balance from a preimage in a successful forward.
+ ///
+ /// For more information on these balances see [`Balance::MaybeTimeoutClaimableHTLC`] and
/// [`Balance::MaybePreimageClaimableHTLC`].
///
/// On-chain fees required to claim the balance are not included in this amount.
Balance::ContentiousClaimable { amount_satoshis, .. }|
Balance::CounterpartyRevokedOutputClaimable { amount_satoshis, .. }
=> *amount_satoshis,
- Balance::MaybeTimeoutClaimableHTLC { .. }|
- Balance::MaybePreimageClaimableHTLC { .. }
- => 0,
+ Balance::MaybeTimeoutClaimableHTLC { amount_satoshis, outbound_payment, .. }
+ => if *outbound_payment { 0 } else { *amount_satoshis },
+ Balance::MaybePreimageClaimableHTLC { .. } => 0,
}
}
}
impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
/// Helper for get_claimable_balances which does the work for an individual HTLC, generating up
/// to one `Balance` for the HTLC.
- fn get_htlc_balance(&self, htlc: &HTLCOutputInCommitment, holder_commitment: bool,
- counterparty_revoked_commitment: bool, confirmed_txid: Option<Txid>)
- -> Option<Balance> {
+ fn get_htlc_balance(&self, htlc: &HTLCOutputInCommitment, source: Option<&HTLCSource>,
+ holder_commitment: bool, counterparty_revoked_commitment: bool,
+ confirmed_txid: Option<Txid>
+ ) -> Option<Balance> {
let htlc_commitment_tx_output_idx =
if let Some(v) = htlc.transaction_output_index { v } else { return None; };
confirmation_height: conf_thresh,
});
} else {
+ let outbound_payment = match source {
+ None => {
+ debug_assert!(false, "Outbound HTLCs should have a source");
+ true
+ },
+ Some(&HTLCSource::PreviousHopData(_)) => false,
+ Some(&HTLCSource::OutboundRoute { .. }) => true,
+ };
return Some(Balance::MaybeTimeoutClaimableHTLC {
amount_satoshis: htlc.amount_msat / 1000,
claimable_height: htlc.cltv_expiry,
payment_hash: htlc.payment_hash,
+ outbound_payment,
});
}
} else if let Some(payment_preimage) = self.payment_preimages.get(&htlc.payment_hash) {
macro_rules! walk_htlcs {
($holder_commitment: expr, $counterparty_revoked_commitment: expr, $htlc_iter: expr) => {
- for htlc in $htlc_iter {
+ for (htlc, source) in $htlc_iter {
if htlc.transaction_output_index.is_some() {
- if let Some(bal) = us.get_htlc_balance(htlc, $holder_commitment, $counterparty_revoked_commitment, confirmed_txid) {
+ if let Some(bal) = us.get_htlc_balance(
+ htlc, source, $holder_commitment, $counterparty_revoked_commitment, confirmed_txid
+ ) {
res.push(bal);
}
}
}
}
if Some(txid) == us.current_counterparty_commitment_txid || Some(txid) == us.prev_counterparty_commitment_txid {
- walk_htlcs!(false, false, counterparty_tx_htlcs.iter().map(|(a, _)| a));
+ walk_htlcs!(false, false, counterparty_tx_htlcs.iter().map(|(a, b)| (a, b.as_ref().map(|b| &**b))));
} else {
- walk_htlcs!(false, true, counterparty_tx_htlcs.iter().map(|(a, _)| a));
+ walk_htlcs!(false, true, counterparty_tx_htlcs.iter().map(|(a, b)| (a, b.as_ref().map(|b| &**b))));
// The counterparty broadcasted a revoked state!
// Look for any StaticOutputs first, generating claimable balances for those.
// If any match the confirmed counterparty revoked to_self output, skip
}
found_commitment_tx = true;
} else if txid == us.current_holder_commitment_tx.txid {
- walk_htlcs!(true, false, us.current_holder_commitment_tx.htlc_outputs.iter().map(|(a, _, _)| a));
+ walk_htlcs!(true, false, us.current_holder_commitment_tx.htlc_outputs.iter().map(|(a, _, c)| (a, c.as_ref())));
if let Some(conf_thresh) = pending_commitment_tx_conf_thresh {
res.push(Balance::ClaimableAwaitingConfirmations {
amount_satoshis: us.current_holder_commitment_tx.to_self_value_sat,
found_commitment_tx = true;
} else if let Some(prev_commitment) = &us.prev_holder_signed_commitment_tx {
if txid == prev_commitment.txid {
- walk_htlcs!(true, false, prev_commitment.htlc_outputs.iter().map(|(a, _, _)| a));
+ walk_htlcs!(true, false, prev_commitment.htlc_outputs.iter().map(|(a, _, c)| (a, c.as_ref())));
if let Some(conf_thresh) = pending_commitment_tx_conf_thresh {
res.push(Balance::ClaimableAwaitingConfirmations {
amount_satoshis: prev_commitment.to_self_value_sat,
}
} else {
let mut claimable_inbound_htlc_value_sat = 0;
- for (htlc, _, _) in us.current_holder_commitment_tx.htlc_outputs.iter() {
+ for (htlc, _, source) in us.current_holder_commitment_tx.htlc_outputs.iter() {
if htlc.transaction_output_index.is_none() { continue; }
if htlc.offered {
+ let outbound_payment = match source {
+ None => {
+ debug_assert!(false, "Outbound HTLCs should have a source");
+ true
+ },
+ Some(HTLCSource::PreviousHopData(_)) => false,
+ Some(HTLCSource::OutboundRoute { .. }) => true,
+ };
res.push(Balance::MaybeTimeoutClaimableHTLC {
amount_satoshis: htlc.amount_msat / 1000,
claimable_height: htlc.cltv_expiry,
payment_hash: htlc.payment_hash,
+ outbound_payment,
});
} else if us.payment_preimages.get(&htlc.payment_hash).is_some() {
claimable_inbound_htlc_value_sat += htlc.amount_msat / 1000;
amount_satoshis: 3_000,
claimable_height: htlc_cltv_timeout,
payment_hash,
+ outbound_payment: true,
};
let sent_htlc_timeout_balance = Balance::MaybeTimeoutClaimableHTLC {
amount_satoshis: 4_000,
claimable_height: htlc_cltv_timeout,
payment_hash: timeout_payment_hash,
+ outbound_payment: true,
};
let received_htlc_balance = Balance::MaybePreimageClaimableHTLC {
amount_satoshis: 3_000,
amount_satoshis: 10_000,
claimable_height: htlc_cltv_timeout,
payment_hash,
+ outbound_payment: true,
};
let htlc_balance_unknown_preimage = Balance::MaybeTimeoutClaimableHTLC {
amount_satoshis: 20_000,
claimable_height: htlc_cltv_timeout,
payment_hash: payment_hash_2,
+ outbound_payment: true,
};
let commitment_tx_fee = chan_feerate *
amount_satoshis: 10_000,
claimable_height: htlc_cltv_timeout,
payment_hash: to_b_failed_payment_hash,
+ outbound_payment: true,
};
let a_received_htlc_balance = Balance::MaybePreimageClaimableHTLC {
amount_satoshis: 20_000,
amount_satoshis: 20_000,
claimable_height: htlc_cltv_timeout,
payment_hash: to_a_failed_payment_hash,
+ outbound_payment: true,
};
// Both A and B will have an HTLC that's claimable on timeout and one that's claimable if they
amount_satoshis: 2_000,
claimable_height: missing_htlc_cltv_timeout,
payment_hash: missing_htlc_payment_hash,
+ outbound_payment: true,
}, Balance::MaybeTimeoutClaimableHTLC {
amount_satoshis: 4_000,
claimable_height: htlc_cltv_timeout,
payment_hash: timeout_payment_hash,
+ outbound_payment: true,
}, Balance::MaybeTimeoutClaimableHTLC {
amount_satoshis: 5_000,
claimable_height: live_htlc_cltv_timeout,
payment_hash: live_payment_hash,
+ outbound_payment: true,
}]),
sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));
amount_satoshis: 4_000,
claimable_height: htlc_cltv_timeout,
payment_hash: revoked_payment_hash,
+ outbound_payment: true,
}, Balance::MaybeTimeoutClaimableHTLC {
amount_satoshis: 3_000,
claimable_height: htlc_cltv_timeout,
payment_hash: claimed_payment_hash,
+ outbound_payment: true,
}]),
sorted_vec(nodes[1].chain_monitor.chain_monitor.get_monitor(funding_outpoint).unwrap().get_claimable_balances()));