X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Fchannel.rs;h=796c041f8185d78d68972370726fdccb96fb4fd5;hb=26b515c13cccd1d027e67d0c65d69321d235ce40;hp=da1364021cefe99e9f1b63e2ecd215d8f67b438e;hpb=e9d9711de4ddc20b78eb110abfe400da6eef863d;p=rust-lightning diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index da136402..796c041f 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -594,6 +594,9 @@ pub(crate) const DISCONNECT_PEER_AWAITING_RESPONSE_TICKS: usize = 2; /// exceeding this age limit will be force-closed and purged from memory. pub(crate) const UNFUNDED_CHANNEL_AGE_LIMIT_TICKS: usize = 60; +/// Number of blocks needed for an output from a coinbase transaction to be spendable. +pub(crate) const COINBASE_MATURITY: u32 = 100; + struct PendingChannelMonitorUpdate { update: ChannelMonitorUpdate, } @@ -602,6 +605,35 @@ impl_writeable_tlv_based!(PendingChannelMonitorUpdate, { (0, update, required), }); +/// The `ChannelPhase` enum describes the current phase in life of a lightning channel with each of +/// its variants containing an appropriate channel struct. +pub(super) enum ChannelPhase where SP::Target: SignerProvider { + UnfundedOutboundV1(OutboundV1Channel), + UnfundedInboundV1(InboundV1Channel), + Funded(Channel), +} + +impl<'a, SP: Deref> ChannelPhase where + SP::Target: SignerProvider, + ::Signer: ChannelSigner, +{ + pub fn context(&'a self) -> &'a ChannelContext { + match self { + ChannelPhase::Funded(chan) => &chan.context, + ChannelPhase::UnfundedOutboundV1(chan) => &chan.context, + ChannelPhase::UnfundedInboundV1(chan) => &chan.context, + } + } + + pub fn context_mut(&'a mut self) -> &'a mut ChannelContext { + match self { + ChannelPhase::Funded(ref mut chan) => &mut chan.context, + ChannelPhase::UnfundedOutboundV1(ref mut chan) => &mut chan.context, + ChannelPhase::UnfundedInboundV1(ref mut chan) => &mut chan.context, + } + } +} + /// Contains all state common to unfunded inbound/outbound channels. pub(super) struct UnfundedChannelContext { /// A counter tracking how many ticks have elapsed since this unfunded channel was @@ -2033,11 +2065,6 @@ fn commit_tx_fee_msat(feerate_per_kw: u32, num_htlcs: usize, channel_type_featur (commitment_tx_base_weight(channel_type_features) + num_htlcs as u64 * COMMITMENT_TX_WEIGHT_PER_HTLC) * feerate_per_kw as u64 / 1000 * 1000 } -// TODO: We should refactor this to be an Inbound/OutboundChannel until initial setup handshaking -// has been completed, and then turn into a Channel to get compiler-time enforcement of things like -// calling channel_id() before we're set up or things like get_funding_signed on an -// inbound channel. -// // Holder designates channel data owned for the benefit of the user client. // Counterparty designates channel data owned by the another channel participant entity. pub(super) struct Channel where SP::Target: SignerProvider { @@ -4734,12 +4761,14 @@ impl Channel where return Err(ClosureReason::ProcessingError { err: err_reason.to_owned() }); } else { if self.context.is_outbound() { - for input in tx.input.iter() { - if input.witness.is_empty() { - // We generated a malleable funding transaction, implying we've - // just exposed ourselves to funds loss to our counterparty. - #[cfg(not(fuzzing))] - panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction!"); + if !tx.is_coin_base() { + for input in tx.input.iter() { + if input.witness.is_empty() { + // We generated a malleable funding transaction, implying we've + // just exposed ourselves to funds loss to our counterparty. + #[cfg(not(fuzzing))] + panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction!"); + } } } } @@ -4750,6 +4779,13 @@ impl Channel where Err(_) => panic!("Block was bogus - either height was > 16 million, had > 16 million transactions, or had > 65k outputs"), } } + // If this is a coinbase transaction and not a 0-conf channel + // we should update our min_depth to 100 to handle coinbase maturity + if tx.is_coin_base() && + self.context.minimum_depth.unwrap_or(0) > 0 && + self.context.minimum_depth.unwrap_or(0) < COINBASE_MATURITY { + self.context.minimum_depth = Some(COINBASE_MATURITY); + } } // If we allow 1-conf funding, we may need to check for channel_ready here and // send it immediately instead of waiting for a best_block_updated call (which @@ -5821,6 +5857,15 @@ impl OutboundV1Channel where SP::Target: SignerProvider { self.context.channel_state = ChannelState::FundingCreated as u32; self.context.channel_id = funding_txo.to_channel_id(); + + // If the funding transaction is a coinbase transaction, we need to set the minimum depth to 100. + // We can skip this if it is a zero-conf channel. + if funding_transaction.is_coin_base() && + self.context.minimum_depth.unwrap_or(0) > 0 && + self.context.minimum_depth.unwrap_or(0) < COINBASE_MATURITY { + self.context.minimum_depth = Some(COINBASE_MATURITY); + } + self.context.funding_transaction = Some(funding_transaction); let channel = Channel {