Skip to content

Commit 4ef16ab

Browse files
committed
Update Channel Monitor without broadcasting transactions
Allowing Channel Monitor to be updated without executing any commands outside ChannelMonitor. This allows reading Channel Monitor in MonitorUpdatingPersister without using broadcaster and fee estimator and broadcast claims when the node is ready for it.
1 parent 299b7bd commit 4ef16ab

File tree

1 file changed

+335
-0
lines changed

1 file changed

+335
-0
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1550,6 +1550,23 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
15501550
let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
15511551
inner.update_monitor(updates, broadcaster, fee_estimator, &logger)
15521552
}
1553+
1554+
/// Updates a ChannelMonitor on the basis of some new information provided by the Channel
1555+
/// itself.
1556+
///
1557+
/// panics if the given update is not the next update by update_id.
1558+
pub fn update_monitor_without_broadcast<L: Deref>(
1559+
&self,
1560+
updates: &ChannelMonitorUpdate,
1561+
logger: &L,
1562+
) ->Result<(Vec<Result<Vec<PackageTemplate>, ()>>, bool), ()>
1563+
where
1564+
L::Target: Logger,
1565+
{
1566+
let mut inner = self.inner.lock().unwrap();
1567+
let logger = WithChannelMonitor::from_impl(logger, &*inner, None);
1568+
inner.update_monitor_without_broadcast(updates, &logger)
1569+
}
15531570

15541571
/// Gets the update_id from the latest ChannelMonitorUpdate which was applied to this
15551572
/// ChannelMonitor.
@@ -3046,6 +3063,95 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
30463063
}
30473064
}
30483065
}
3066+
3067+
/// Provides a payment_hash->payment_preimage mapping. Will be automatically pruned when all
3068+
/// commitment_tx_infos which contain the payment hash have been revoked.
3069+
///
3070+
/// Note that this is often called multiple times for the same payment and must be idempotent.
3071+
fn provide_payment_preimage_without_broadcasting<L: Deref>(
3072+
&mut self, payment_hash: &PaymentHash, payment_preimage: &PaymentPreimage,
3073+
payment_info: &Option<PaymentClaimDetails>, logger: &WithChannelMonitor<L>) -> Vec<PackageTemplate>
3074+
where L::Target: Logger,
3075+
{
3076+
let mut claimable_htlcs = vec![];
3077+
self.payment_preimages.entry(payment_hash.clone())
3078+
.and_modify(|(_, payment_infos)| {
3079+
if let Some(payment_info) = payment_info {
3080+
if !payment_infos.contains(&payment_info) {
3081+
payment_infos.push(payment_info.clone());
3082+
}
3083+
}
3084+
})
3085+
.or_insert_with(|| {
3086+
(payment_preimage.clone(), payment_info.clone().into_iter().collect())
3087+
});
3088+
3089+
let confirmed_spend_txid = self.funding_spend_confirmed.or_else(|| {
3090+
self.onchain_events_awaiting_threshold_conf.iter().find_map(|event| match event.event {
3091+
OnchainEvent::FundingSpendConfirmation { .. } => Some(event.txid),
3092+
_ => None,
3093+
})
3094+
});
3095+
let confirmed_spend_txid = if let Some(txid) = confirmed_spend_txid {
3096+
txid
3097+
} else {
3098+
return claimable_htlcs;
3099+
};
3100+
3101+
if let Some(txid) = self.current_counterparty_commitment_txid {
3102+
if txid == confirmed_spend_txid {
3103+
if let Some(commitment_number) = self.counterparty_commitment_txn_on_chain.get(&txid) {
3104+
let (htlc_claim_reqs, _) = self.get_counterparty_output_claim_info(*commitment_number, txid, None,self.counterparty_claimable_outpoints.get(&txid));
3105+
claimable_htlcs.extend(htlc_claim_reqs);
3106+
} else {
3107+
debug_assert!(false);
3108+
log_error!(logger, "Detected counterparty commitment tx on-chain without tracking commitment number");
3109+
}
3110+
return claimable_htlcs;
3111+
}
3112+
}
3113+
if let Some(txid) = self.prev_counterparty_commitment_txid {
3114+
if txid == confirmed_spend_txid {
3115+
if let Some(commitment_number) = self.counterparty_commitment_txn_on_chain.get(&txid) {
3116+
let (htlc_claim_reqs, _) = self.get_counterparty_output_claim_info(*commitment_number, txid, None,self.counterparty_claimable_outpoints.get(&txid));
3117+
claimable_htlcs.extend(htlc_claim_reqs);
3118+
} else {
3119+
debug_assert!(false);
3120+
log_error!(logger, "Detected counterparty commitment tx on-chain without tracking commitment number");
3121+
}
3122+
return claimable_htlcs;
3123+
}
3124+
}
3125+
3126+
// Then if a holder commitment transaction has been seen on-chain, broadcast transactions
3127+
// claiming the HTLC output from each of the holder commitment transactions.
3128+
// Note that we can't just use `self.holder_tx_signed`, because that only covers the case where
3129+
// *we* sign a holder commitment transaction, not when e.g. a watchtower broadcasts one of our
3130+
// holder commitment transactions.
3131+
if self.broadcasted_holder_revokable_script.is_some() {
3132+
let holder_commitment_tx = if self.current_holder_commitment_tx.txid == confirmed_spend_txid {
3133+
Some(&self.current_holder_commitment_tx)
3134+
} else if let Some(prev_holder_commitment_tx) = &self.prev_holder_signed_commitment_tx {
3135+
if prev_holder_commitment_tx.txid == confirmed_spend_txid {
3136+
Some(prev_holder_commitment_tx)
3137+
} else {
3138+
None
3139+
}
3140+
} else {
3141+
None
3142+
};
3143+
if let Some(holder_commitment_tx) = holder_commitment_tx {
3144+
// Assume that the broadcasted commitment transaction confirmed in the current best
3145+
// block. Even if not, its a reasonable metric for the bump criteria on the HTLC
3146+
// transactions.
3147+
let (claim_reqs, _) = self.get_broadcasted_holder_claims(&holder_commitment_tx, self.best_block.height);
3148+
let conf_target = self.closure_conf_target();
3149+
claimable_htlcs.extend(claim_reqs);
3150+
//self.onchain_tx_handler.update_claims_view_from_requests(claim_reqs, self.best_block.height, self.best_block.height, broadcaster, conf_target, fee_estimator, logger);
3151+
}
3152+
}
3153+
claimable_htlcs
3154+
}
30493155

