use ln::features::{ChannelFeatures, InitFeatures};
use ln::msgs;
use ln::msgs::{DecodeError, OptionalField, DataLossProtect};
-use ln::channelmanager::{PendingHTLCStatus, HTLCSource, HTLCFailReason, HTLCFailureMsg, PendingHTLCInfo, RAACommitmentOrder, PaymentPreimage, PaymentHash, BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA, MAX_LOCAL_BREAKDOWN_TIMEOUT};
+use ln::channelmanager::{BestBlock, PendingHTLCStatus, HTLCSource, HTLCFailReason, HTLCFailureMsg, PendingHTLCInfo, RAACommitmentOrder, PaymentPreimage, PaymentHash, BREAKDOWN_TIMEOUT, MIN_CLTV_EXPIRY_DELTA, MAX_LOCAL_BREAKDOWN_TIMEOUT};
use ln::chan_utils::{CounterpartyCommitmentSecrets, TxCreationKeys, HTLCOutputInCommitment, HTLC_SUCCESS_TX_WEIGHT, HTLC_TIMEOUT_TX_WEIGHT, make_funding_redeemscript, ChannelPublicKeys, CommitmentTransaction, HolderCommitmentTransaction, ChannelTransactionParameters, CounterpartyChannelTransactionParameters, MAX_HTLCS, get_commitment_transaction_number_obscure_factor};
use ln::chan_utils;
use chain::chaininterface::{FeeEstimator,ConfirmationTarget};
/// Liveness is called to fluctuate given peer disconnecton/monitor failures/closing.
/// If channel is public, network should have a liveness view announced by us on a
/// best-effort, which means we may filter out some status transitions to avoid spam.
-/// See further timer_chan_freshness_every_min.
+/// See further timer_tick_occurred.
#[derive(PartialEq)]
enum UpdateStatus {
/// Status has been gossiped.
/// The hash of the block in which the funding transaction was included.
funding_tx_confirmed_in: Option<BlockHash>,
- funding_tx_confirmation_height: u64,
+ funding_tx_confirmation_height: u32,
short_channel_id: Option<u64>,
counterparty_dust_limit_satoshis: u64,
counterparty_forwarding_info: Option<CounterpartyForwardingInfo>,
pub(crate) channel_transaction_parameters: ChannelTransactionParameters,
+ funding_transaction: Option<Transaction>,
counterparty_cur_commitment_point: Option<PublicKey>,
-
counterparty_prev_commitment_point: Option<PublicKey>,
counterparty_node_id: PublicKey,
counterparty_parameters: None,
funding_outpoint: None
},
- counterparty_cur_commitment_point: None,
+ funding_transaction: None,
+ counterparty_cur_commitment_point: None,
counterparty_prev_commitment_point: None,
counterparty_node_id,
}),
funding_outpoint: None
},
- counterparty_cur_commitment_point: Some(msg.first_per_commitment_point),
+ funding_transaction: None,
+ counterparty_cur_commitment_point: Some(msg.first_per_commitment_point),
counterparty_prev_commitment_point: None,
counterparty_node_id,
&self.get_counterparty_pubkeys().funding_pubkey
}
- pub fn funding_created<L: Deref>(&mut self, msg: &msgs::FundingCreated, last_block_hash: BlockHash, logger: &L) -> Result<(msgs::FundingSigned, ChannelMonitor<Signer>), ChannelError> where L::Target: Logger {
+ pub fn funding_created<L: Deref>(&mut self, msg: &msgs::FundingCreated, best_block: BestBlock, logger: &L) -> Result<(msgs::FundingSigned, ChannelMonitor<Signer>), ChannelError> where L::Target: Logger {
if self.is_outbound() {
return Err(ChannelError::Close("Received funding_created for an outbound channel?".to_owned()));
}
&self.channel_transaction_parameters,
funding_redeemscript.clone(), self.channel_value_satoshis,
obscure_factor,
- holder_commitment_tx, last_block_hash);
+ holder_commitment_tx, best_block);
channel_monitor.provide_latest_counterparty_commitment_tx(counterparty_initial_commitment_txid, Vec::new(), self.cur_counterparty_commitment_transaction_number, self.counterparty_cur_commitment_point.unwrap(), logger);
/// Handles a funding_signed message from the remote end.
/// If this call is successful, broadcast the funding transaction (and not before!)
- pub fn funding_signed<L: Deref>(&mut self, msg: &msgs::FundingSigned, last_block_hash: BlockHash, logger: &L) -> Result<ChannelMonitor<Signer>, ChannelError> where L::Target: Logger {
+ pub fn funding_signed<L: Deref>(&mut self, msg: &msgs::FundingSigned, best_block: BestBlock, logger: &L) -> Result<(ChannelMonitor<Signer>, Transaction), ChannelError> where L::Target: Logger {
if !self.is_outbound() {
return Err(ChannelError::Close("Received funding_signed for an inbound channel?".to_owned()));
}
&self.channel_transaction_parameters,
funding_redeemscript.clone(), self.channel_value_satoshis,
obscure_factor,
- holder_commitment_tx, last_block_hash);
+ holder_commitment_tx, best_block);
channel_monitor.provide_latest_counterparty_commitment_tx(counterparty_initial_bitcoin_tx.txid, Vec::new(), self.cur_counterparty_commitment_transaction_number, self.counterparty_cur_commitment_point.unwrap(), logger);
self.cur_holder_commitment_transaction_number -= 1;
self.cur_counterparty_commitment_transaction_number -= 1;
- Ok(channel_monitor)
+ Ok((channel_monitor, self.funding_transaction.as_ref().cloned().unwrap()))
}
pub fn funding_locked(&mut self, msg: &msgs::FundingLocked) -> Result<(), ChannelError> {
/// Indicates that the latest ChannelMonitor update has been committed by the client
/// successfully and we should restore normal operation. Returns messages which should be sent
/// to the remote side.
- pub fn monitor_updating_restored<L: Deref>(&mut self, logger: &L) -> (Option<msgs::RevokeAndACK>, Option<msgs::CommitmentUpdate>, RAACommitmentOrder, Vec<(PendingHTLCInfo, u64)>, Vec<(HTLCSource, PaymentHash, HTLCFailReason)>, bool, Option<msgs::FundingLocked>) where L::Target: Logger {
+ pub fn monitor_updating_restored<L: Deref>(&mut self, logger: &L) -> (Option<msgs::RevokeAndACK>, Option<msgs::CommitmentUpdate>, RAACommitmentOrder, Vec<(PendingHTLCInfo, u64)>, Vec<(HTLCSource, PaymentHash, HTLCFailReason)>, Option<Transaction>, Option<msgs::FundingLocked>) where L::Target: Logger {
assert_eq!(self.channel_state & ChannelState::MonitorUpdateFailed as u32, ChannelState::MonitorUpdateFailed as u32);
self.channel_state &= !(ChannelState::MonitorUpdateFailed as u32);
- let needs_broadcast_safe = self.channel_state & (ChannelState::FundingSent as u32) != 0 && self.is_outbound();
+ let funding_broadcastable = if self.channel_state & (ChannelState::FundingSent as u32) != 0 && self.is_outbound() {
+ self.funding_transaction.take()
+ } else { None };
- // Because we will never generate a FundingBroadcastSafe event when we're in
- // MonitorUpdateFailed, if we assume the user only broadcast the funding transaction when
- // they received the FundingBroadcastSafe event, we can only ever hit
- // monitor_pending_funding_locked when we're an inbound channel which failed to persist the
- // monitor on funding_created, and we even got the funding transaction confirmed before the
- // monitor was persisted.
+ // We will never broadcast the funding transaction when we're in MonitorUpdateFailed (and
+ // we assume the user never directly broadcasts the funding transaction and waits for us to
+ // do it). Thus, we can only ever hit monitor_pending_funding_locked when we're an inbound
+ // channel which failed to persist the monitor on funding_created, and we got the funding
+ // transaction confirmed before the monitor was persisted.
let funding_locked = if self.monitor_pending_funding_locked {
- assert!(!self.is_outbound(), "Funding transaction broadcast without FundingBroadcastSafe!");
+ assert!(!self.is_outbound(), "Funding transaction broadcast by the local client before it should have - LDK didn't do it!");
self.monitor_pending_funding_locked = false;
let next_per_commitment_point = self.holder_signer.get_per_commitment_point(self.cur_holder_commitment_transaction_number, &self.secp_ctx);
Some(msgs::FundingLocked {
if self.channel_state & (ChannelState::PeerDisconnected as u32) != 0 {
self.monitor_pending_revoke_and_ack = false;
self.monitor_pending_commitment_signed = false;
- return (None, None, RAACommitmentOrder::RevokeAndACKFirst, forwards, failures, needs_broadcast_safe, funding_locked);
+ return (None, None, RAACommitmentOrder::RevokeAndACKFirst, forwards, failures, funding_broadcastable, funding_locked);
}
let raa = if self.monitor_pending_revoke_and_ack {
self.monitor_pending_commitment_signed = false;
let order = self.resend_order.clone();
log_trace!(logger, "Restored monitor updating resulting in {}{} commitment update and {} RAA, with {} first",
- if needs_broadcast_safe { "a funding broadcast safe, " } else { "" },
+ if funding_broadcastable.is_some() { "a funding broadcastable, " } else { "" },
if commitment_update.is_some() { "a" } else { "no" },
if raa.is_some() { "an" } else { "no" },
match order { RAACommitmentOrder::CommitmentFirst => "commitment", RAACommitmentOrder::RevokeAndACKFirst => "RAA"});
- (raa, commitment_update, order, forwards, failures, needs_broadcast_safe, funding_locked)
+ (raa, commitment_update, order, forwards, failures, funding_broadcastable, funding_locked)
}
pub fn update_fee<F: Deref>(&mut self, fee_estimator: &F, msg: &msgs::UpdateFee) -> Result<(), ChannelError>
#[cfg(not(feature = "fuzztarget"))]
panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction!");
}
- self.channel_state = ChannelState::ShutdownComplete as u32;
self.update_time_counter += 1;
return Err(msgs::ErrorMessage {
channel_id: self.channel_id(),
}
}
}
- self.funding_tx_confirmation_height = height as u64;
+ self.funding_tx_confirmation_height = height;
self.funding_tx_confirmed_in = Some(*block_hash);
self.short_channel_id = match scid_from_parts(height as u64, index_in_block as u64, txo_idx as u64) {
Ok(scid) => Some(scid),
}
}
// If we allow 1-conf funding, we may need to check for funding_locked here and
- // send it immediately instead of waiting for an update_best_block call (which
+ // send it immediately instead of waiting for a best_block_updated call (which
// may have already happened for this block).
if let Some(funding_locked) = self.check_get_funding_locked(height) {
return Ok(Some(funding_locked));
///
/// May return some HTLCs (and their payment_hash) which have timed out and should be failed
/// back.
- pub fn update_best_block(&mut self, height: u32, highest_header_time: u32) -> Result<(Option<msgs::FundingLocked>, Vec<(HTLCSource, PaymentHash)>), msgs::ErrorMessage> {
+ pub fn best_block_updated(&mut self, height: u32, highest_header_time: u32) -> Result<(Option<msgs::FundingLocked>, Vec<(HTLCSource, PaymentHash)>), msgs::ErrorMessage> {
let mut timed_out_htlcs = Vec::new();
let unforwarded_htlc_cltv_limit = height + HTLC_FAIL_BACK_BUFFER;
self.holding_cell_htlc_updates.retain(|htlc_update| {
Ok((None, timed_out_htlcs))
}
+ /// Indicates the funding transaction is no longer confirmed in the main chain. This may
+ /// force-close the channel, but may also indicate a harmless reorganization of a block or two
+ /// before the channel has reached funding_locked and we can just wait for more blocks.
+ pub fn funding_transaction_unconfirmed(&mut self) -> Result<(), msgs::ErrorMessage> {
+ if self.funding_tx_confirmation_height != 0 {
+ // We handle the funding disconnection by calling best_block_updated with a height one
+ // below where our funding was connected, implying a reorg back to conf_height - 1.
+ let reorg_height = self.funding_tx_confirmation_height - 1;
+ // We use the time field to bump the current time we set on channel updates if its
+ // larger. If we don't know that time has moved forward, we can just set it to the last
+ // time we saw and it will be ignored.
+ let best_time = self.update_time_counter;
+ match self.best_block_updated(reorg_height, best_time) {
+ Ok((funding_locked, timed_out_htlcs)) => {
+ assert!(funding_locked.is_none(), "We can't generate a funding with 0 confirmations?");
+ assert!(timed_out_htlcs.is_empty(), "We can't have accepted HTLCs with a timeout before our funding confirmation?");
+ Ok(())
+ },
+ Err(e) => Err(e)
+ }
+ } else {
+ // We never learned about the funding confirmation anyway, just ignore
+ Ok(())
+ }
+ }
+
// Methods to get unprompted messages to send to the remote end (or where we already returned
// something in the handler for the message that prompted this message):
/// Note that channel_id changes during this call!
/// Do NOT broadcast the funding transaction until after a successful funding_signed call!
/// If an Err is returned, it is a ChannelError::Close.
- pub fn get_outbound_funding_created<L: Deref>(&mut self, funding_txo: OutPoint, logger: &L) -> Result<msgs::FundingCreated, ChannelError> where L::Target: Logger {
+ pub fn get_outbound_funding_created<L: Deref>(&mut self, funding_transaction: Transaction, funding_txo: OutPoint, logger: &L) -> Result<msgs::FundingCreated, ChannelError> where L::Target: Logger {
if !self.is_outbound() {
panic!("Tried to create outbound funding_created message on an inbound channel!");
}
self.channel_state = ChannelState::FundingCreated as u32;
self.channel_id = funding_txo.to_channel_id();
+ self.funding_transaction = Some(funding_transaction);
Ok(msgs::FundingCreated {
temporary_channel_id,
}
self.channel_transaction_parameters.write(writer)?;
- self.counterparty_cur_commitment_point.write(writer)?;
+ self.funding_transaction.write(writer)?;
+ self.counterparty_cur_commitment_point.write(writer)?;
self.counterparty_prev_commitment_point.write(writer)?;
self.counterparty_node_id.write(writer)?;
};
let channel_parameters = Readable::read(reader)?;
+ let funding_transaction = Readable::read(reader)?;
+
let counterparty_cur_commitment_point = Readable::read(reader)?;
let counterparty_prev_commitment_point = Readable::read(reader)?;
counterparty_forwarding_info,
channel_transaction_parameters: channel_parameters,
- counterparty_cur_commitment_point,
+ funding_transaction,
+ counterparty_cur_commitment_point,
counterparty_prev_commitment_point,
counterparty_node_id,
use bitcoin::network::constants::Network;
use bitcoin::hashes::hex::FromHex;
use hex;
- use ln::channelmanager::{HTLCSource, PaymentPreimage, PaymentHash};
- use ln::channel::{Channel,Sign,InboundHTLCOutput,OutboundHTLCOutput,InboundHTLCState,OutboundHTLCState,HTLCOutputInCommitment,HTLCCandidate,HTLCInitiator,TxCreationKeys};
+ use ln::channelmanager::{BestBlock, HTLCSource, PaymentPreimage, PaymentHash};
+ use ln::channel::{Channel,InboundHTLCOutput,OutboundHTLCOutput,InboundHTLCState,OutboundHTLCState,HTLCOutputInCommitment,HTLCCandidate,HTLCInitiator,TxCreationKeys};
use ln::channel::MAX_FUNDING_SATOSHIS;
use ln::features::InitFeatures;
use ln::msgs::{ChannelUpdate, DataLossProtect, DecodeError, OptionalField, UnsignedChannelUpdate};
use ln::chan_utils;
use ln::chan_utils::{ChannelPublicKeys, HolderCommitmentTransaction, CounterpartyChannelTransactionParameters, HTLC_SUCCESS_TX_WEIGHT, HTLC_TIMEOUT_TX_WEIGHT};
use chain::chaininterface::{FeeEstimator,ConfirmationTarget};
- use chain::keysinterface::{InMemorySigner, KeysInterface};
+ use chain::keysinterface::{InMemorySigner, KeysInterface, BaseSign};
use chain::transaction::OutPoint;
use util::config::UserConfig;
use util::enforcing_trait_impls::EnforcingSigner;
let secp_ctx = Secp256k1::new();
let seed = [42; 32];
let network = Network::Testnet;
- let chain_hash = genesis_block(network).header.block_hash();
- let last_block_hash = chain_hash;
+ let best_block = BestBlock::from_genesis(network);
+ let chain_hash = best_block.block_hash();
let keys_provider = test_utils::TestKeysInterface::new(&seed, network);
// Go through the flow of opening a channel between two nodes.
value: 10000000, script_pubkey: output_script.clone(),
}]};
let funding_outpoint = OutPoint{ txid: tx.txid(), index: 0 };
- let funding_created_msg = node_a_chan.get_outbound_funding_created(funding_outpoint, &&logger).unwrap();
- let (funding_signed_msg, _) = node_b_chan.funding_created(&funding_created_msg, last_block_hash, &&logger).unwrap();
+ let funding_created_msg = node_a_chan.get_outbound_funding_created(tx.clone(), funding_outpoint, &&logger).unwrap();
+ let (funding_signed_msg, _) = node_b_chan.funding_created(&funding_created_msg, best_block, &&logger).unwrap();
// Node B --> Node A: funding signed
- let _ = node_a_chan.funding_signed(&funding_signed_msg, last_block_hash, &&logger);
+ let _ = node_a_chan.funding_signed(&funding_signed_msg, best_block, &&logger);
// Now disconnect the two nodes and check that the commitment point in
// Node B's channel_reestablish message is sane.