Skip to content

Commit 97c3969

Browse files
committed
Refactor channel funding transaction fields
1 parent 870a0f1 commit 97c3969

File tree

1 file changed

+97
-52
lines changed

1 file changed

+97
-52
lines changed

lightning/src/ln/channel.rs

+97-52
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,43 @@ impl UnfundedChannelContext {
686686
}
687687
}
688688

689+
/// Info about a transaction and its confirmation status, used mainly for funding transactions.
690+
pub(super) struct TransactionConfirmation {
691+
/// The transaction, or None.
692+
transaction: Option<Transaction>,
693+
/// The hash of the block in which the transaction was included, or None.
694+
confirmed_in: Option<BlockHash>,
695+
/// The height of the block in which the transaction was included, or 0.
696+
confirmation_height: u32,
697+
}
698+
699+
impl TransactionConfirmation {
700+
/// Construct with empty values
701+
fn default() -> Self {
702+
Self {
703+
transaction: None,
704+
confirmed_in: None,
705+
confirmation_height: 0,
706+
}
707+
}
708+
709+
/// Get the confirmation depth: height relative to the given current height.
710+
/// Also returns a flag indicating the special case when the confirmation is in the 'future'.
711+
/// If there is no confirmation height (it was not confirmed, or confirmed and reorged): (0, false)
712+
/// If the confirmation height is in the 'future' (e.g. due to a reorg): (0, true)
713+
/// Otherwise the result is height - confirmation_height + 1, a number always larger than 0.
714+
fn confirmation_depth(&self, current_height: u32) -> (u32, bool) {
715+
if self.confirmation_height == 0 {
716+
(0, false)
717+
} else {
718+
match current_height.checked_sub(self.confirmation_height) {
719+
None => (0, true),
720+
Some(d) => (d + 1, false),
721+
}
722+
}
723+
}
724+
}
725+
689726
/// Contains everything about the channel including state, and various flags.
690727
pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
691728
config: LegacyChannelConfig,
@@ -829,9 +866,6 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
829866
/// milliseconds, so any accidental force-closes here should be exceedingly rare.
830867
expecting_peer_commitment_signed: bool,
831868

832-
/// The hash of the block in which the funding transaction was included.
833-
funding_tx_confirmed_in: Option<BlockHash>,
834-
funding_tx_confirmation_height: u32,
835869
short_channel_id: Option<u64>,
836870
/// Either the height at which this channel was created or the height at which it was last
837871
/// serialized if it was serialized by versions prior to 0.0.103.
@@ -875,7 +909,8 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
875909
counterparty_forwarding_info: Option<CounterpartyForwardingInfo>,
876910

877911
pub(crate) channel_transaction_parameters: ChannelTransactionParameters,
878-
funding_transaction: Option<Transaction>,
912+
/// Info about the funding transaction and its confirmation status
913+
funding_tx_confirmation: TransactionConfirmation,
879914
is_batch_funding: Option<()>,
880915

881916
counterparty_cur_commitment_point: Option<PublicKey>,
@@ -1113,17 +1148,12 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
11131148

11141149
/// Returns the block hash in which our funding transaction was confirmed.
11151150
pub fn get_funding_tx_confirmed_in(&self) -> Option<BlockHash> {
1116-
self.funding_tx_confirmed_in
1151+
self.funding_tx_confirmation.confirmed_in
11171152
}
11181153

11191154
/// Returns the current number of confirmations on the funding transaction.
11201155
pub fn get_funding_tx_confirmations(&self, height: u32) -> u32 {
1121-
if self.funding_tx_confirmation_height == 0 {
1122-
// We either haven't seen any confirmation yet, or observed a reorg.
1123-
return 0;
1124-
}
1125-
1126-
height.checked_sub(self.funding_tx_confirmation_height).map_or(0, |c| c + 1)
1156+
self.funding_tx_confirmation.confirmation_depth(height).0
11271157
}
11281158

