Skip to content

Commit ce0c534

Browse files
committed
Make Channel's block connection API more electrum-friendly
Electrum clients primarily operate in a world where they query (and subscribe to notifications for) transactions by script_pubkeys. They may never learn very much about the actual blockchain and orient their events around individual transactions, not the blockchain. This makes our ChannelManager interface somewhat more amenable to such a client by splitting `block_connected` into `transactions_confirmed` and `update_best_block`. The first handles checking the funding transaction and storing its height/confirmation block, whereas the second handles funding_locked and reorg logic. Sadly, this interface is somewhat easy to misuse - notifying the channel of the funding transaction being reorganized out of the chain is complicated when the only notification received is that a new block is connected at a given height. This will be addressed in a future commit.
1 parent 5815e63 commit ce0c534

File tree

1 file changed

+66
-43
lines changed

1 file changed

+66
-43
lines changed

lightning/src/ln/channel.rs

Lines changed: 66 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3502,34 +3502,7 @@ impl<Signer: Sign> Channel<Signer> {
35023502
self.network_sync == UpdateStatus::DisabledMarked
35033503
}
35043504

3505-
/// When we receive a new block, we (a) check whether the block contains the funding
3506-
/// transaction (which would start us counting blocks until we send the funding_signed), and
3507-
/// (b) check the height of the block against outbound holding cell HTLCs in case we need to
3508-
/// give up on them prematurely and time them out. Everything else (e.g. commitment
3509-
/// transaction broadcasts, channel closure detection, HTLC transaction broadcasting, etc) is
3510-
/// handled by the ChannelMonitor.
3511-
///
3512-
/// If we return Err, the channel may have been closed, at which point the standard
3513-
/// requirements apply - no calls may be made except those explicitly stated to be allowed
3514-
/// post-shutdown.
3515-
/// Only returns an ErrorAction of DisconnectPeer, if Err.
3516-
///
3517-
/// May return some HTLCs (and their payment_hash) which have timed out and should be failed
3518-
/// back.
3519-
pub fn block_connected(&mut self, header: &BlockHeader, txdata: &TransactionData, height: u32) -> Result<(Option<msgs::FundingLocked>, Vec<(HTLCSource, PaymentHash)>), msgs::ErrorMessage> {
3520-
let mut timed_out_htlcs = Vec::new();
3521-
self.holding_cell_htlc_updates.retain(|htlc_update| {
3522-
match htlc_update {
3523-
&HTLCUpdateAwaitingACK::AddHTLC { ref payment_hash, ref source, ref cltv_expiry, .. } => {
3524-
if *cltv_expiry <= height + HTLC_FAIL_BACK_BUFFER {
3525-
timed_out_htlcs.push((source.clone(), payment_hash.clone()));
3526-
false
3527-
} else { true }
3528-
},
3529-
_ => true
3530-
}
3531-
});
3532-
3505+
pub fn transactions_confirmed(&mut self, block_hash: &BlockHash, height: u32, txdata: &TransactionData) -> Result<(), msgs::ErrorMessage> {
35333506
let non_shutdown_state = self.channel_state & (!MULTI_STATE_FLAGS);
35343507
if non_shutdown_state & !(ChannelState::TheirFundingLocked as u32) == ChannelState::FundingSent as u32 {
35353508
for &(index_in_block, tx) in txdata.iter() {
@@ -3565,7 +3538,7 @@ impl<Signer: Sign> Channel<Signer> {
35653538
}
35663539
}
35673540
self.funding_tx_confirmation_height = height as u64;
3568-
self.funding_tx_confirmed_in = Some(header.block_hash());
3541+
self.funding_tx_confirmed_in = Some(*block_hash);
35693542
self.short_channel_id = match scid_from_parts(height as u64, index_in_block as u64, txo_idx as u64) {
35703543
Ok(scid) => Some(scid),
35713544
Err(_) => panic!("Block was bogus - either height was > 16 million, had > 16 million transactions, or had > 65k outputs"),
@@ -3574,11 +3547,51 @@ impl<Signer: Sign> Channel<Signer> {
35743547
}
35753548
}
35763549
}
3550+
Ok(())
3551+
}
35773552

3553+
/// When a new block is connected, we check the height of the block against outbound holding
3554+
/// cell HTLCs in case we need to give up on them prematurely and time them out. Everything
3555+
/// else (e.g. commitment transaction broadcasts, channel closure detection, HTLC transaction
3556+
/// broadcasting, etc) is handled by the ChannelMonitor.
3557+
///
3558+
/// If we return Err, the channel may have been closed, at which point the standard
3559+
/// requirements apply - no calls may be made except those explicitly stated to be allowed
3560+
/// post-shutdown.
3561+
///
3562+
/// May return some HTLCs (and their payment_hash) which have timed out and should be failed
3563+
/// back.
3564+
pub fn update_best_block(&mut self, height: u32, highest_header_time: u32) -> Result<(Option<msgs::FundingLocked>, Vec<(HTLCSource, PaymentHash)>), msgs::ErrorMessage> {
3565+
let mut timed_out_htlcs = Vec::new();
3566+
self.holding_cell_htlc_updates.retain(|htlc_update| {
3567+
match htlc_update {
3568+
&HTLCUpdateAwaitingACK::AddHTLC { ref payment_hash, ref source, ref cltv_expiry, .. } => {
3569+
if *cltv_expiry <= height + HTLC_FAIL_BACK_BUFFER {
3570+
timed_out_htlcs.push((source.clone(), payment_hash.clone()));
3571+
false
3572+
} else { true }
3573+
},
3574+
_ => true
3575+
}
3576+
});
35783577

3579-
self.update_time_counter = cmp::max(self.update_time_counter, header.time);
3578+
self.update_time_counter = cmp::max(self.update_time_counter, highest_header_time);
35803579
if self.funding_tx_confirmation_height > 0 {
35813580
let funding_tx_confirmations = height as i64 - self.funding_tx_confirmation_height as i64 + 1;
3581+
if funding_tx_confirmations <= 0 {
3582+
self.funding_tx_confirmation_height = 0;
3583+
}
3584+
3585+
let non_shutdown_state = self.channel_state & (!MULTI_STATE_FLAGS);
3586+
if (non_shutdown_state >= ChannelState::ChannelFunded as u32 ||
3587+
(non_shutdown_state & ChannelState::OurFundingLocked as u32) == ChannelState::OurFundingLocked as u32) &&
3588+
funding_tx_confirmations < self.minimum_depth as i64 / 2 {
3589+
return Err(msgs::ErrorMessage {
3590+
channel_id: self.channel_id(),
3591+
data: format!("Funding transaction was un-confirmed. Locked at {} confs, now have {} confs.", self.minimum_depth, funding_tx_confirmations),
3592+
});
3593+
}
3594+
35823595
if funding_tx_confirmations == self.minimum_depth as i64 {
35833596
let need_commitment_update = if non_shutdown_state == ChannelState::FundingSent as u32 {
35843597
self.channel_state |= ChannelState::OurFundingLocked as u32;
@@ -3617,25 +3630,35 @@ impl<Signer: Sign> Channel<Signer> {
36173630
}
36183631
}
36193632
}
3633+
36203634
Ok((None, timed_out_htlcs))
36213635
}
36223636

3637+
/// When we receive a new block, we (a) check whether the block contains the funding
3638+
/// transaction (which would start us counting blocks until we send the funding_signed), and
3639+
/// (b) check the height of the block against outbound holding cell HTLCs in case we need to
3640+
/// give up on them prematurely and time them out. Everything else (e.g. commitment
3641+
/// transaction broadcasts, channel closure detection, HTLC transaction broadcasting, etc) is
3642+
/// handled by the ChannelMonitor.
3643+
///
3644+
/// If we return Err, the channel may have been closed, at which point the standard
3645+
/// requirements apply - no calls may be made except those explicitly stated to be allowed
3646+
/// post-shutdown.
3647+
/// Only returns an ErrorAction of DisconnectPeer, if Err.
3648+
///
3649+
/// May return some HTLCs (and their payment_hash) which have timed out and should be failed
3650+
/// back.
3651+
pub fn block_connected(&mut self, header: &BlockHeader, txdata: &TransactionData, height: u32) -> Result<(Option<msgs::FundingLocked>, Vec<(HTLCSource, PaymentHash)>), msgs::ErrorMessage> {
3652+
self.transactions_confirmed(&header.block_hash(), height, txdata)?;
3653+
self.update_best_block(height, header.time)
3654+
}
3655+
36233656
/// Called by channelmanager based on chain blocks being disconnected.
36243657
/// Returns true if we need to close the channel now due to funding transaction
36253658
/// unconfirmation/reorg.
3626-
pub fn block_disconnected(&mut self, header: &BlockHeader, height: u32) -> bool {
3627-
if self.funding_tx_confirmation_height > 0 {
3628-
let funding_tx_confirmations = height as i64 - self.funding_tx_confirmation_height as i64 + 1;
3629-
if funding_tx_confirmations <= 0 {
3630-
self.funding_tx_confirmation_height = 0;
3631-
}
3632-
3633-
let non_shutdown_state = self.channel_state & (!MULTI_STATE_FLAGS);
3634-
if (non_shutdown_state >= ChannelState::ChannelFunded as u32 ||
3635-
(non_shutdown_state & ChannelState::OurFundingLocked as u32) == ChannelState::OurFundingLocked as u32) &&
3636-
funding_tx_confirmations < self.minimum_depth as i64 / 2 {
3637-
return true;
3638-
}
3659+
pub fn block_disconnected(&mut self, header: &BlockHeader, new_height: u32) -> bool {
3660+
if self.update_best_block(new_height, header.time).is_err() {
3661+
return true;
36393662
}
36403663
false
36413664
}

0 commit comments

Comments
 (0)