30503156
fn generate_claimable_outpoints_and_watch_outputs(&mut self, reason: ClosureReason) -> (Vec<PackageTemplate>, Vec<TransactionOutputs>) {
30513157
let funding_outp = HolderFundingOutput::build(
@@ -3246,6 +3352,155 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
32463352
} else { ret }
32473353
}
32483354

3355+
3356+
fn update_monitor_without_broadcast<L: Deref>(
3357+
&mut self, updates: &ChannelMonitorUpdate, logger: &WithChannelMonitor<L>
3358+
) -> Result<(Vec<Result<Vec<PackageTemplate>, ()>>, bool), ()>
3359+
where L::Target: Logger,
3360+
{
3361+
if self.latest_update_id == CLOSED_CHANNEL_UPDATE_ID && updates.update_id == CLOSED_CHANNEL_UPDATE_ID {
3362+
log_info!(logger, "Applying post-force-closed update to monitor {} with {} change(s).",
3363+
log_funding_info!(self), updates.updates.len());
3364+
} else if updates.update_id == CLOSED_CHANNEL_UPDATE_ID {
3365+
log_info!(logger, "Applying force close update to monitor {} with {} change(s).",
3366+
log_funding_info!(self), updates.updates.len());
3367+
} else {
3368+
log_info!(logger, "Applying update to monitor {}, bringing update_id from {} to {} with {} change(s).",
3369+
log_funding_info!(self), self.latest_update_id, updates.update_id, updates.updates.len());
3370+
}
3371+
3372+
if updates.counterparty_node_id.is_some() {
3373+
if self.counterparty_node_id.is_none() {
3374+
self.counterparty_node_id = updates.counterparty_node_id;
3375+
} else {
3376+
debug_assert_eq!(self.counterparty_node_id, updates.counterparty_node_id);
3377+
}
3378+
}
3379+
3380+
// ChannelMonitor updates may be applied after force close if we receive a preimage for a
3381+
// broadcasted commitment transaction HTLC output that we'd like to claim on-chain. If this
3382+
// is the case, we no longer have guaranteed access to the monitor's update ID, so we use a
3383+
// sentinel value instead.
3384+
//
3385+
// The `ChannelManager` may also queue redundant `ChannelForceClosed` updates if it still
3386+
// thinks the channel needs to have its commitment transaction broadcast, so we'll allow
3387+
// them as well.
3388+
if updates.update_id == CLOSED_CHANNEL_UPDATE_ID {
3389+
assert_eq!(updates.updates.len(), 1);
3390+
match updates.updates[0] {
3391+
ChannelMonitorUpdateStep::ChannelForceClosed { .. } => {},
3392+
// We should have already seen a `ChannelForceClosed` update if we're trying to
3393+
// provide a preimage at this point.
3394+
ChannelMonitorUpdateStep::PaymentPreimage { .. } =>
3395+
debug_assert_eq!(self.latest_update_id, CLOSED_CHANNEL_UPDATE_ID),
3396+
_ => {
3397+
log_error!(logger, "Attempted to apply post-force-close ChannelMonitorUpdate of type {}", updates.updates[0].variant_name());
3398+
panic!("Attempted to apply post-force-close ChannelMonitorUpdate that wasn't providing a payment preimage");
3399+
},
3400+
}
3401+
} else if self.latest_update_id + 1 != updates.update_id {
3402+
panic!("Attempted to apply ChannelMonitorUpdates out of order, check the update_id before passing an update to update_monitor!");
3403+
}
3404+
3405+
let mut update_results = Vec::new();
3406+
for update in updates.updates.iter() {
3407+
let update_result:Result<Vec<PackageTemplate>, ()> = match update {
3408+
ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { commitment_tx, htlc_outputs, claimed_htlcs, nondust_htlc_sources } => {
3409+
log_trace!(logger, "Updating ChannelMonitor with latest holder commitment transaction info");
3410+
if self.lockdown_from_offchain { panic!(); }
3411+
if let Err(e) = self.provide_latest_holder_commitment_tx(commitment_tx.clone(), htlc_outputs.clone(), &claimed_htlcs, nondust_htlc_sources.clone()) {
3412+
log_error!(logger, "Providing latest holder commitment transaction failed/was refused:");
3413+
log_error!(logger, " {}", e);
3414+
Err(())
3415+
} else {
3416+
Ok(vec![])
3417+
}
3418+
}
3419+
ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { commitment_txid, htlc_outputs, commitment_number, their_per_commitment_point, .. } => {
3420+
log_trace!(logger, "Updating ChannelMonitor with latest counterparty commitment transaction info");
3421+
self.provide_latest_counterparty_commitment_tx(*commitment_txid, htlc_outputs.clone(), *commitment_number, *their_per_commitment_point, logger);
3422+
Ok(vec![])
3423+
},
3424+
ChannelMonitorUpdateStep::PaymentPreimage { payment_preimage, payment_info } => {
3425+
log_trace!(logger, "Updating ChannelMonitor with payment preimage");
3426+
Ok(self.provide_payment_preimage_without_broadcasting(&PaymentHash(Sha256::hash(&payment_preimage.0[..]).to_byte_array()), &payment_preimage, payment_info, logger))
3427+
},
3428+
ChannelMonitorUpdateStep::CommitmentSecret { idx, secret } => {
3429+
log_trace!(logger, "Updating ChannelMonitor with commitment secret");
3430+
if let Err(e) = self.provide_secret(*idx, *secret) {
3431+
debug_assert!(false, "Latest counterparty commitment secret was invalid");
3432+
log_error!(logger, "Providing latest counterparty commitment secret failed/was refused:");
3433+
log_error!(logger, " {}", e);
3434+
Err(())
3435+
} else {
3436+
Ok(vec![])
3437+
}
3438+
},
3439+
ChannelMonitorUpdateStep::ChannelForceClosed { should_broadcast } => {
3440+
log_trace!(logger, "Updating ChannelMonitor: channel force closed, should broadcast: {}", should_broadcast);
3441+
self.lockdown_from_offchain = true;
3442+
let res = if *should_broadcast {
3443+
// There's no need to broadcast our commitment transaction if we've seen one
3444+
// confirmed (even with 1 confirmation) as it'll be rejected as
3445+
// duplicate/conflicting.
3446+
let detected_funding_spend = self.funding_spend_confirmed.is_some() ||
3447+
self.onchain_events_awaiting_threshold_conf.iter().find(|event| match event.event {
3448+
OnchainEvent::FundingSpendConfirmation { .. } => true,
3449+
_ => false,
3450+
}).is_some();
3451+
if detected_funding_spend {
3452+
log_trace!(logger, "Avoiding commitment broadcast, already detected confirmed spend onchain");
3453+
continue;
3454+
}
3455+
let (claimable_outpoints, _) = self.generate_claimable_outpoints_and_watch_outputs(ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(true) });
3456+
claimable_outpoints
3457+
} else if !self.holder_tx_signed {
3458+
log_error!(logger, "WARNING: You have a potentially-unsafe holder commitment transaction available to broadcast");
3459+
log_error!(logger, " in channel monitor for channel {}!", &self.channel_id());
3460+
log_error!(logger, " Read the docs for ChannelMonitor::broadcast_latest_holder_commitment_txn to take manual action!");
3461+
vec![]
3462+
} else {
3463+
// If we generated a MonitorEvent::HolderForceClosed, the ChannelManager
3464+
// will still give us a ChannelForceClosed event with !should_broadcast, but we
3465+
// shouldn't print the scary warning above.
3466+
log_info!(logger, "Channel off-chain state closed after we broadcasted our latest commitment transaction.");
3467+
vec![]
3468+
};
3469+
Ok(res)
3470+
},
3471+
ChannelMonitorUpdateStep::ShutdownScript { scriptpubkey } => {
3472+
log_trace!(logger, "Updating ChannelMonitor with shutdown script");
3473+
if let Some(shutdown_script) = self.shutdown_script.replace(scriptpubkey.clone()) {
3474+
panic!("Attempted to replace shutdown script {} with {}", shutdown_script, scriptpubkey);
3475+
}
3476+
Ok(vec![])
3477+
},
3478+
};
3479+
update_results.push(update_result);
3480+
}
3481+
3482+
#[cfg(debug_assertions)] {
3483+
self.counterparty_commitment_txs_from_update(updates);
3484+
}
3485+
3486+
let mut next_updates_allowed = !update_results.iter().any(|r| r.is_err());
3487+
// If the updates succeeded and we were in an already closed channel state, then there's no
3488+
// need to refuse any updates we expect to receive afer seeing a confirmed commitment.
3489+
if next_updates_allowed && updates.update_id == CLOSED_CHANNEL_UPDATE_ID && self.latest_update_id == updates.update_id {
3490+
return Ok((update_results, next_updates_allowed));
3491+
}
3492+
3493+
self.latest_update_id = updates.update_id;
3494+
3495+
// Refuse updates after we've detected a spend onchain, but only if we haven't processed a
3496+
// force closed monitor update yet.
3497+
if next_updates_allowed && self.funding_spend_seen && self.latest_update_id != CLOSED_CHANNEL_UPDATE_ID {
3498+
log_error!(logger, "Refusing Channel Monitor Update as counterparty attempted to update commitment after funding was spent");
3499+
next_updates_allowed = false
3500+
};
3501+
Ok((update_results, next_updates_allowed))
3502+
}
3503+
32493504
fn get_latest_update_id(&self) -> u64 {
32503505
self.latest_update_id
32513506
}
@@ -5175,10 +5430,90 @@ mod tests {
51755430
check_spends!(htlc_txn[0], broadcast_tx);
51765431
check_spends!(htlc_txn[1], broadcast_tx);
51775432
}
5433+
5434+
fn do_test_funding_spend_refuses_updates_without_broadcast(use_local_txn: bool) {
5435+
// Previously, monitor updates were allowed freely even after a funding-spend transaction
5436+
// confirmed. This would allow a race condition where we could receive a payment (including
5437+
// the counterparty revoking their broadcasted state!) and accept it without recourse as
5438+
// long as the ChannelMonitor receives the block first, the full commitment update dance
5439+
// occurs after the block is connected, and before the ChannelManager receives the block.
5440+
// Obviously this is an incredibly contrived race given the counterparty would be risking
5441+
// their full channel balance for it, but its worth fixing nonetheless as it makes the
5442+
// potential ChannelMonitor states simpler to reason about.
5443+
//
5444+
// This test checks said behavior, as well as ensuring a ChannelMonitorUpdate with multiple
5445+
// updates is handled correctly in such conditions.
5446+
let chanmon_cfgs = create_chanmon_cfgs(3);
5447+
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
5448+
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
5449+
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
5450+
let channel = create_announced_chan_between_nodes(&nodes, 0, 1);
5451+
create_announced_chan_between_nodes(&nodes, 1, 2);
5452+
5453+
// Rebalance somewhat
5454+
send_payment(&nodes[0], &[&nodes[1]], 10_000_000);
5455+
5456+
// First route two payments for testing at the end
5457+
let payment_preimage_1 = route_payment(&nodes[0], &[&nodes[1], &nodes[2]], 1_000_000).0;
5458+
let payment_preimage_2 = route_payment(&nodes[0], &[&nodes[1], &nodes[2]], 1_000_000).0;
5459+
5460+
let local_txn = get_local_commitment_txn!(nodes[1], channel.2);
5461+
assert_eq!(local_txn.len(), 1);
5462+
let remote_txn = get_local_commitment_txn!(nodes[0], channel.2);
5463+
assert_eq!(remote_txn.len(), 3); // Commitment and two HTLC-Timeouts
5464+
check_spends!(remote_txn[1], remote_txn[0]);
5465+
check_spends!(remote_txn[2], remote_txn[0]);
5466+
let broadcast_tx = if use_local_txn { &local_txn[0] } else { &remote_txn[0] };
5467+
5468+
// Connect a commitment transaction, but only to the ChainMonitor/ChannelMonitor. The
5469+
// channel is now closed, but the ChannelManager doesn't know that yet.
5470+
let new_header = create_dummy_header(nodes[0].best_block_info().0, 0);
5471+
let conf_height = nodes[0].best_block_info().1 + 1;
5472+
nodes[1].chain_monitor.chain_monitor.transactions_confirmed(&new_header,
5473+
&[(0, broadcast_tx)], conf_height);
5474+
5475+
let (_, pre_update_monitor) = <(BlockHash, ChannelMonitor<InMemorySigner>)>::read(
5476+
&mut io::Cursor::new(&get_monitor!(nodes[1], channel.2).encode()),
5477+
(&nodes[1].keys_manager.backing, &nodes[1].keys_manager.backing)).unwrap();
5478+
5479+
// If the ChannelManager tries to update the channel, however, the ChainMonitor will pass
5480+
// the update through to the ChannelMonitor which will refuse it (as the channel is closed).
5481+
let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[1], nodes[0], 100_000);
5482+
unwrap_send_err!(nodes[1].node.send_payment_with_route(route, payment_hash,
5483+
RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_hash.0)
5484+
), false, APIError::MonitorUpdateInProgress, {});
5485+
check_added_monitors!(nodes[1], 1);
5486+
5487+
// Build a new ChannelMonitorUpdate which contains both the failing commitment tx update
5488+
// and provides the claim preimages for the two pending HTLCs. The first update generates
5489+
// an error, but the point of this test is to ensure the later updates are still applied.
5490+
let monitor_updates = nodes[1].chain_monitor.monitor_updates.lock().unwrap();
5491+
let mut replay_update = monitor_updates.get(&channel.2).unwrap().iter().rev().next().unwrap().clone();
5492+
assert_eq!(replay_update.updates.len(), 1);
5493+
if let ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { .. } = replay_update.updates[0] {
5494+
} else { panic!(); }
5495+
replay_update.updates.push(ChannelMonitorUpdateStep::PaymentPreimage {
5496+
payment_preimage: payment_preimage_1, payment_info: None,
5497+
});
5498+
replay_update.updates.push(ChannelMonitorUpdateStep::PaymentPreimage {
5499+
payment_preimage: payment_preimage_2, payment_info: None,
5500+
});
5501+
5502+
let res = pre_update_monitor.update_monitor_without_broadcast(&replay_update, &nodes[1].logger).unwrap();
5503+
assert_eq!(false, res.1);
5504+
5505+
// Even though we error'd on the first update, we should still have generated an HTLC claim
5506+
// transaction
5507+
let txn_broadcasted = res.0;
5508+
assert!(txn_broadcasted.iter().fold(0, |len, r| len + r.as_ref().unwrap_or(&vec![]).len()) >= 2);
5509+
}
5510+
51785511
#[test]
51795512
fn test_funding_spend_refuses_updates() {
51805513
do_test_funding_spend_refuses_updates(true);
51815514
do_test_funding_spend_refuses_updates(false);
5515+
do_test_funding_spend_refuses_updates_without_broadcast(true);
5516+
do_test_funding_spend_refuses_updates_without_broadcast(false);
51825517
}
51835518

51845519
#[test]

0 commit comments

Comments
 (0)