pub struct HTLCUpdate {
pub(crate) payment_hash: PaymentHash,
pub(crate) payment_preimage: Option<PaymentPreimage>,
- pub(crate) source: HTLCSource
+ pub(crate) source: HTLCSource,
+ pub(crate) onchain_value_satoshis: Option<u64>,
}
impl_writeable_tlv_based!(HTLCUpdate, {
(0, payment_hash, required),
+ (1, onchain_value_satoshis, option),
(2, source, required),
(4, payment_preimage, option),
});
HTLCUpdate {
source: HTLCSource,
payment_hash: PaymentHash,
+ onchain_value_satoshis: Option<u64>,
},
MaturingOutput {
descriptor: SpendableOutputDescriptor,
impl_writeable_tlv_based_enum!(OnchainEvent,
(0, HTLCUpdate) => {
(0, source, required),
+ (1, onchain_value_satoshis, option),
(2, payment_hash, required),
},
(1, MaturingOutput) => {
}
}
+/// Compares a broadcasted commitment transaction's HTLCs with those in the latest state,
+/// failing any HTLCs which didn't make it into the broadcasted commitment transaction back
+/// after ANTI_REORG_DELAY blocks.
+macro_rules! fail_unbroadcast_htlcs {
+ ($self: expr, $commitment_tx_type: expr, $commitment_tx_conf_height: expr, $confirmed_htlcs_list: expr, $logger: expr) => { {
+ macro_rules! check_htlc_fails {
+ ($txid: expr, $commitment_tx: expr) => {
+ if let Some(ref latest_outpoints) = $self.counterparty_claimable_outpoints.get($txid) {
+ for &(ref htlc, ref source_option) in latest_outpoints.iter() {
+ if let &Some(ref source) = source_option {
+ // Check if the HTLC is present in the commitment transaction that was
+ // broadcast, but not if it was below the dust limit, which we should
+ // fail backwards immediately as there is no way for us to learn the
+ // payment_preimage.
+ // Note that if the dust limit were allowed to change between
+ // commitment transactions we'd want to be check whether *any*
+ // broadcastable commitment transaction has the HTLC in it, but it
+ // cannot currently change after channel initialization, so we don't
+ // need to here.
+ let confirmed_htlcs_iter: &mut Iterator<Item = (&HTLCOutputInCommitment, Option<&HTLCSource>)> = &mut $confirmed_htlcs_list;
+ let mut matched_htlc = false;
+ for (ref broadcast_htlc, ref broadcast_source) in confirmed_htlcs_iter {
+ if broadcast_htlc.transaction_output_index.is_some() && Some(&**source) == *broadcast_source {
+ matched_htlc = true;
+ break;
+ }
+ }
+ if matched_htlc { continue; }
+ $self.onchain_events_awaiting_threshold_conf.retain(|ref entry| {
+ if entry.height != $commitment_tx_conf_height { return true; }
+ match entry.event {
+ OnchainEvent::HTLCUpdate { source: ref update_source, .. } => {
+ *update_source != **source
+ },
+ _ => true,
+ }
+ });
+ let entry = OnchainEventEntry {
+ txid: *$txid,
+ height: $commitment_tx_conf_height,
+ event: OnchainEvent::HTLCUpdate {
+ source: (**source).clone(),
+ payment_hash: htlc.payment_hash.clone(),
+ onchain_value_satoshis: Some(htlc.amount_msat / 1000),
+ },
+ };
+ log_trace!($logger, "Failing HTLC with payment_hash {} from {} counterparty commitment tx due to broadcast of {} commitment transaction, waiting for confirmation (at height {})",
+ log_bytes!(htlc.payment_hash.0), $commitment_tx, $commitment_tx_type, entry.confirmation_threshold());
+ $self.onchain_events_awaiting_threshold_conf.push(entry);
+ }
+ }
+ }
+ }
+ }
+ if let Some(ref txid) = $self.current_counterparty_commitment_txid {
+ check_htlc_fails!(txid, "current");
+ }
+ if let Some(ref txid) = $self.prev_counterparty_commitment_txid {
+ check_htlc_fails!(txid, "previous");
+ }
+ } }
+}
+
impl<Signer: Sign> ChannelMonitorImpl<Signer> {
/// Inserts a revocation secret into this channel monitor. Prunes old preimages if neither
/// needed by holder commitment transactions HTCLs nor by counterparty ones. Unless we haven't already seen
}
self.counterparty_commitment_txn_on_chain.insert(commitment_txid, commitment_number);
- macro_rules! check_htlc_fails {
- ($txid: expr, $commitment_tx: expr) => {
- if let Some(ref outpoints) = self.counterparty_claimable_outpoints.get($txid) {
- for &(ref htlc, ref source_option) in outpoints.iter() {
- if let &Some(ref source) = source_option {
- self.onchain_events_awaiting_threshold_conf.retain(|ref entry| {
- if entry.height != height { return true; }
- match entry.event {
- OnchainEvent::HTLCUpdate { source: ref update_source, .. } => {
- *update_source != **source
- },
- _ => true,
- }
- });
- let entry = OnchainEventEntry {
- txid: *$txid,
- height,
- event: OnchainEvent::HTLCUpdate {
- source: (**source).clone(),
- payment_hash: htlc.payment_hash.clone(),
- },
- };
- log_info!(logger, "Failing HTLC with payment_hash {} from {} counterparty commitment tx due to broadcast of revoked counterparty commitment transaction, waiting for confirmation (at height {})", log_bytes!(htlc.payment_hash.0), $commitment_tx, entry.confirmation_threshold());
- self.onchain_events_awaiting_threshold_conf.push(entry);
- }
- }
- }
- }
- }
- if let Some(ref txid) = self.current_counterparty_commitment_txid {
- check_htlc_fails!(txid, "current");
- }
- if let Some(ref txid) = self.prev_counterparty_commitment_txid {
- check_htlc_fails!(txid, "counterparty");
- }
- // No need to check holder commitment txn, symmetric HTLCSource must be present as per-htlc data on counterparty commitment tx
+ fail_unbroadcast_htlcs!(self, "revoked counterparty", height, [].iter().map(|a| *a), logger);
}
} else if let Some(per_commitment_data) = per_commitment_option {
// While this isn't useful yet, there is a potential race where if a counterparty
self.counterparty_commitment_txn_on_chain.insert(commitment_txid, commitment_number);
log_info!(logger, "Got broadcast of non-revoked counterparty commitment transaction {}", commitment_txid);
-
- macro_rules! check_htlc_fails {
- ($txid: expr, $commitment_tx: expr, $id: tt) => {
- if let Some(ref latest_outpoints) = self.counterparty_claimable_outpoints.get($txid) {
- $id: for &(ref htlc, ref source_option) in latest_outpoints.iter() {
- if let &Some(ref source) = source_option {
- // Check if the HTLC is present in the commitment transaction that was
- // broadcast, but not if it was below the dust limit, which we should
- // fail backwards immediately as there is no way for us to learn the
- // payment_preimage.
- // Note that if the dust limit were allowed to change between
- // commitment transactions we'd want to be check whether *any*
- // broadcastable commitment transaction has the HTLC in it, but it
- // cannot currently change after channel initialization, so we don't
- // need to here.
- for &(ref broadcast_htlc, ref broadcast_source) in per_commitment_data.iter() {
- if broadcast_htlc.transaction_output_index.is_some() && Some(source) == broadcast_source.as_ref() {
- continue $id;
- }
- }
- log_trace!(logger, "Failing HTLC with payment_hash {} from {} counterparty commitment tx due to broadcast of counterparty commitment transaction", log_bytes!(htlc.payment_hash.0), $commitment_tx);
- self.onchain_events_awaiting_threshold_conf.retain(|ref entry| {
- if entry.height != height { return true; }
- match entry.event {
- OnchainEvent::HTLCUpdate { source: ref update_source, .. } => {
- *update_source != **source
- },
- _ => true,
- }
- });
- self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry {
- txid: *$txid,
- height,
- event: OnchainEvent::HTLCUpdate {
- source: (**source).clone(),
- payment_hash: htlc.payment_hash.clone(),
- },
- });
- }
- }
- }
- }
- }
- if let Some(ref txid) = self.current_counterparty_commitment_txid {
- check_htlc_fails!(txid, "current", 'current_loop);
- }
- if let Some(ref txid) = self.prev_counterparty_commitment_txid {
- check_htlc_fails!(txid, "previous", 'prev_loop);
- }
+ fail_unbroadcast_htlcs!(self, "counterparty", height, per_commitment_data.iter().map(|(a, b)| (a, b.as_ref().map(|b| b.as_ref()))), logger);
let htlc_claim_reqs = self.get_counterparty_htlc_output_claim_reqs(commitment_number, commitment_txid, Some(tx));
for req in htlc_claim_reqs {
let mut claim_requests = Vec::new();
let mut watch_outputs = Vec::new();
- macro_rules! wait_threshold_conf {
- ($source: expr, $commitment_tx: expr, $payment_hash: expr) => {
- self.onchain_events_awaiting_threshold_conf.retain(|ref entry| {
- if entry.height != height { return true; }
- match entry.event {
- OnchainEvent::HTLCUpdate { source: ref update_source, .. } => {
- *update_source != $source
- },
- _ => true,
- }
- });
- let entry = OnchainEventEntry {
- txid: commitment_txid,
- height,
- event: OnchainEvent::HTLCUpdate { source: $source, payment_hash: $payment_hash },
- };
- log_trace!(logger, "Failing HTLC with payment_hash {} from {} holder commitment tx due to broadcast of transaction, waiting confirmation (at height{})", log_bytes!($payment_hash.0), $commitment_tx, entry.confirmation_threshold());
- self.onchain_events_awaiting_threshold_conf.push(entry);
- }
- }
-
macro_rules! append_onchain_update {
($updates: expr, $to_watch: expr) => {
claim_requests = $updates.0;
}
macro_rules! fail_dust_htlcs_after_threshold_conf {
- ($holder_tx: expr) => {
+ ($holder_tx: expr, $commitment_tx: expr) => {
for &(ref htlc, _, ref source) in &$holder_tx.htlc_outputs {
if htlc.transaction_output_index.is_none() {
if let &Some(ref source) = source {
- wait_threshold_conf!(source.clone(), "lastest", htlc.payment_hash.clone());
+ self.onchain_events_awaiting_threshold_conf.retain(|ref entry| {
+ if entry.height != height { return true; }
+ match entry.event {
+ OnchainEvent::HTLCUpdate { source: ref update_source, .. } => {
+ update_source != source
+ },
+ _ => true,
+ }
+ });
+ let entry = OnchainEventEntry {
+ txid: commitment_txid,
+ height,
+ event: OnchainEvent::HTLCUpdate {
+ source: source.clone(), payment_hash: htlc.payment_hash,
+ onchain_value_satoshis: Some(htlc.amount_msat / 1000)
+ },
+ };
+ log_trace!(logger, "Failing HTLC with payment_hash {} from {} holder commitment tx due to broadcast of transaction, waiting confirmation (at height{})",
+ log_bytes!(htlc.payment_hash.0), $commitment_tx, entry.confirmation_threshold());
+ self.onchain_events_awaiting_threshold_conf.push(entry);
}
}
}
}
if is_holder_tx {
- fail_dust_htlcs_after_threshold_conf!(self.current_holder_commitment_tx);
+ fail_dust_htlcs_after_threshold_conf!(self.current_holder_commitment_tx, "latest");
if let &Some(ref holder_tx) = &self.prev_holder_signed_commitment_tx {
- fail_dust_htlcs_after_threshold_conf!(holder_tx);
+ fail_dust_htlcs_after_threshold_conf!(holder_tx, "previous");
}
}
// Produce actionable events from on-chain events having reached their threshold.
for entry in onchain_events_reaching_threshold_conf.drain(..) {
match entry.event {
- OnchainEvent::HTLCUpdate { ref source, payment_hash } => {
+ OnchainEvent::HTLCUpdate { ref source, payment_hash, onchain_value_satoshis } => {
// Check for duplicate HTLC resolutions.
#[cfg(debug_assertions)]
{
log_debug!(logger, "HTLC {} failure update has got enough confirmations to be passed upstream", log_bytes!(payment_hash.0));
self.pending_monitor_events.push(MonitorEvent::HTLCEvent(HTLCUpdate {
- payment_hash: payment_hash,
+ payment_hash,
payment_preimage: None,
source: source.clone(),
+ onchain_value_satoshis,
}));
},
OnchainEvent::MaturingOutput { descriptor } => {
if pending_htlc.payment_hash == $htlc_output.payment_hash && pending_htlc.amount_msat == $htlc_output.amount_msat {
if let &Some(ref source) = pending_source {
log_claim!("revoked counterparty commitment tx", false, pending_htlc, true);
- payment_data = Some(((**source).clone(), $htlc_output.payment_hash));
+ payment_data = Some(((**source).clone(), $htlc_output.payment_hash, $htlc_output.amount_msat));
break;
}
}
// transaction. This implies we either learned a preimage, the HTLC
// has timed out, or we screwed up. In any case, we should now
// resolve the source HTLC with the original sender.
- payment_data = Some(((*source).clone(), htlc_output.payment_hash));
+ payment_data = Some(((*source).clone(), htlc_output.payment_hash, htlc_output.amount_msat));
} else if !$holder_tx {
check_htlc_valid_counterparty!(self.current_counterparty_commitment_txid, htlc_output);
if payment_data.is_none() {
// Check that scan_commitment, above, decided there is some source worth relaying an
// HTLC resolution backwards to and figure out whether we learned a preimage from it.
- if let Some((source, payment_hash)) = payment_data {
+ if let Some((source, payment_hash, amount_msat)) = payment_data {
let mut payment_preimage = PaymentPreimage([0; 32]);
if accepted_preimage_claim {
if !self.pending_monitor_events.iter().any(
self.pending_monitor_events.push(MonitorEvent::HTLCEvent(HTLCUpdate {
source,
payment_preimage: Some(payment_preimage),
- payment_hash
+ payment_hash,
+ onchain_value_satoshis: Some(amount_msat / 1000),
}));
}
} else if offered_preimage_claim {
self.pending_monitor_events.push(MonitorEvent::HTLCEvent(HTLCUpdate {
source,
payment_preimage: Some(payment_preimage),
- payment_hash
+ payment_hash,
+ onchain_value_satoshis: Some(amount_msat / 1000),
}));
}
} else {
let entry = OnchainEventEntry {
txid: tx.txid(),
height,
- event: OnchainEvent::HTLCUpdate { source: source, payment_hash: payment_hash },
+ event: OnchainEvent::HTLCUpdate {
+ source, payment_hash,
+ onchain_value_satoshis: Some(amount_msat / 1000),
+ },
};
log_info!(logger, "Failing HTLC with payment_hash {} timeout by a spend tx, waiting for confirmation (at height {})", log_bytes!(payment_hash.0), entry.confirmation_threshold());
self.onchain_events_awaiting_threshold_conf.push(entry);