mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-03-13 14:52:21 +01:00
Store info about claimed payments, incl HTLCs in ChannelMonitor
s
When we claim an MPP payment, then crash before persisting all the relevant `ChannelMonitor`s, we rely on the payment data being available in the `ChannelManager` on restart to re-claim any parts that haven't yet been claimed. This is fine as long as the `ChannelManager` was persisted before the `PaymentClaimable` event was processed, which is generally the case in our `lightning-background-processor`, but may not be in other cases or in a somewhat rare race. In order to fix this, we need to track where all the MPP parts of a payment are in the `ChannelMonitor`, allowing us to re-claim any missing pieces without reference to any `ChannelManager` data. Further, in order to properly generate a `PaymentClaimed` event against the re-started claim, we have to store various payment metadata with the HTLC list as well. Here we store the required MPP parts and metadata in `ChannelMonitor`s and make them available to `ChannelManager` on load.
This commit is contained in:
parent
b8661ef6cf
commit
7790e30880
3 changed files with 52 additions and 13 deletions
|
@ -923,8 +923,16 @@ pub(crate) struct ChannelMonitorImpl<Signer: EcdsaChannelSigner> {
|
||||||
/// The set of payment hashes from inbound payments for which we know the preimage. Payment
|
/// The set of payment hashes from inbound payments for which we know the preimage. Payment
|
||||||
/// preimages that are not included in any unrevoked local commitment transaction or unrevoked
|
/// preimages that are not included in any unrevoked local commitment transaction or unrevoked
|
||||||
/// remote commitment transactions are automatically removed when commitment transactions are
|
/// remote commitment transactions are automatically removed when commitment transactions are
|
||||||
/// revoked.
|
/// revoked. Note that this happens one revocation after it theoretically could, leaving
|
||||||
payment_preimages: HashMap<PaymentHash, PaymentPreimage>,
|
/// preimages present here for the previous state even when the channel is "at rest". This is a
|
||||||
|
/// good safety buffer, but also is important as it ensures we retain payment preimages for the
|
||||||
|
/// previous local commitment transaction, which may have been broadcast already when we see
|
||||||
|
/// the revocation (in setups with redundant monitors).
|
||||||
|
///
|
||||||
|
/// We also store [`PaymentClaimDetails`] here, tracking the payment information(s) for this
|
||||||
|
/// preimage for inbound payments. This allows us to rebuild the inbound payment information on
|
||||||
|
/// startup even if we lost our `ChannelManager`.
|
||||||
|
payment_preimages: HashMap<PaymentHash, (PaymentPreimage, Vec<PaymentClaimDetails>)>,
|
||||||
|
|
||||||
// Note that `MonitorEvent`s MUST NOT be generated during update processing, only generated
|
// Note that `MonitorEvent`s MUST NOT be generated during update processing, only generated
|
||||||
// during chain data processing. This prevents a race in `ChainMonitor::update_channel` (and
|
// during chain data processing. This prevents a race in `ChainMonitor::update_channel` (and
|
||||||
|
@ -1150,7 +1158,7 @@ impl<Signer: EcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signer> {
|
||||||
writer.write_all(&byte_utils::be48_to_array(self.current_holder_commitment_number))?;
|
writer.write_all(&byte_utils::be48_to_array(self.current_holder_commitment_number))?;
|
||||||
|
|
||||||
writer.write_all(&(self.payment_preimages.len() as u64).to_be_bytes())?;
|
writer.write_all(&(self.payment_preimages.len() as u64).to_be_bytes())?;
|
||||||
for payment_preimage in self.payment_preimages.values() {
|
for (payment_preimage, _) in self.payment_preimages.values() {
|
||||||
writer.write_all(&payment_preimage.0[..])?;
|
writer.write_all(&payment_preimage.0[..])?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1228,6 +1236,7 @@ impl<Signer: EcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signer> {
|
||||||
(19, self.channel_id, required),
|
(19, self.channel_id, required),
|
||||||
(21, self.balances_empty_height, option),
|
(21, self.balances_empty_height, option),
|
||||||
(23, self.holder_pays_commitment_tx_fee, option),
|
(23, self.holder_pays_commitment_tx_fee, option),
|
||||||
|
(25, self.payment_preimages, required),
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -2201,7 +2210,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
|
||||||
outbound_payment,
|
outbound_payment,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if let Some(payment_preimage) = self.payment_preimages.get(&htlc.payment_hash) {
|
} else if let Some((payment_preimage, _)) = self.payment_preimages.get(&htlc.payment_hash) {
|
||||||
// Otherwise (the payment was inbound), only expose it as claimable if
|
// Otherwise (the payment was inbound), only expose it as claimable if
|
||||||
// we know the preimage.
|
// we know the preimage.
|
||||||
// Note that if there is a pending claim, but it did not use the
|
// Note that if there is a pending claim, but it did not use the
|
||||||
|
@ -2422,7 +2431,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
|
||||||
outbound_payment,
|
outbound_payment,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if us.payment_preimages.get(&htlc.payment_hash).is_some() {
|
} else if us.payment_preimages.contains_key(&htlc.payment_hash) {
|
||||||
inbound_claiming_htlc_rounded_msat += rounded_value_msat;
|
inbound_claiming_htlc_rounded_msat += rounded_value_msat;
|
||||||
if htlc.transaction_output_index.is_some() {
|
if htlc.transaction_output_index.is_some() {
|
||||||
claimable_inbound_htlc_value_sat += htlc.amount_msat / 1000;
|
claimable_inbound_htlc_value_sat += htlc.amount_msat / 1000;
|
||||||
|
@ -2577,7 +2586,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_stored_preimages(&self) -> HashMap<PaymentHash, PaymentPreimage> {
|
pub(crate) fn get_stored_preimages(&self) -> HashMap<PaymentHash, (PaymentPreimage, Vec<PaymentClaimDetails>)> {
|
||||||
self.inner.lock().unwrap().payment_preimages.clone()
|
self.inner.lock().unwrap().payment_preimages.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2936,6 +2945,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
|
||||||
|
|
||||||
/// Provides a payment_hash->payment_preimage mapping. Will be automatically pruned when all
|
/// Provides a payment_hash->payment_preimage mapping. Will be automatically pruned when all
|
||||||
/// commitment_tx_infos which contain the payment hash have been revoked.
|
/// commitment_tx_infos which contain the payment hash have been revoked.
|
||||||
|
///
|
||||||
|
/// Note that this is often called multiple times for the same payment and must be idempotent.
|
||||||
fn provide_payment_preimage<B: Deref, F: Deref, L: Deref>(
|
fn provide_payment_preimage<B: Deref, F: Deref, L: Deref>(
|
||||||
&mut self, payment_hash: &PaymentHash, payment_preimage: &PaymentPreimage,
|
&mut self, payment_hash: &PaymentHash, payment_preimage: &PaymentPreimage,
|
||||||
payment_info: &Option<PaymentClaimDetails>, broadcaster: &B,
|
payment_info: &Option<PaymentClaimDetails>, broadcaster: &B,
|
||||||
|
@ -2944,8 +2955,17 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
|
||||||
F::Target: FeeEstimator,
|
F::Target: FeeEstimator,
|
||||||
L::Target: Logger,
|
L::Target: Logger,
|
||||||
{
|
{
|
||||||
// TODO: Store payment_info (but do not override any existing values)
|
self.payment_preimages.entry(payment_hash.clone())
|
||||||
self.payment_preimages.insert(payment_hash.clone(), payment_preimage.clone());
|
.and_modify(|(_, payment_infos)| {
|
||||||
|
if let Some(payment_info) = payment_info {
|
||||||
|
if !payment_infos.contains(&payment_info) {
|
||||||
|
payment_infos.push(payment_info.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.or_insert_with(|| {
|
||||||
|
(payment_preimage.clone(), payment_info.clone().into_iter().collect())
|
||||||
|
});
|
||||||
|
|
||||||
let confirmed_spend_txid = self.funding_spend_confirmed.or_else(|| {
|
let confirmed_spend_txid = self.funding_spend_confirmed.or_else(|| {
|
||||||
self.onchain_events_awaiting_threshold_conf.iter().find_map(|event| match event.event {
|
self.onchain_events_awaiting_threshold_conf.iter().find_map(|event| match event.event {
|
||||||
|
@ -3602,7 +3622,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
|
||||||
return (claimable_outpoints, to_counterparty_output_info);
|
return (claimable_outpoints, to_counterparty_output_info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let preimage = if htlc.offered { if let Some(p) = self.payment_preimages.get(&htlc.payment_hash) { Some(*p) } else { None } } else { None };
|
let preimage = if htlc.offered { if let Some((p, _)) = self.payment_preimages.get(&htlc.payment_hash) { Some(*p) } else { None } } else { None };
|
||||||
if preimage.is_some() || !htlc.offered {
|
if preimage.is_some() || !htlc.offered {
|
||||||
let counterparty_htlc_outp = if htlc.offered {
|
let counterparty_htlc_outp = if htlc.offered {
|
||||||
PackageSolvingData::CounterpartyOfferedHTLCOutput(
|
PackageSolvingData::CounterpartyOfferedHTLCOutput(
|
||||||
|
@ -3690,7 +3710,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
|
||||||
);
|
);
|
||||||
(htlc_output, conf_height)
|
(htlc_output, conf_height)
|
||||||
} else {
|
} else {
|
||||||
let payment_preimage = if let Some(preimage) = self.payment_preimages.get(&htlc.payment_hash) {
|
let payment_preimage = if let Some((preimage, _)) = self.payment_preimages.get(&htlc.payment_hash) {
|
||||||
preimage.clone()
|
preimage.clone()
|
||||||
} else {
|
} else {
|
||||||
// We can't build an HTLC-Success transaction without the preimage
|
// We can't build an HTLC-Success transaction without the preimage
|
||||||
|
@ -3844,7 +3864,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
|
||||||
for htlc in self.current_holder_commitment_tx.htlc_outputs.iter() {
|
for htlc in self.current_holder_commitment_tx.htlc_outputs.iter() {
|
||||||
if let Some(vout) = htlc.0.transaction_output_index {
|
if let Some(vout) = htlc.0.transaction_output_index {
|
||||||
let preimage = if !htlc.0.offered {
|
let preimage = if !htlc.0.offered {
|
||||||
if let Some(preimage) = self.payment_preimages.get(&htlc.0.payment_hash) { Some(preimage.clone()) } else {
|
if let Some((preimage, _)) = self.payment_preimages.get(&htlc.0.payment_hash) { Some(preimage.clone()) } else {
|
||||||
// We can't build an HTLC-Success transaction without the preimage
|
// We can't build an HTLC-Success transaction without the preimage
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -4817,7 +4837,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
|
||||||
for _ in 0..payment_preimages_len {
|
for _ in 0..payment_preimages_len {
|
||||||
let preimage: PaymentPreimage = Readable::read(reader)?;
|
let preimage: PaymentPreimage = Readable::read(reader)?;
|
||||||
let hash = PaymentHash(Sha256::hash(&preimage.0[..]).to_byte_array());
|
let hash = PaymentHash(Sha256::hash(&preimage.0[..]).to_byte_array());
|
||||||
if let Some(_) = payment_preimages.insert(hash, preimage) {
|
if let Some(_) = payment_preimages.insert(hash, (preimage, Vec::new())) {
|
||||||
return Err(DecodeError::InvalidValue);
|
return Err(DecodeError::InvalidValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4900,6 +4920,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
|
||||||
let mut balances_empty_height = None;
|
let mut balances_empty_height = None;
|
||||||
let mut channel_id = None;
|
let mut channel_id = None;
|
||||||
let mut holder_pays_commitment_tx_fee = None;
|
let mut holder_pays_commitment_tx_fee = None;
|
||||||
|
let mut payment_preimages_with_info: Option<HashMap<_, _>> = None;
|
||||||
read_tlv_fields!(reader, {
|
read_tlv_fields!(reader, {
|
||||||
(1, funding_spend_confirmed, option),
|
(1, funding_spend_confirmed, option),
|
||||||
(3, htlcs_resolved_on_chain, optional_vec),
|
(3, htlcs_resolved_on_chain, optional_vec),
|
||||||
|
@ -4913,7 +4934,24 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
|
||||||
(19, channel_id, option),
|
(19, channel_id, option),
|
||||||
(21, balances_empty_height, option),
|
(21, balances_empty_height, option),
|
||||||
(23, holder_pays_commitment_tx_fee, option),
|
(23, holder_pays_commitment_tx_fee, option),
|
||||||
|
(25, payment_preimages_with_info, option),
|
||||||
});
|
});
|
||||||
|
if let Some(payment_preimages_with_info) = payment_preimages_with_info {
|
||||||
|
if payment_preimages_with_info.len() != payment_preimages.len() {
|
||||||
|
return Err(DecodeError::InvalidValue);
|
||||||
|
}
|
||||||
|
for (payment_hash, (payment_preimage, _)) in payment_preimages.iter() {
|
||||||
|
// Note that because `payment_preimages` is built back from preimages directly,
|
||||||
|
// checking that the two maps have the same hash -> preimage pairs also checks that
|
||||||
|
// the payment hashes in `payment_preimages_with_info`'s preimages match its
|
||||||
|
// hashes.
|
||||||
|
let new_preimage = payment_preimages_with_info.get(payment_hash).map(|(p, _)| p);
|
||||||
|
if new_preimage != Some(payment_preimage) {
|
||||||
|
return Err(DecodeError::InvalidValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
payment_preimages = payment_preimages_with_info;
|
||||||
|
}
|
||||||
|
|
||||||
// `HolderForceClosedWithInfo` replaced `HolderForceClosed` in v0.0.122. If we have both
|
// `HolderForceClosedWithInfo` replaced `HolderForceClosed` in v0.0.122. If we have both
|
||||||
// events, we can remove the `HolderForceClosed` event and just keep the `HolderForceClosedWithInfo`.
|
// events, we can remove the `HolderForceClosed` event and just keep the `HolderForceClosedWithInfo`.
|
||||||
|
|
|
@ -12987,7 +12987,7 @@ where
|
||||||
let bounded_fee_estimator = LowerBoundedFeeEstimator::new(args.fee_estimator);
|
let bounded_fee_estimator = LowerBoundedFeeEstimator::new(args.fee_estimator);
|
||||||
|
|
||||||
for (_, monitor) in args.channel_monitors.iter() {
|
for (_, monitor) in args.channel_monitors.iter() {
|
||||||
for (payment_hash, payment_preimage) in monitor.get_stored_preimages() {
|
for (payment_hash, (payment_preimage, _)) in monitor.get_stored_preimages() {
|
||||||
if let Some(payment) = claimable_payments.remove(&payment_hash) {
|
if let Some(payment) = claimable_payments.remove(&payment_hash) {
|
||||||
log_info!(args.logger, "Re-claiming HTLCs with payment hash {} as we've released the preimage to a ChannelMonitor!", &payment_hash);
|
log_info!(args.logger, "Re-claiming HTLCs with payment hash {} as we've released the preimage to a ChannelMonitor!", &payment_hash);
|
||||||
let mut claimable_amt_msat = 0;
|
let mut claimable_amt_msat = 0;
|
||||||
|
|
|
@ -1001,6 +1001,7 @@ impl Readable for Vec<u8> {
|
||||||
impl_for_vec!(ecdsa::Signature);
|
impl_for_vec!(ecdsa::Signature);
|
||||||
impl_for_vec!(crate::chain::channelmonitor::ChannelMonitorUpdate);
|
impl_for_vec!(crate::chain::channelmonitor::ChannelMonitorUpdate);
|
||||||
impl_for_vec!(crate::ln::channelmanager::MonitorUpdateCompletionAction);
|
impl_for_vec!(crate::ln::channelmanager::MonitorUpdateCompletionAction);
|
||||||
|
impl_for_vec!(crate::ln::channelmanager::PaymentClaimDetails);
|
||||||
impl_for_vec!(crate::ln::msgs::SocketAddress);
|
impl_for_vec!(crate::ln::msgs::SocketAddress);
|
||||||
impl_for_vec!((A, B), A, B);
|
impl_for_vec!((A, B), A, B);
|
||||||
impl_writeable_for_vec!(&crate::routing::router::BlindedTail);
|
impl_writeable_for_vec!(&crate::routing::router::BlindedTail);
|
||||||
|
|
Loading…
Add table
Reference in a new issue