11291159
fn get_holder_selected_contest_delay(&self) -> u16 {
@@ -2048,7 +2078,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
20482078
/// Returns the transaction if there is a pending funding transaction that is yet to be
20492079
/// broadcast.
20502080
pub fn unbroadcasted_funding(&self) -> Option<Transaction> {
2051-
self.if_unbroadcasted_funding(|| self.funding_transaction.clone())
2081+
self.if_unbroadcasted_funding(|| self.funding_tx_confirmation.transaction.clone())
20522082
}
20532083

20542084
/// Returns the transaction ID if there is a pending funding transaction that is yet to be
@@ -3896,7 +3926,7 @@ impl<SP: Deref> Channel<SP> where
38963926
// first received the funding_signed.
38973927
let mut funding_broadcastable =
38983928
if self.context.is_outbound() && self.context.channel_state & !STATE_FLAGS >= ChannelState::FundingSent as u32 && self.context.channel_state & ChannelState::WaitingForBatch as u32 == 0 {
3899-
self.context.funding_transaction.take()
3929+
self.context.funding_tx_confirmation.transaction.take()
39003930
} else { None };
39013931
// That said, if the funding transaction is already confirmed (ie we're active with a
39023932
// minimum_depth over 0) don't bother re-broadcasting the confirmed funding tx.
@@ -4885,7 +4915,7 @@ impl<SP: Deref> Channel<SP> where
48854915
// Because deciding we're awaiting initial broadcast spuriously could result in
48864916
// funds-loss (as we don't have a monitor, but have the funding transaction confirmed),
48874917
// we hard-assert here, even in production builds.
4888-
if self.context.is_outbound() { assert!(self.context.funding_transaction.is_some()); }
4918+
if self.context.is_outbound() { assert!(self.context.funding_tx_confirmation.transaction.is_some()); }
48894919
assert!(self.context.monitor_pending_channel_ready);
48904920
assert_eq!(self.context.latest_monitor_update_id, 0);
48914921
return true;
@@ -4931,16 +4961,16 @@ impl<SP: Deref> Channel<SP> where
49314961
// Called:
49324962
// * always when a new block/transactions are confirmed with the new height
49334963
// * when funding is signed with a height of 0
4934-
if self.context.funding_tx_confirmation_height == 0 && self.context.minimum_depth != Some(0) {
4964+
if self.context.funding_tx_confirmation.confirmation_height == 0 && self.context.minimum_depth != Some(0) {
49354965
return None;
49364966
}
49374967

4938-
let funding_tx_confirmations = height as i64 - self.context.funding_tx_confirmation_height as i64 + 1;
4939-
if funding_tx_confirmations <= 0 {
4940-
self.context.funding_tx_confirmation_height = 0;
4968+
let (funding_tx_confirmations, in_future) = self.context.funding_tx_confirmation.confirmation_depth(height);
4969+
if in_future {
4970+
self.context.funding_tx_confirmation.confirmation_height = 0;
49414971
}
49424972

4943-
if funding_tx_confirmations < self.context.minimum_depth.unwrap_or(0) as i64 {
4973+
if funding_tx_confirmations < self.context.minimum_depth.unwrap_or(0) {
49444974
return None;
49454975
}
49464976

@@ -4964,7 +4994,7 @@ impl<SP: Deref> Channel<SP> where
49644994
// We got a reorg but not enough to trigger a force close, just ignore.
49654995
false
49664996
} else {
4967-
if self.context.funding_tx_confirmation_height != 0 && self.context.channel_state & !STATE_FLAGS < ChannelState::ChannelReady as u32 {
4997+
if self.context.funding_tx_confirmation.confirmation_height != 0 && self.context.channel_state & !STATE_FLAGS < ChannelState::ChannelReady as u32 {
49684998
// We should never see a funding transaction on-chain until we've received
49694999
// funding_signed (if we're an outbound channel), or seen funding_generated (if we're
49705000
// an inbound channel - before that we have no known funding TXID). The fuzzer,
@@ -5012,7 +5042,7 @@ impl<SP: Deref> Channel<SP> where
50125042
for &(index_in_block, tx) in txdata.iter() {
50135043
// Check if the transaction is the expected funding transaction, and if it is,
50145044
// check that it pays the right amount to the right script.
5015-
if self.context.funding_tx_confirmation_height == 0 {
5045+
if self.context.funding_tx_confirmation.confirmation_height == 0 {
50165046
if tx.txid() == funding_txo.txid {
50175047
let txo_idx = funding_txo.index as usize;
50185048
if txo_idx >= tx.output.len() || tx.output[txo_idx].script_pubkey != self.context.get_funding_redeemscript().to_v0_p2wsh() ||
@@ -5042,8 +5072,8 @@ impl<SP: Deref> Channel<SP> where
50425072
}
50435073
}
50445074
}
5045-
self.context.funding_tx_confirmation_height = height;
5046-
self.context.funding_tx_confirmed_in = Some(*block_hash);
5075+
self.context.funding_tx_confirmation.confirmation_height = height;
5076+
self.context.funding_tx_confirmation.confirmed_in = Some(*block_hash);
50475077
self.context.short_channel_id = match scid_from_parts(height as u64, index_in_block as u64, txo_idx as u64) {
50485078
Ok(scid) => Some(scid),
50495079
Err(_) => panic!("Block was bogus - either height was > 16 million, had > 16 million transactions, or had > 65k outputs"),
@@ -5137,13 +5167,10 @@ impl<SP: Deref> Channel<SP> where
51375167
let non_shutdown_state = self.context.channel_state & (!MULTI_STATE_FLAGS);
51385168
if non_shutdown_state & !STATE_FLAGS >= ChannelState::ChannelReady as u32 ||
51395169
(non_shutdown_state & ChannelState::OurChannelReady as u32) == ChannelState::OurChannelReady as u32 {
5140-
let mut funding_tx_confirmations = height as i64 - self.context.funding_tx_confirmation_height as i64 + 1;
5141-
if self.context.funding_tx_confirmation_height == 0 {
5142-
// Note that check_get_channel_ready may reset funding_tx_confirmation_height to
5143-
// zero if it has been reorged out, however in either case, our state flags
5144-
// indicate we've already sent a channel_ready
5145-
funding_tx_confirmations = 0;
5146-
}
5170+
let (funding_tx_confirmations, _) = self.context.funding_tx_confirmation.confirmation_depth(height);
5171+
// Note that check_get_channel_ready may reset funding_tx_confirmation.confirmation_height to
5172+
// zero if it has been reorged out, however in either case, our state flags
5173+
// indicate we've already sent a channel_ready
51475174

51485175
// If we've sent channel_ready (or have both sent and received channel_ready), and
51495176
// the funding transaction has become unconfirmed,
@@ -5154,15 +5181,15 @@ impl<SP: Deref> Channel<SP> where
51545181
// 0-conf channel, but not doing so may lead to the
51555182
// `ChannelManager::short_to_chan_info` map being inconsistent, so we currently have
51565183
// to.
5157-
if funding_tx_confirmations == 0 && self.context.funding_tx_confirmed_in.is_some() {
5184+
if funding_tx_confirmations == 0 && self.context.funding_tx_confirmation.confirmed_in.is_some() {
51585185
let err_reason = format!("Funding transaction was un-confirmed. Locked at {} confs, now have {} confs.",
51595186
self.context.minimum_depth.unwrap(), funding_tx_confirmations);
51605187
return Err(ClosureReason::ProcessingError { err: err_reason });
51615188
}
5162-
} else if !self.context.is_outbound() && self.context.funding_tx_confirmed_in.is_none() &&
5189+
} else if !self.context.is_outbound() && self.context.funding_tx_confirmation.confirmed_in.is_none() &&
51635190
height >= self.context.channel_creation_height + FUNDING_CONF_DEADLINE_BLOCKS {
51645191
log_info!(logger, "Closing channel {} due to funding timeout", &self.context.channel_id);
5165-
// If funding_tx_confirmed_in is unset, the channel must not be active
5192+
// If funding_tx_confirmation.confirmed_in is unset, the channel must not be active
51665193
assert!(non_shutdown_state & !STATE_FLAGS <= ChannelState::ChannelReady as u32);
51675194
assert_eq!(non_shutdown_state & ChannelState::OurChannelReady as u32, 0);
51685195
return Err(ClosureReason::FundingTimedOut);
@@ -5178,10 +5205,10 @@ impl<SP: Deref> Channel<SP> where
51785205
/// force-close the channel, but may also indicate a harmless reorganization of a block or two
51795206
/// before the channel has reached channel_ready and we can just wait for more blocks.
51805207
pub fn funding_transaction_unconfirmed<L: Deref>(&mut self, logger: &L) -> Result<(), ClosureReason> where L::Target: Logger {
5181-
if self.context.funding_tx_confirmation_height != 0 {
5208+
if self.context.funding_tx_confirmation.confirmation_height > 0 {
51825209
// We handle the funding disconnection by calling best_block_updated with a height one
51835210
// below where our funding was connected, implying a reorg back to conf_height - 1.
5184-
let reorg_height = self.context.funding_tx_confirmation_height - 1;
5211+
let reorg_height = self.context.funding_tx_confirmation.confirmation_height - 1;
51855212
// We use the time field to bump the current time we set on channel updates if its
51865213
// larger. If we don't know that time has moved forward, we can just set it to the last
51875214
// time we saw and it will be ignored.
@@ -5254,7 +5281,7 @@ impl<SP: Deref> Channel<SP> where
52545281
NS::Target: NodeSigner,
52555282
L::Target: Logger
52565283
{
5257-
if self.context.funding_tx_confirmation_height == 0 || self.context.funding_tx_confirmation_height + 5 > best_block_height {
5284+
if self.context.funding_tx_confirmation.confirmation_height == 0 || self.context.funding_tx_confirmation.confirmation_height + 5 > best_block_height {
52585285
return None;
52595286
}
52605287

@@ -5365,7 +5392,7 @@ impl<SP: Deref> Channel<SP> where
53655392
}
53665393

53675394
self.context.announcement_sigs = Some((msg.node_signature, msg.bitcoin_signature));
5368-
if self.context.funding_tx_confirmation_height == 0 || self.context.funding_tx_confirmation_height + 5 > best_block_height {
5395+
if self.context.funding_tx_confirmation.confirmation_height == 0 || self.context.funding_tx_confirmation.confirmation_height + 5 > best_block_height {
53695396
return Err(ChannelError::Ignore(
53705397
"Got announcement_signatures prior to the required six confirmations - we may not have received a block yet that our peer has".to_owned()));
53715398
}
@@ -5378,7 +5405,7 @@ impl<SP: Deref> Channel<SP> where
53785405
pub fn get_signed_channel_announcement<NS: Deref>(
53795406
&self, node_signer: &NS, chain_hash: ChainHash, best_block_height: u32, user_config: &UserConfig
53805407
) -> Option<msgs::ChannelAnnouncement> where NS::Target: NodeSigner {
5381-
if self.context.funding_tx_confirmation_height == 0 || self.context.funding_tx_confirmation_height + 5 > best_block_height {
5408+
if self.context.funding_tx_confirmation.confirmation_height == 0 || self.context.funding_tx_confirmation.confirmation_height + 5 > best_block_height {
53825409
return None;
53835410
}
53845411
let announcement = match self.get_channel_announcement(node_signer, chain_hash, user_config) {
@@ -6020,8 +6047,6 @@ impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider {
60206047
closing_fee_limits: None,
60216048
target_closing_feerate_sats_per_kw: None,
60226049

6023-
funding_tx_confirmed_in: None,
6024-
funding_tx_confirmation_height: 0,
60256050
short_channel_id: None,
60266051
channel_creation_height: current_chain_height,
60276052

@@ -6048,7 +6073,7 @@ impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider {
60486073
funding_outpoint: None,
60496074
channel_type_features: channel_type.clone()
60506075
},
6051-
funding_transaction: None,
6076+
funding_tx_confirmation: TransactionConfirmation::default(),
60526077
is_batch_funding: None,
60536078

60546079
counterparty_cur_commitment_point: None,
@@ -6127,7 +6152,7 @@ impl<SP: Deref> OutboundV1Channel<SP> where SP::Target: SignerProvider {
61276152
self.context.minimum_depth = Some(COINBASE_MATURITY);
61286153
}
61296154

6130-
self.context.funding_transaction = Some(funding_transaction);
6155+
self.context.funding_tx_confirmation.transaction = Some(funding_transaction);
61316156
self.context.is_batch_funding = Some(()).filter(|_| is_batch_funding);
61326157

61336158
let funding_created = self.context.get_funding_created_msg(logger);
@@ -6653,8 +6678,6 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
66536678
closing_fee_limits: None,
66546679
target_closing_feerate_sats_per_kw: None,
66556680

6656-
funding_tx_confirmed_in: None,
6657-
funding_tx_confirmation_height: 0,
66586681
short_channel_id: None,
66596682
channel_creation_height: current_chain_height,
66606683

@@ -6685,7 +6708,7 @@ impl<SP: Deref> InboundV1Channel<SP> where SP::Target: SignerProvider {
66856708
funding_outpoint: None,
66866709
channel_type_features: channel_type.clone()
66876710
},
6688-
funding_transaction: None,
6711+
funding_tx_confirmation: TransactionConfirmation::default(),
66896712
is_batch_funding: None,
66906713

66916714
counterparty_cur_commitment_point: Some(msg.first_per_commitment_point),
@@ -7162,8 +7185,8 @@ impl<SP: Deref> Writeable for Channel<SP> where SP::Target: SignerProvider {
71627185
// consider the stale state on reload.
71637186
0u8.write(writer)?;
71647187

7165-
self.context.funding_tx_confirmed_in.write(writer)?;
7166-
self.context.funding_tx_confirmation_height.write(writer)?;
7188+
self.context.funding_tx_confirmation.confirmed_in.write(writer)?;
7189+
self.context.funding_tx_confirmation.confirmation_height.write(writer)?;
71677190
self.context.short_channel_id.write(writer)?;
71687191

71697192
self.context.counterparty_dust_limit_satoshis.write(writer)?;
@@ -7191,7 +7214,7 @@ impl<SP: Deref> Writeable for Channel<SP> where SP::Target: SignerProvider {
71917214
}
71927215

71937216
self.context.channel_transaction_parameters.write(writer)?;
7194-
self.context.funding_transaction.write(writer)?;
7217+
self.context.funding_tx_confirmation.transaction.write(writer)?;
71957218

71967219
self.context.counterparty_cur_commitment_point.write(writer)?;
71977220
self.context.counterparty_prev_commitment_point.write(writer)?;
@@ -7725,8 +7748,6 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
77257748
closing_fee_limits: None,
77267749
target_closing_feerate_sats_per_kw,
77277750

7728-
funding_tx_confirmed_in,
7729-
funding_tx_confirmation_height,
77307751
short_channel_id,
77317752
channel_creation_height: channel_creation_height.unwrap(),
77327753

@@ -7744,7 +7765,11 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
77447765
counterparty_forwarding_info,
77457766

77467767
channel_transaction_parameters: channel_parameters,
7747-
funding_transaction,
7768+
funding_tx_confirmation: TransactionConfirmation {
7769+
transaction: funding_transaction,
7770+
confirmed_in: funding_tx_confirmed_in,
7771+
confirmation_height: funding_tx_confirmation_height,
7772+
},
77487773
is_batch_funding,
77497774

77507775
counterparty_cur_commitment_point,
@@ -7799,7 +7824,7 @@ mod tests {
77997824
use crate::ln::PaymentHash;
78007825
use crate::ln::channelmanager::{self, HTLCSource, PaymentId};
78017826
use crate::ln::channel::InitFeatures;
7802-
use crate::ln::channel::{ChannelState, InboundHTLCOutput, OutboundV1Channel, InboundV1Channel, OutboundHTLCOutput, InboundHTLCState, OutboundHTLCState, HTLCCandidate, HTLCInitiator, commit_tx_fee_msat};
7827+
use crate::ln::channel::{ChannelState, InboundHTLCOutput, OutboundV1Channel, InboundV1Channel, OutboundHTLCOutput, InboundHTLCState, OutboundHTLCState, HTLCCandidate, HTLCInitiator, TransactionConfirmation, commit_tx_fee_msat};
78037828
use crate::ln::channel::{MAX_FUNDING_SATOSHIS_NO_WUMBO, TOTAL_BITCOIN_SUPPLY_SATOSHIS, MIN_THEIR_CHAN_RESERVE_SATOSHIS};
78047829
use crate::ln::features::ChannelTypeFeatures;
78057830
use crate::ln::msgs::{ChannelUpdate, DecodeError, UnsignedChannelUpdate, MAX_VALUE_MSAT};
@@ -7841,6 +7866,26 @@ mod tests {
78417866
"MAX_FUNDING_SATOSHIS_NO_WUMBO is greater than all satoshis in existence");
78427867
}
78437868

7869+
#[test]
7870+
fn test_transaction_confirmation_depth() {
7871+
{
7872+
let tx_conf = TransactionConfirmation { transaction: None, confirmed_in: None, confirmation_height: 0 };
7873+
assert_eq!(tx_conf.confirmation_depth(42), (0, false));
7874+
}
7875+
{
7876+
let tx_conf = TransactionConfirmation { transaction: None, confirmed_in: None, confirmation_height: 100005 };
7877+
assert_eq!(tx_conf.confirmation_depth(100008), (4, false));
7878+
}
7879+
{
7880+
let tx_conf = TransactionConfirmation { transaction: None, confirmed_in: None, confirmation_height: 100005 };
7881+
assert_eq!(tx_conf.confirmation_depth(100005), (1, false));
7882+
}
7883+
{
7884+
// confirmation_height is larger than current, 'in the future'
7885+
let tx_conf = TransactionConfirmation { transaction: None, confirmed_in: None, confirmation_height: 100005 };
7886+
assert_eq!(tx_conf.confirmation_depth(100000), (0, true));
7887+
}
7888+
}
78447889
struct Keys {
78457890
signer: InMemorySigner,
78467891
}

0 commit comments

Comments
 (0)