X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Fchannel.rs;h=8a1489d4ef775c7be7d515bcc3153895437eb726;hb=36570f4593bbbb8d5f894e5ed9d5a7f9a4720004;hp=e700b1f3bd52e728768946d21acfaeae116cbb44;hpb=e885d0a7747cfc3b89a3c2765a8c0dd174e3889a;p=rust-lightning diff --git a/lightning/src/ln/channel.rs b/lightning/src/ln/channel.rs index e700b1f3..8a1489d4 100644 --- a/lightning/src/ln/channel.rs +++ b/lightning/src/ln/channel.rs @@ -7,7 +7,6 @@ // You may not use this file except in accordance with one or both of these // licenses. -use bitcoin::blockdata::block::BlockHeader; use bitcoin::blockdata::script::{Script,Builder}; use bitcoin::blockdata::transaction::{TxIn, TxOut, Transaction, SigHashType}; use bitcoin::blockdata::opcodes; @@ -25,24 +24,27 @@ use bitcoin::secp256k1; 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, 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}; use chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateStep, HTLC_FAIL_BACK_BUFFER}; use chain::transaction::{OutPoint, TransactionData}; -use chain::keysinterface::{ChannelKeys, KeysInterface}; +use chain::keysinterface::{Sign, KeysInterface}; use util::transaction_utils; use util::ser::{Readable, ReadableArgs, Writeable, Writer, VecWriter}; use util::logger::Logger; use util::errors::APIError; use util::config::{UserConfig,ChannelConfig}; +use util::scid_utils::scid_from_parts; use std; -use std::default::Default; use std::{cmp,mem,fmt}; use std::ops::Deref; +#[cfg(any(test, feature = "fuzztarget"))] +use std::sync::Mutex; use bitcoin::hashes::hex::ToHex; +use bitcoin::blockdata::opcodes::all::OP_PUSHBYTES_0; #[cfg(test)] pub struct ChannelValueStat { @@ -93,6 +95,7 @@ enum InboundHTLCState { /// is used to derive commitment keys, which are used to construct the /// signatures in a commitment_signed message. /// Implies AwaitingRemoteRevoke. + /// /// [BOLT #2]: https://github.com/lightningnetwork/lightning-rfc/blob/master/02-peer-protocol.md AwaitingRemoteRevokeToAnnounce(PendingHTLCStatus), /// Included in a received commitment_signed message (implying we've revoke_and_ack'd it). @@ -247,7 +250,7 @@ pub const INITIAL_COMMITMENT_NUMBER: u64 = (1 << 48) - 1; /// 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. @@ -258,6 +261,40 @@ enum UpdateStatus { DisabledStaged, } +/// An enum indicating whether the local or remote side offered a given HTLC. +enum HTLCInitiator { + LocalOffered, + RemoteOffered, +} + +/// Used when calculating whether we or the remote can afford an additional HTLC. +struct HTLCCandidate { + amount_msat: u64, + origin: HTLCInitiator, +} + +impl HTLCCandidate { + fn new(amount_msat: u64, origin: HTLCInitiator) -> Self { + Self { + amount_msat, + origin, + } + } +} + +/// Information needed for constructing an invoice route hint for this channel. +#[derive(Clone)] +pub struct CounterpartyForwardingInfo { + /// Base routing fee in millisatoshis. + pub fee_base_msat: u32, + /// Amount in millionths of a satoshi the channel will charge per transferred satoshi. + pub fee_proportional_millionths: u32, + /// The minimum difference in cltv_expiry between an ingoing HTLC and its outgoing counterpart, + /// such that the outgoing HTLC is forwardable to this counterparty. See `msgs::ChannelUpdate`'s + /// `cltv_expiry_delta` for more details. + pub cltv_expiry_delta: u16, +} + // 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_outbound_funding_signed on an @@ -265,7 +302,7 @@ enum UpdateStatus { // // Holder designates channel data owned for the benefice of the user client. // Counterparty designates channel data owned by the another channel participant entity. -pub(super) struct Channel { +pub(super) struct Channel { config: ChannelConfig, user_id: u64, @@ -277,10 +314,7 @@ pub(super) struct Channel { latest_monitor_update_id: u64, - #[cfg(not(test))] - holder_keys: ChanSigner, - #[cfg(test)] - pub(super) holder_keys: ChanSigner, + holder_signer: Signer, shutdown_pubkey: PublicKey, destination_script: Script, @@ -341,16 +375,10 @@ pub(super) struct Channel { last_sent_closing_fee: Option<(u32, u64, Signature)>, // (feerate, fee, holder_sig) - /// The hash of the block in which the funding transaction reached our CONF_TARGET. We use this - /// to detect unconfirmation after a serialize-unserialize roundtrip where we may not see a full - /// series of block_connected/block_disconnected calls. Obviously this is not a guarantee as we - /// could miss the funding_tx_confirmed_in block as well, but it serves as a useful fallback. + /// The hash of the block in which the funding transaction was included. funding_tx_confirmed_in: Option, + funding_tx_confirmation_height: u32, short_channel_id: Option, - /// Used to deduplicate block_connected callbacks, also used to verify consistency during - /// ChannelManager deserialization (hence pub(super)) - pub(super) last_block_connected: BlockHash, - funding_tx_confirmations: u64, counterparty_dust_limit_satoshis: u64, #[cfg(test)] @@ -374,10 +402,12 @@ pub(super) struct Channel { //implied by OUR_MAX_HTLCS: max_accepted_htlcs: u16, minimum_depth: u32, + counterparty_forwarding_info: Option, + pub(crate) channel_transaction_parameters: ChannelTransactionParameters, + funding_transaction: Option, counterparty_cur_commitment_point: Option, - counterparty_prev_commitment_point: Option, counterparty_node_id: PublicKey, @@ -386,13 +416,27 @@ pub(super) struct Channel { commitment_secrets: CounterpartyCommitmentSecrets, network_sync: UpdateStatus, + + // We save these values so we can make sure `next_local_commit_tx_fee_msat` and + // `next_remote_commit_tx_fee_msat` properly predict what the next commitment transaction fee will + // be, by comparing the cached values to the fee of the tranaction generated by + // `build_commitment_transaction`. + #[cfg(any(test, feature = "fuzztarget"))] + next_local_commitment_tx_fee_info_cached: Mutex>, + #[cfg(any(test, feature = "fuzztarget"))] + next_remote_commitment_tx_fee_info_cached: Mutex>, +} + +#[cfg(any(test, feature = "fuzztarget"))] +struct CommitmentTxInfoCached { + fee: u64, + total_pending_htlcs: usize, + next_holder_htlc_id: u64, + next_counterparty_htlc_id: u64, + feerate: u32, } pub const OUR_MAX_HTLCS: u16 = 50; //TODO -/// Confirmation count threshold at which we close a channel. Ideally we'd keep the channel around -/// on ice until the funding transaction gets more confirmations, but the LN protocol doesn't -/// really allow for this, so instead we're stuck closing it out at that point. -const UNCONF_THRESHOLD: u32 = 6; const SPENDING_INPUT_FOR_A_OUTPUT_WEIGHT: u64 = 79; // prevout: 36, nSequence: 4, script len: 1, witness lengths: (3+1)/4, sig: 73/4, if-selector: 1, redeemScript: (6 ops + 2*33 pubkeys + 1*2 delay)/4 const B_OUTPUT_PLUS_SPENDING_INPUT_WEIGHT: u64 = 104; // prevout: 40, nSequence: 4, script len: 1, witness lengths: 3/4, sig: 73/4, pubkey: 33/4, output: 31 (TODO: Wrong? Useless?) @@ -437,7 +481,7 @@ macro_rules! secp_check { }; } -impl Channel { +impl Channel { // Convert constants + channel value to limits: fn get_holder_max_htlc_value_in_flight_msat(channel_value_satoshis: u64) -> u64 { channel_value_satoshis * 1000 / 10 //TODO @@ -457,13 +501,13 @@ impl Channel { } // Constructors: - pub fn new_outbound(fee_estimator: &F, keys_provider: &K, counterparty_node_id: PublicKey, channel_value_satoshis: u64, push_msat: u64, user_id: u64, config: &UserConfig) -> Result, APIError> - where K::Target: KeysInterface, + pub fn new_outbound(fee_estimator: &F, keys_provider: &K, counterparty_node_id: PublicKey, channel_value_satoshis: u64, push_msat: u64, user_id: u64, config: &UserConfig) -> Result, APIError> + where K::Target: KeysInterface, F::Target: FeeEstimator, { let holder_selected_contest_delay = config.own_channel_config.our_to_self_delay; - let chan_keys = keys_provider.get_channel_keys(false, channel_value_satoshis); - let pubkeys = chan_keys.pubkeys().clone(); + let holder_signer = keys_provider.get_channel_signer(false, channel_value_satoshis); + let pubkeys = holder_signer.pubkeys().clone(); if channel_value_satoshis >= MAX_FUNDING_SATOSHIS { return Err(APIError::APIMisuseError{err: format!("funding_value must be smaller than {}, it was {}", MAX_FUNDING_SATOSHIS, channel_value_satoshis)}); @@ -476,24 +520,27 @@ impl Channel { return Err(APIError::APIMisuseError {err: format!("Configured with an unreasonable our_to_self_delay ({}) putting user funds at risks", holder_selected_contest_delay)}); } let background_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background); - if Channel::::get_holder_selected_channel_reserve_satoshis(channel_value_satoshis) < Channel::::derive_holder_dust_limit_satoshis(background_feerate) { + if Channel::::get_holder_selected_channel_reserve_satoshis(channel_value_satoshis) < Channel::::derive_holder_dust_limit_satoshis(background_feerate) { return Err(APIError::FeeRateTooHigh{err: format!("Not enough reserve above dust limit can be found at current fee rate({})", background_feerate), feerate: background_feerate}); } let feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Normal); + let mut secp_ctx = Secp256k1::new(); + secp_ctx.seeded_randomize(&keys_provider.get_secure_random_bytes()); + Ok(Channel { user_id, config: config.channel_options.clone(), channel_id: keys_provider.get_secure_random_bytes(), channel_state: ChannelState::OurInitSent as u32, - secp_ctx: Secp256k1::new(), + secp_ctx, channel_value_satoshis, latest_monitor_update_id: 0, - holder_keys: chan_keys, + holder_signer, shutdown_pubkey: keys_provider.get_shutdown_pubkey(), destination_script: keys_provider.get_destination_script(), @@ -526,13 +573,12 @@ impl Channel { last_sent_closing_fee: None, funding_tx_confirmed_in: None, + funding_tx_confirmation_height: 0, short_channel_id: None, - last_block_connected: Default::default(), - funding_tx_confirmations: 0, feerate_per_kw: feerate, counterparty_dust_limit_satoshis: 0, - holder_dust_limit_satoshis: Channel::::derive_holder_dust_limit_satoshis(background_feerate), + holder_dust_limit_satoshis: Channel::::derive_holder_dust_limit_satoshis(background_feerate), counterparty_max_htlc_value_in_flight_msat: 0, counterparty_selected_channel_reserve_satoshis: 0, counterparty_htlc_minimum_msat: 0, @@ -540,6 +586,8 @@ impl Channel { counterparty_max_accepted_htlcs: 0, minimum_depth: 0, // Filled in in accept_channel + counterparty_forwarding_info: None, + channel_transaction_parameters: ChannelTransactionParameters { holder_pubkeys: pubkeys, holder_selected_contest_delay: config.own_channel_config.our_to_self_delay, @@ -547,8 +595,9 @@ impl Channel { 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, @@ -557,6 +606,11 @@ impl Channel { commitment_secrets: CounterpartyCommitmentSecrets::new(), network_sync: UpdateStatus::Fresh, + + #[cfg(any(test, feature = "fuzztarget"))] + next_local_commitment_tx_fee_info_cached: Mutex::new(None), + #[cfg(any(test, feature = "fuzztarget"))] + next_remote_commitment_tx_fee_info_cached: Mutex::new(None), }) } @@ -576,12 +630,12 @@ impl Channel { /// Creates a new channel from a remote sides' request for one. /// Assumes chain_hash has already been checked and corresponds with what we expect! - pub fn new_from_req(fee_estimator: &F, keys_provider: &K, counterparty_node_id: PublicKey, their_features: InitFeatures, msg: &msgs::OpenChannel, user_id: u64, config: &UserConfig) -> Result, ChannelError> - where K::Target: KeysInterface, + pub fn new_from_req(fee_estimator: &F, keys_provider: &K, counterparty_node_id: PublicKey, their_features: InitFeatures, msg: &msgs::OpenChannel, user_id: u64, config: &UserConfig) -> Result, ChannelError> + where K::Target: KeysInterface, F::Target: FeeEstimator { - let chan_keys = keys_provider.get_channel_keys(true, msg.funding_satoshis); - let pubkeys = chan_keys.pubkeys().clone(); + let holder_signer = keys_provider.get_channel_signer(true, msg.funding_satoshis); + let pubkeys = holder_signer.pubkeys().clone(); let counterparty_pubkeys = ChannelPublicKeys { funding_pubkey: msg.funding_pubkey, revocation_basepoint: msg.revocation_basepoint, @@ -616,7 +670,7 @@ impl Channel { if msg.htlc_minimum_msat >= full_channel_value_msat { return Err(ChannelError::Close(format!("Minimum htlc value ({}) was larger than full channel value ({})", msg.htlc_minimum_msat, full_channel_value_msat))); } - Channel::::check_remote_fee(fee_estimator, msg.feerate_per_kw)?; + Channel::::check_remote_fee(fee_estimator, msg.feerate_per_kw)?; let max_counterparty_selected_contest_delay = u16::min(config.peer_channel_config_limits.their_to_self_delay, MAX_LOCAL_BREAKDOWN_TIMEOUT); if msg.to_self_delay > max_counterparty_selected_contest_delay { @@ -665,8 +719,8 @@ impl Channel { let background_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background); - let holder_dust_limit_satoshis = Channel::::derive_holder_dust_limit_satoshis(background_feerate); - let holder_selected_channel_reserve_satoshis = Channel::::get_holder_selected_channel_reserve_satoshis(msg.funding_satoshis); + let holder_dust_limit_satoshis = Channel::::derive_holder_dust_limit_satoshis(background_feerate); + let holder_selected_channel_reserve_satoshis = Channel::::get_holder_selected_channel_reserve_satoshis(msg.funding_satoshis); if holder_selected_channel_reserve_satoshis < holder_dust_limit_satoshis { return Err(ChannelError::Close(format!("Suitable channel reserve not found. remote_channel_reserve was ({}). dust_limit_satoshis is ({}).", holder_selected_channel_reserve_satoshis, holder_dust_limit_satoshis))); } @@ -694,15 +748,14 @@ impl Channel { let counterparty_shutdown_scriptpubkey = if their_features.supports_upfront_shutdown_script() { match &msg.shutdown_scriptpubkey { &OptionalField::Present(ref script) => { - // Peer is signaling upfront_shutdown and has provided a non-accepted scriptpubkey format. We enforce it while receiving shutdown msg - if script.is_p2pkh() || script.is_p2sh() || script.is_v0_p2wsh() || script.is_v0_p2wpkh() { - Some(script.clone()) // Peer is signaling upfront_shutdown and has opt-out with a 0-length script. We don't enforce anything - } else if script.len() == 0 { + if script.len() == 0 { None // Peer is signaling upfront_shutdown and has provided a non-accepted scriptpubkey format. Fail the channel - } else { + } else if is_unsupported_shutdown_script(&their_features, script) { return Err(ChannelError::Close(format!("Peer is signaling upfront_shutdown but has provided a non-accepted scriptpubkey format. script: ({})", script.to_bytes().to_hex()))); + } else { + Some(script.clone()) } }, // Peer is signaling upfront shutdown but don't opt-out with correct mechanism (a.k.a 0-length script). Peer looks buggy, we fail the channel @@ -712,17 +765,20 @@ impl Channel { } } else { None }; + let mut secp_ctx = Secp256k1::new(); + secp_ctx.seeded_randomize(&keys_provider.get_secure_random_bytes()); + let chan = Channel { user_id, config: local_config, channel_id: msg.temporary_channel_id, channel_state: (ChannelState::OurInitSent as u32) | (ChannelState::TheirInitSent as u32), - secp_ctx: Secp256k1::new(), + secp_ctx, latest_monitor_update_id: 0, - holder_keys: chan_keys, + holder_signer, shutdown_pubkey: keys_provider.get_shutdown_pubkey(), destination_script: keys_provider.get_destination_script(), @@ -755,9 +811,8 @@ impl Channel { last_sent_closing_fee: None, funding_tx_confirmed_in: None, + funding_tx_confirmation_height: 0, short_channel_id: None, - last_block_connected: Default::default(), - funding_tx_confirmations: 0, feerate_per_kw: msg.feerate_per_kw, channel_value_satoshis: msg.funding_satoshis, @@ -770,6 +825,8 @@ impl Channel { counterparty_max_accepted_htlcs: msg.max_accepted_htlcs, minimum_depth: config.own_channel_config.minimum_depth, + counterparty_forwarding_info: None, + channel_transaction_parameters: ChannelTransactionParameters { holder_pubkeys: pubkeys, holder_selected_contest_delay: config.own_channel_config.our_to_self_delay, @@ -780,8 +837,9 @@ impl Channel { }), 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, @@ -790,6 +848,11 @@ impl Channel { commitment_secrets: CounterpartyCommitmentSecrets::new(), network_sync: UpdateStatus::Fresh, + + #[cfg(any(test, feature = "fuzztarget"))] + next_local_commitment_tx_fee_info_cached: Mutex::new(None), + #[cfg(any(test, feature = "fuzztarget"))] + next_remote_commitment_tx_fee_info_cached: Mutex::new(None), }; Ok(chan) @@ -936,7 +999,7 @@ impl Channel { }; debug_assert!(broadcaster_max_commitment_tx_output.0 <= value_to_self_msat as u64 || value_to_self_msat / 1000 >= self.counterparty_selected_channel_reserve_satoshis as i64); broadcaster_max_commitment_tx_output.0 = cmp::max(broadcaster_max_commitment_tx_output.0, value_to_self_msat as u64); - debug_assert!(broadcaster_max_commitment_tx_output.1 <= value_to_remote_msat as u64 || value_to_remote_msat / 1000 >= Channel::::get_holder_selected_channel_reserve_satoshis(self.channel_value_satoshis) as i64); + debug_assert!(broadcaster_max_commitment_tx_output.1 <= value_to_remote_msat as u64 || value_to_remote_msat / 1000 >= Channel::::get_holder_selected_channel_reserve_satoshis(self.channel_value_satoshis) as i64); broadcaster_max_commitment_tx_output.1 = cmp::max(broadcaster_max_commitment_tx_output.1, value_to_remote_msat as u64); } @@ -1085,7 +1148,7 @@ impl Channel { /// The result is a transaction which we can revoke broadcastership of (ie a "local" transaction) /// TODO Some magic rust shit to compile-time check this? fn build_holder_transaction_keys(&self, commitment_number: u64) -> Result { - let per_commitment_point = self.holder_keys.get_per_commitment_point(commitment_number, &self.secp_ctx); + let per_commitment_point = self.holder_signer.get_per_commitment_point(commitment_number, &self.secp_ctx); let delayed_payment_base = &self.get_holder_pubkeys().delayed_payment_basepoint; let htlc_basepoint = &self.get_holder_pubkeys().htlc_basepoint; let counterparty_pubkeys = self.get_counterparty_pubkeys(); @@ -1346,7 +1409,7 @@ impl Channel { if msg.channel_reserve_satoshis < self.holder_dust_limit_satoshis { return Err(ChannelError::Close(format!("Peer never wants payout outputs? channel_reserve_satoshis was ({}). dust_limit is ({})", msg.channel_reserve_satoshis, self.holder_dust_limit_satoshis))); } - let remote_reserve = Channel::::get_holder_selected_channel_reserve_satoshis(self.channel_value_satoshis); + let remote_reserve = Channel::::get_holder_selected_channel_reserve_satoshis(self.channel_value_satoshis); if msg.dust_limit_satoshis > remote_reserve { return Err(ChannelError::Close(format!("Dust limit ({}) is bigger than our channel reserve ({})", msg.dust_limit_satoshis, remote_reserve))); } @@ -1391,15 +1454,14 @@ impl Channel { let counterparty_shutdown_scriptpubkey = if their_features.supports_upfront_shutdown_script() { match &msg.shutdown_scriptpubkey { &OptionalField::Present(ref script) => { - // Peer is signaling upfront_shutdown and has provided a non-accepted scriptpubkey format. We enforce it while receiving shutdown msg - if script.is_p2pkh() || script.is_p2sh() || script.is_v0_p2wsh() || script.is_v0_p2wpkh() { - Some(script.clone()) // Peer is signaling upfront_shutdown and has opt-out with a 0-length script. We don't enforce anything - } else if script.len() == 0 { + if script.len() == 0 { None // Peer is signaling upfront_shutdown and has provided a non-accepted scriptpubkey format. Fail the channel + } else if is_unsupported_shutdown_script(&their_features, script) { + return Err(ChannelError::Close(format!("Peer is signaling upfront_shutdown but has provided a non-accepted scriptpubkey format. script: ({})", script.to_bytes().to_hex()))); } else { - return Err(ChannelError::Close(format!("Peer is signaling upfront_shutdown but has provided a non-accepted scriptpubkey format. scriptpubkey: ({})", script.to_bytes().to_hex()))); + Some(script.clone()) } }, // Peer is signaling upfront shutdown but don't opt-out with correct mechanism (a.k.a 0-length script). Peer looks buggy, we fail the channel @@ -1458,7 +1520,7 @@ impl Channel { let counterparty_initial_bitcoin_tx = counterparty_trusted_tx.built_transaction(); log_trace!(logger, "Initial counterparty ID {} tx {}", counterparty_initial_bitcoin_tx.txid, encode::serialize_hex(&counterparty_initial_bitcoin_tx.transaction)); - let counterparty_signature = self.holder_keys.sign_counterparty_commitment(&counterparty_initial_commitment_tx, &self.secp_ctx) + let counterparty_signature = self.holder_signer.sign_counterparty_commitment(&counterparty_initial_commitment_tx, &self.secp_ctx) .map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed".to_owned()))?.0; // We sign "counterparty" commitment transaction, allowing them to broadcast the tx if they wish. @@ -1469,7 +1531,7 @@ impl Channel { &self.get_counterparty_pubkeys().funding_pubkey } - pub fn funding_created(&mut self, msg: &msgs::FundingCreated, logger: &L) -> Result<(msgs::FundingSigned, ChannelMonitor), ChannelError> where L::Target: Logger { + pub fn funding_created(&mut self, msg: &msgs::FundingCreated, best_block: BestBlock, logger: &L) -> Result<(msgs::FundingSigned, ChannelMonitor), ChannelError> where L::Target: Logger { if self.is_outbound() { return Err(ChannelError::Close("Received funding_created for an outbound channel?".to_owned())); } @@ -1489,7 +1551,7 @@ impl Channel { self.channel_transaction_parameters.funding_outpoint = Some(funding_txo); // This is an externally observable change before we finish all our checks. In particular // funding_created_signature may fail. - self.holder_keys.ready_channel(&self.channel_transaction_parameters); + self.holder_signer.ready_channel(&self.channel_transaction_parameters); let (counterparty_initial_commitment_txid, initial_commitment_tx, signature) = match self.funding_created_signature(&msg.signature, logger) { Ok(res) => res, @@ -1517,13 +1579,13 @@ impl Channel { let funding_redeemscript = self.get_funding_redeemscript(); let funding_txo_script = funding_redeemscript.to_v0_p2wsh(); let obscure_factor = get_commitment_transaction_number_obscure_factor(&self.get_holder_pubkeys().payment_point, &self.get_counterparty_pubkeys().payment_point, self.is_outbound()); - let mut channel_monitor = ChannelMonitor::new(self.holder_keys.clone(), - &self.shutdown_pubkey, self.get_holder_selected_contest_delay(), - &self.destination_script, (funding_txo, funding_txo_script.clone()), - &self.channel_transaction_parameters, - funding_redeemscript.clone(), self.channel_value_satoshis, - obscure_factor, - holder_commitment_tx); + let channel_monitor = ChannelMonitor::new(self.secp_ctx.clone(), self.holder_signer.clone(), + &self.shutdown_pubkey, self.get_holder_selected_contest_delay(), + &self.destination_script, (funding_txo, funding_txo_script.clone()), + &self.channel_transaction_parameters, + funding_redeemscript.clone(), self.channel_value_satoshis, + obscure_factor, + 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); @@ -1540,7 +1602,7 @@ impl Channel { /// 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(&mut self, msg: &msgs::FundingSigned, logger: &L) -> Result, ChannelError> where L::Target: Logger { + pub fn funding_signed(&mut self, msg: &msgs::FundingSigned, best_block: BestBlock, logger: &L) -> Result<(ChannelMonitor, Transaction), ChannelError> where L::Target: Logger { if !self.is_outbound() { return Err(ChannelError::Close("Received funding_signed for an inbound channel?".to_owned())); } @@ -1562,8 +1624,8 @@ impl Channel { log_trace!(logger, "Initial counterparty ID {} tx {}", counterparty_initial_bitcoin_tx.txid, encode::serialize_hex(&counterparty_initial_bitcoin_tx.transaction)); - let holder_keys = self.build_holder_transaction_keys(self.cur_holder_commitment_transaction_number)?; - let initial_commitment_tx = self.build_commitment_transaction(self.cur_holder_commitment_transaction_number, &holder_keys, true, false, self.feerate_per_kw, logger).0; + let holder_signer = self.build_holder_transaction_keys(self.cur_holder_commitment_transaction_number)?; + let initial_commitment_tx = self.build_commitment_transaction(self.cur_holder_commitment_transaction_number, &holder_signer, true, false, self.feerate_per_kw, logger).0; { let trusted_tx = initial_commitment_tx.trust(); let initial_commitment_bitcoin_tx = trusted_tx.built_transaction(); @@ -1587,13 +1649,13 @@ impl Channel { let funding_txo = self.get_funding_txo().unwrap(); let funding_txo_script = funding_redeemscript.to_v0_p2wsh(); let obscure_factor = get_commitment_transaction_number_obscure_factor(&self.get_holder_pubkeys().payment_point, &self.get_counterparty_pubkeys().payment_point, self.is_outbound()); - let mut channel_monitor = ChannelMonitor::new(self.holder_keys.clone(), - &self.shutdown_pubkey, self.get_holder_selected_contest_delay(), - &self.destination_script, (funding_txo, funding_txo_script), - &self.channel_transaction_parameters, - funding_redeemscript.clone(), self.channel_value_satoshis, - obscure_factor, - holder_commitment_tx); + let channel_monitor = ChannelMonitor::new(self.secp_ctx.clone(), self.holder_signer.clone(), + &self.shutdown_pubkey, self.get_holder_selected_contest_delay(), + &self.destination_script, (funding_txo, funding_txo_script), + &self.channel_transaction_parameters, + funding_redeemscript.clone(), self.channel_value_satoshis, + obscure_factor, + 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); @@ -1602,7 +1664,7 @@ impl Channel { 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> { @@ -1684,63 +1746,172 @@ impl Channel { (COMMITMENT_TX_BASE_WEIGHT + num_htlcs as u64 * COMMITMENT_TX_WEIGHT_PER_HTLC) * self.feerate_per_kw as u64 / 1000 * 1000 } - // Get the commitment tx fee for the local (i.e our) next commitment transaction - // based on the number of pending HTLCs that are on track to be in our next - // commitment tx. `addl_htcs` is an optional parameter allowing the caller - // to add a number of additional HTLCs to the calculation. Note that dust - // HTLCs are excluded. - fn next_local_commit_tx_fee_msat(&self, addl_htlcs: usize) -> u64 { + // Get the commitment tx fee for the local's (i.e. our) next commitment transaction based on the + // number of pending HTLCs that are on track to be in our next commitment tx, plus an additional + // HTLC if `fee_spike_buffer_htlc` is Some, plus a new HTLC given by `new_htlc_amount`. Dust HTLCs + // are excluded. + fn next_local_commit_tx_fee_msat(&self, htlc: HTLCCandidate, fee_spike_buffer_htlc: Option<()>) -> u64 { assert!(self.is_outbound()); - let mut their_acked_htlcs = self.pending_inbound_htlcs.len(); + let real_dust_limit_success_sat = (self.feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000) + self.holder_dust_limit_satoshis; + let real_dust_limit_timeout_sat = (self.feerate_per_kw as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000) + self.holder_dust_limit_satoshis; + + let mut addl_htlcs = 0; + if fee_spike_buffer_htlc.is_some() { addl_htlcs += 1; } + match htlc.origin { + HTLCInitiator::LocalOffered => { + if htlc.amount_msat / 1000 >= real_dust_limit_timeout_sat { + addl_htlcs += 1; + } + }, + HTLCInitiator::RemoteOffered => { + if htlc.amount_msat / 1000 >= real_dust_limit_success_sat { + addl_htlcs += 1; + } + } + } + + let mut included_htlcs = 0; + for ref htlc in self.pending_inbound_htlcs.iter() { + if htlc.amount_msat / 1000 < real_dust_limit_success_sat { + continue + } + // We include LocalRemoved HTLCs here because we may still need to broadcast a commitment + // transaction including this HTLC if it times out before they RAA. + included_htlcs += 1; + } + for ref htlc in self.pending_outbound_htlcs.iter() { - if htlc.amount_msat / 1000 <= self.holder_dust_limit_satoshis { + if htlc.amount_msat / 1000 < real_dust_limit_timeout_sat { continue } match htlc.state { - OutboundHTLCState::Committed => their_acked_htlcs += 1, - OutboundHTLCState::RemoteRemoved {..} => their_acked_htlcs += 1, - OutboundHTLCState::LocalAnnounced {..} => their_acked_htlcs += 1, + OutboundHTLCState::LocalAnnounced {..} => included_htlcs += 1, + OutboundHTLCState::Committed => included_htlcs += 1, + OutboundHTLCState::RemoteRemoved {..} => included_htlcs += 1, + // We don't include AwaitingRemoteRevokeToRemove HTLCs because our next commitment + // transaction won't be generated until they send us their next RAA, which will mean + // dropping any HTLCs in this state. _ => {}, } } for htlc in self.holding_cell_htlc_updates.iter() { match htlc { - &HTLCUpdateAwaitingACK::AddHTLC { .. } => their_acked_htlcs += 1, - _ => {}, + &HTLCUpdateAwaitingACK::AddHTLC { amount_msat, .. } => { + if amount_msat / 1000 < real_dust_limit_timeout_sat { + continue + } + included_htlcs += 1 + }, + _ => {}, // Don't include claims/fails that are awaiting ack, because once we get the + // ack we're guaranteed to never include them in commitment txs anymore. } } - self.commit_tx_fee_msat(their_acked_htlcs + addl_htlcs) + let num_htlcs = included_htlcs + addl_htlcs; + let res = self.commit_tx_fee_msat(num_htlcs); + #[cfg(any(test, feature = "fuzztarget"))] + { + let mut fee = res; + if fee_spike_buffer_htlc.is_some() { + fee = self.commit_tx_fee_msat(num_htlcs - 1); + } + let total_pending_htlcs = self.pending_inbound_htlcs.len() + self.pending_outbound_htlcs.len() + + self.holding_cell_htlc_updates.len(); + let commitment_tx_info = CommitmentTxInfoCached { + fee, + total_pending_htlcs, + next_holder_htlc_id: match htlc.origin { + HTLCInitiator::LocalOffered => self.next_holder_htlc_id + 1, + HTLCInitiator::RemoteOffered => self.next_holder_htlc_id, + }, + next_counterparty_htlc_id: match htlc.origin { + HTLCInitiator::LocalOffered => self.next_counterparty_htlc_id, + HTLCInitiator::RemoteOffered => self.next_counterparty_htlc_id + 1, + }, + feerate: self.feerate_per_kw, + }; + *self.next_local_commitment_tx_fee_info_cached.lock().unwrap() = Some(commitment_tx_info); + } + res } - // Get the commitment tx fee for the remote's next commitment transaction - // based on the number of pending HTLCs that are on track to be in their - // next commitment tx. `addl_htcs` is an optional parameter allowing the caller - // to add a number of additional HTLCs to the calculation. Note that dust HTLCs - // are excluded. - fn next_remote_commit_tx_fee_msat(&self, addl_htlcs: usize) -> u64 { + // Get the commitment tx fee for the remote's next commitment transaction based on the number of + // pending HTLCs that are on track to be in their next commitment tx, plus an additional HTLC if + // `fee_spike_buffer_htlc` is Some, plus a new HTLC given by `new_htlc_amount`. Dust HTLCs are + // excluded. + fn next_remote_commit_tx_fee_msat(&self, htlc: HTLCCandidate, fee_spike_buffer_htlc: Option<()>) -> u64 { assert!(!self.is_outbound()); - // When calculating the set of HTLCs which will be included in their next - // commitment_signed, all inbound HTLCs are included (as all states imply it will be - // included) and only committed outbound HTLCs, see below. - let mut their_acked_htlcs = self.pending_inbound_htlcs.len(); + let real_dust_limit_success_sat = (self.feerate_per_kw as u64 * HTLC_SUCCESS_TX_WEIGHT / 1000) + self.counterparty_dust_limit_satoshis; + let real_dust_limit_timeout_sat = (self.feerate_per_kw as u64 * HTLC_TIMEOUT_TX_WEIGHT / 1000) + self.counterparty_dust_limit_satoshis; + + let mut addl_htlcs = 0; + if fee_spike_buffer_htlc.is_some() { addl_htlcs += 1; } + match htlc.origin { + HTLCInitiator::LocalOffered => { + if htlc.amount_msat / 1000 >= real_dust_limit_success_sat { + addl_htlcs += 1; + } + }, + HTLCInitiator::RemoteOffered => { + if htlc.amount_msat / 1000 >= real_dust_limit_timeout_sat { + addl_htlcs += 1; + } + } + } + + // When calculating the set of HTLCs which will be included in their next commitment_signed, all + // non-dust inbound HTLCs are included (as all states imply it will be included) and only + // committed outbound HTLCs, see below. + let mut included_htlcs = 0; + for ref htlc in self.pending_inbound_htlcs.iter() { + if htlc.amount_msat / 1000 <= real_dust_limit_timeout_sat { + continue + } + included_htlcs += 1; + } + for ref htlc in self.pending_outbound_htlcs.iter() { - if htlc.amount_msat / 1000 <= self.counterparty_dust_limit_satoshis { + if htlc.amount_msat / 1000 <= real_dust_limit_success_sat { continue } - // We only include outbound HTLCs if it will not be included in their next - // commitment_signed, i.e. if they've responded to us with an RAA after announcement. + // We only include outbound HTLCs if it will not be included in their next commitment_signed, + // i.e. if they've responded to us with an RAA after announcement. match htlc.state { - OutboundHTLCState::Committed => their_acked_htlcs += 1, - OutboundHTLCState::RemoteRemoved {..} => their_acked_htlcs += 1, + OutboundHTLCState::Committed => included_htlcs += 1, + OutboundHTLCState::RemoteRemoved {..} => included_htlcs += 1, + OutboundHTLCState::LocalAnnounced { .. } => included_htlcs += 1, _ => {}, } } - self.commit_tx_fee_msat(their_acked_htlcs + addl_htlcs) + let num_htlcs = included_htlcs + addl_htlcs; + let res = self.commit_tx_fee_msat(num_htlcs); + #[cfg(any(test, feature = "fuzztarget"))] + { + let mut fee = res; + if fee_spike_buffer_htlc.is_some() { + fee = self.commit_tx_fee_msat(num_htlcs - 1); + } + let total_pending_htlcs = self.pending_inbound_htlcs.len() + self.pending_outbound_htlcs.len(); + let commitment_tx_info = CommitmentTxInfoCached { + fee, + total_pending_htlcs, + next_holder_htlc_id: match htlc.origin { + HTLCInitiator::LocalOffered => self.next_holder_htlc_id + 1, + HTLCInitiator::RemoteOffered => self.next_holder_htlc_id, + }, + next_counterparty_htlc_id: match htlc.origin { + HTLCInitiator::LocalOffered => self.next_counterparty_htlc_id, + HTLCInitiator::RemoteOffered => self.next_counterparty_htlc_id + 1, + }, + feerate: self.feerate_per_kw, + }; + *self.next_remote_commitment_tx_fee_info_cached.lock().unwrap() = Some(commitment_tx_info); + } + res } pub fn update_add_htlc(&mut self, msg: &msgs::UpdateAddHTLC, mut pending_forward_status: PendingHTLCStatus, create_pending_htlc_status: F, logger: &L) -> Result<(), ChannelError> @@ -1772,7 +1943,7 @@ impl Channel { if inbound_htlc_count + 1 > OUR_MAX_HTLCS as u32 { return Err(ChannelError::Close(format!("Remote tried to push more than our max accepted HTLCs ({})", OUR_MAX_HTLCS))); } - let holder_max_htlc_value_in_flight_msat = Channel::::get_holder_max_htlc_value_in_flight_msat(self.channel_value_satoshis); + let holder_max_htlc_value_in_flight_msat = Channel::::get_holder_max_htlc_value_in_flight_msat(self.channel_value_satoshis); if htlc_inbound_value_msat + msg.amount_msat > holder_max_htlc_value_in_flight_msat { return Err(ChannelError::Close(format!("Remote HTLC add would put them over our max HTLC value ({})", holder_max_htlc_value_in_flight_msat))); } @@ -1808,28 +1979,30 @@ impl Channel { // Check that the remote can afford to pay for this HTLC on-chain at the current // feerate_per_kw, while maintaining their channel reserve (as required by the spec). let remote_commit_tx_fee_msat = if self.is_outbound() { 0 } else { - // +1 for this HTLC. - self.next_remote_commit_tx_fee_msat(1) + let htlc_candidate = HTLCCandidate::new(msg.amount_msat, HTLCInitiator::RemoteOffered); + self.next_remote_commit_tx_fee_msat(htlc_candidate, None) // Don't include the extra fee spike buffer HTLC in calculations }; if pending_remote_value_msat - msg.amount_msat < remote_commit_tx_fee_msat { return Err(ChannelError::Close("Remote HTLC add would not leave enough to pay for fees".to_owned())); }; let chan_reserve_msat = - Channel::::get_holder_selected_channel_reserve_satoshis(self.channel_value_satoshis) * 1000; + Channel::::get_holder_selected_channel_reserve_satoshis(self.channel_value_satoshis) * 1000; if pending_remote_value_msat - msg.amount_msat - remote_commit_tx_fee_msat < chan_reserve_msat { return Err(ChannelError::Close("Remote HTLC add would put them under remote reserve value".to_owned())); } if !self.is_outbound() { - // `+1` for this HTLC, `2 *` and `+1` fee spike buffer we keep for the remote. This deviates from the - // spec because in the spec, the fee spike buffer requirement doesn't exist on the receiver's side, - // only on the sender's. - // Note that when we eventually remove support for fee updates and switch to anchor output fees, - // we will drop the `2 *`, since we no longer be as sensitive to fee spikes. But, keep the extra +1 - // as we should still be able to afford adding this HTLC plus one more future HTLC, regardless of - // being sensitive to fee spikes. - let remote_fee_cost_incl_stuck_buffer_msat = 2 * self.next_remote_commit_tx_fee_msat(1 + 1); + // `2 *` and `Some(())` is for the fee spike buffer we keep for the remote. This deviates from + // the spec because in the spec, the fee spike buffer requirement doesn't exist on the + // receiver's side, only on the sender's. + // Note that when we eventually remove support for fee updates and switch to anchor output + // fees, we will drop the `2 *`, since we no longer be as sensitive to fee spikes. But, keep + // the extra htlc when calculating the next remote commitment transaction fee as we should + // still be able to afford adding this HTLC plus one more future HTLC, regardless of being + // sensitive to fee spikes. + let htlc_candidate = HTLCCandidate::new(msg.amount_msat, HTLCInitiator::RemoteOffered); + let remote_fee_cost_incl_stuck_buffer_msat = 2 * self.next_remote_commit_tx_fee_msat(htlc_candidate, Some(())); if pending_remote_value_msat - msg.amount_msat - chan_reserve_msat < remote_fee_cost_incl_stuck_buffer_msat { // Note that if the pending_forward_status is not updated here, then it's because we're already failing // the HTLC, i.e. its status is already set to failing. @@ -1838,9 +2011,8 @@ impl Channel { } } else { // Check that they won't violate our local required channel reserve by adding this HTLC. - - // +1 for this HTLC. - let local_commit_tx_fee_msat = self.next_local_commit_tx_fee_msat(1); + let htlc_candidate = HTLCCandidate::new(msg.amount_msat, HTLCInitiator::RemoteOffered); + let local_commit_tx_fee_msat = self.next_local_commit_tx_fee_msat(htlc_candidate, None); if self.value_to_self_msat < self.counterparty_selected_channel_reserve_satoshis * 1000 + local_commit_tx_fee_msat { return Err(ChannelError::Close("Cannot accept HTLC that would put our balance under counterparty-announced channel reserve value".to_owned())); } @@ -1976,15 +2148,31 @@ impl Channel { (commitment_tx.1, htlcs_cloned, commitment_tx.0, commitment_txid) }; + let total_fee = feerate_per_kw as u64 * (COMMITMENT_TX_BASE_WEIGHT + (num_htlcs as u64) * COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000; //If channel fee was updated by funder confirm funder can afford the new fee rate when applied to the current local commitment transaction if update_fee { - let total_fee = feerate_per_kw as u64 * (COMMITMENT_TX_BASE_WEIGHT + (num_htlcs as u64) * COMMITMENT_TX_WEIGHT_PER_HTLC) / 1000; - - let counterparty_reserve_we_require = Channel::::get_holder_selected_channel_reserve_satoshis(self.channel_value_satoshis); + let counterparty_reserve_we_require = Channel::::get_holder_selected_channel_reserve_satoshis(self.channel_value_satoshis); if self.channel_value_satoshis - self.value_to_self_msat / 1000 < total_fee + counterparty_reserve_we_require { return Err((None, ChannelError::Close("Funding remote cannot afford proposed new fee".to_owned()))); } } + #[cfg(any(test, feature = "fuzztarget"))] + { + if self.is_outbound() { + let projected_commit_tx_info = self.next_local_commitment_tx_fee_info_cached.lock().unwrap().take(); + *self.next_remote_commitment_tx_fee_info_cached.lock().unwrap() = None; + if let Some(info) = projected_commit_tx_info { + let total_pending_htlcs = self.pending_inbound_htlcs.len() + self.pending_outbound_htlcs.len() + + self.holding_cell_htlc_updates.len(); + if info.total_pending_htlcs == total_pending_htlcs + && info.next_holder_htlc_id == self.next_holder_htlc_id + && info.next_counterparty_htlc_id == self.next_counterparty_htlc_id + && info.feerate == self.feerate_per_kw { + assert_eq!(total_fee, info.fee / 1000); + } + } + } + } if msg.htlc_signatures.len() != num_htlcs { return Err((None, ChannelError::Close(format!("Got wrong number of HTLC signatures ({}) from remote. It must be {}", msg.htlc_signatures.len(), num_htlcs)))); @@ -2015,8 +2203,8 @@ impl Channel { self.counterparty_funding_pubkey() ); - let next_per_commitment_point = self.holder_keys.get_per_commitment_point(self.cur_holder_commitment_transaction_number - 1, &self.secp_ctx); - let per_commitment_secret = self.holder_keys.release_commitment_secret(self.cur_holder_commitment_transaction_number + 1); + let next_per_commitment_point = self.holder_signer.get_per_commitment_point(self.cur_holder_commitment_transaction_number - 1, &self.secp_ctx); + let per_commitment_secret = self.holder_signer.release_commitment_secret(self.cur_holder_commitment_transaction_number + 1); // Update state now that we've passed all the can-fail calls... let mut need_commitment = false; @@ -2250,6 +2438,12 @@ impl Channel { return Err(ChannelError::Close("Received an unexpected revoke_and_ack".to_owned())); } + #[cfg(any(test, feature = "fuzztarget"))] + { + *self.next_local_commitment_tx_fee_info_cached.lock().unwrap() = None; + *self.next_remote_commitment_tx_fee_info_cached.lock().unwrap() = None; + } + self.commitment_secrets.provide_secret(self.cur_counterparty_commitment_transaction_number + 1, msg.per_commitment_secret) .map_err(|_| ChannelError::Close("Previous secrets did not match new one".to_owned()))?; self.latest_monitor_update_id += 1; @@ -2571,22 +2765,23 @@ impl Channel { /// 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(&mut self, logger: &L) -> (Option, Option, RAACommitmentOrder, Vec<(PendingHTLCInfo, u64)>, Vec<(HTLCSource, PaymentHash, HTLCFailReason)>, bool, Option) where L::Target: Logger { + pub fn monitor_updating_restored(&mut self, logger: &L) -> (Option, Option, RAACommitmentOrder, Vec<(PendingHTLCInfo, u64)>, Vec<(HTLCSource, PaymentHash, HTLCFailReason)>, Option, Option) 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_keys.get_per_commitment_point(self.cur_holder_commitment_transaction_number, &self.secp_ctx); + let next_per_commitment_point = self.holder_signer.get_per_commitment_point(self.cur_holder_commitment_transaction_number, &self.secp_ctx); Some(msgs::FundingLocked { channel_id: self.channel_id(), next_per_commitment_point, @@ -2601,7 +2796,7 @@ impl Channel { 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 { @@ -2615,11 +2810,11 @@ impl Channel { 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(&mut self, fee_estimator: &F, msg: &msgs::UpdateFee) -> Result<(), ChannelError> @@ -2631,15 +2826,15 @@ impl Channel { if self.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 { return Err(ChannelError::Close("Peer sent update_fee when we needed a channel_reestablish".to_owned())); } - Channel::::check_remote_fee(fee_estimator, msg.feerate_per_kw)?; + Channel::::check_remote_fee(fee_estimator, msg.feerate_per_kw)?; self.pending_update_fee = Some(msg.feerate_per_kw); self.update_time_counter += 1; Ok(()) } fn get_last_revoke_and_ack(&self) -> msgs::RevokeAndACK { - let next_per_commitment_point = self.holder_keys.get_per_commitment_point(self.cur_holder_commitment_transaction_number, &self.secp_ctx); - let per_commitment_secret = self.holder_keys.release_commitment_secret(self.cur_holder_commitment_transaction_number + 2); + let next_per_commitment_point = self.holder_signer.get_per_commitment_point(self.cur_holder_commitment_transaction_number, &self.secp_ctx); + let per_commitment_secret = self.holder_signer.release_commitment_secret(self.cur_holder_commitment_transaction_number + 2); msgs::RevokeAndACK { channel_id: self.channel_id, per_commitment_secret, @@ -2722,7 +2917,7 @@ impl Channel { if msg.next_remote_commitment_number > 0 { match msg.data_loss_protect { OptionalField::Present(ref data_loss) => { - let expected_point = self.holder_keys.get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - msg.next_remote_commitment_number + 1, &self.secp_ctx); + let expected_point = self.holder_signer.get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - msg.next_remote_commitment_number + 1, &self.secp_ctx); let given_secret = SecretKey::from_slice(&data_loss.your_last_per_commitment_secret) .map_err(|_| ChannelError::Close("Peer sent a garbage channel_reestablish with unparseable secret key".to_owned()))?; if expected_point != PublicKey::from_secret_key(&self.secp_ctx, &given_secret) { @@ -2761,7 +2956,7 @@ impl Channel { } // We have OurFundingLocked set! - let next_per_commitment_point = self.holder_keys.get_per_commitment_point(self.cur_holder_commitment_transaction_number, &self.secp_ctx); + let next_per_commitment_point = self.holder_signer.get_per_commitment_point(self.cur_holder_commitment_transaction_number, &self.secp_ctx); return Ok((Some(msgs::FundingLocked { channel_id: self.channel_id(), next_per_commitment_point, @@ -2791,7 +2986,7 @@ impl Channel { let resend_funding_locked = if msg.next_local_commitment_number == 1 && INITIAL_COMMITMENT_NUMBER - self.cur_holder_commitment_transaction_number == 1 { // We should never have to worry about MonitorUpdateFailed resending FundingLocked - let next_per_commitment_point = self.holder_keys.get_per_commitment_point(self.cur_holder_commitment_transaction_number, &self.secp_ctx); + let next_per_commitment_point = self.holder_signer.get_per_commitment_point(self.cur_holder_commitment_transaction_number, &self.secp_ctx); Some(msgs::FundingLocked { channel_id: self.channel_id(), next_per_commitment_point, @@ -2872,7 +3067,7 @@ impl Channel { let proposed_total_fee_satoshis = proposed_feerate as u64 * tx_weight / 1000; let (closing_tx, total_fee_satoshis) = self.build_closing_transaction(proposed_total_fee_satoshis, false); - let sig = self.holder_keys + let sig = self.holder_signer .sign_closing_transaction(&closing_tx, &self.secp_ctx) .ok(); assert!(closing_tx.get_weight() as u64 <= tx_weight); @@ -2886,7 +3081,7 @@ impl Channel { }) } - pub fn shutdown(&mut self, fee_estimator: &F, msg: &msgs::Shutdown) -> Result<(Option, Option, Vec<(HTLCSource, PaymentHash)>), ChannelError> + pub fn shutdown(&mut self, fee_estimator: &F, their_features: &InitFeatures, msg: &msgs::Shutdown) -> Result<(Option, Option, Vec<(HTLCSource, PaymentHash)>), ChannelError> where F::Target: FeeEstimator { if self.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 { @@ -2905,14 +3100,7 @@ impl Channel { } assert_eq!(self.channel_state & ChannelState::ShutdownComplete as u32, 0); - // BOLT 2 says we must only send a scriptpubkey of certain standard forms, which are up to - // 34 bytes in length, so don't let the remote peer feed us some super fee-heavy script. - if self.is_outbound() && msg.scriptpubkey.len() > 34 { - return Err(ChannelError::Close(format!("Got counterparty shutdown_scriptpubkey ({}) of absurd length from remote peer", msg.scriptpubkey.to_bytes().to_hex()))); - } - - //Check counterparty_shutdown_scriptpubkey form as BOLT says we must - if !msg.scriptpubkey.is_p2pkh() && !msg.scriptpubkey.is_p2sh() && !msg.scriptpubkey.is_v0_p2wpkh() && !msg.scriptpubkey.is_v0_p2wsh() { + if is_unsupported_shutdown_script(&their_features, &msg.scriptpubkey) { return Err(ChannelError::Close(format!("Got a nonstandard scriptpubkey ({}) from remote peer", msg.scriptpubkey.to_bytes().to_hex()))); } @@ -3036,7 +3224,7 @@ impl Channel { ($new_feerate: expr) => { let tx_weight = self.get_closing_transaction_weight(Some(&self.get_closing_scriptpubkey()), Some(self.counterparty_shutdown_scriptpubkey.as_ref().unwrap())); let (closing_tx, used_total_fee) = self.build_closing_transaction($new_feerate as u64 * tx_weight / 1000, false); - let sig = self.holder_keys + let sig = self.holder_signer .sign_closing_transaction(&closing_tx, &self.secp_ctx) .map_err(|_| ChannelError::Close("External signer refused to sign closing transaction".to_owned()))?; assert!(closing_tx.get_weight() as u64 <= tx_weight); @@ -3072,7 +3260,7 @@ impl Channel { propose_new_feerate!(min_feerate); } - let sig = self.holder_keys + let sig = self.holder_signer .sign_closing_transaction(&closing_tx, &self.secp_ctx) .map_err(|_| ChannelError::Close("External signer refused to sign closing transaction".to_owned()))?; self.build_signed_closing_transaction(&mut closing_tx, &msg.signature, &sig); @@ -3147,9 +3335,9 @@ impl Channel { // Upper bound by capacity. We make it a bit less than full capacity to prevent attempts // to use full capacity. This is an effort to reduce routing failures, because in many cases // channel might have been used to route very small values (either by honest users or as DoS). - self.channel_value_satoshis * 9 / 10, + self.channel_value_satoshis * 1000 * 9 / 10, - Channel::::get_holder_max_htlc_value_in_flight_msat(self.channel_value_satoshis) + Channel::::get_holder_max_htlc_value_in_flight_msat(self.channel_value_satoshis) ); } @@ -3166,6 +3354,10 @@ impl Channel { self.config.fee_proportional_millionths } + pub fn get_cltv_expiry_delta(&self) -> u16 { + cmp::max(self.config.cltv_expiry_delta, MIN_CLTV_EXPIRY_DELTA) + } + #[cfg(test)] pub fn get_feerate(&self) -> u32 { self.feerate_per_kw @@ -3184,8 +3376,8 @@ impl Channel { } #[cfg(test)] - pub fn get_keys(&self) -> &ChanSigner { - &self.holder_keys + pub fn get_signer(&self) -> &Signer { + &self.holder_signer } #[cfg(test)] @@ -3312,26 +3504,139 @@ impl Channel { self.network_sync == UpdateStatus::DisabledMarked } - /// When we receive a new block, we (a) check whether the block contains the funding - /// transaction (which would start us counting blocks until we send the funding_signed), and - /// (b) check the height of the block against outbound holding cell HTLCs in case we need to - /// give up on them prematurely and time them out. Everything else (e.g. commitment - /// transaction broadcasts, channel closure detection, HTLC transaction broadcasting, etc) is + fn check_get_funding_locked(&mut self, height: u32) -> Option { + if self.funding_tx_confirmation_height == 0 { + return None; + } + + let funding_tx_confirmations = height as i64 - self.funding_tx_confirmation_height as i64 + 1; + if funding_tx_confirmations <= 0 { + self.funding_tx_confirmation_height = 0; + } + + if funding_tx_confirmations < self.minimum_depth as i64 { + return None; + } + + let non_shutdown_state = self.channel_state & (!MULTI_STATE_FLAGS); + let need_commitment_update = if non_shutdown_state == ChannelState::FundingSent as u32 { + self.channel_state |= ChannelState::OurFundingLocked as u32; + true + } else if non_shutdown_state == (ChannelState::FundingSent as u32 | ChannelState::TheirFundingLocked as u32) { + self.channel_state = ChannelState::ChannelFunded as u32 | (self.channel_state & MULTI_STATE_FLAGS); + self.update_time_counter += 1; + true + } else if non_shutdown_state == (ChannelState::FundingSent as u32 | ChannelState::OurFundingLocked as u32) { + // We got a reorg but not enough to trigger a force close, just ignore. + false + } else if self.channel_state < ChannelState::ChannelFunded as u32 { + panic!("Started confirming a channel in a state pre-FundingSent?: {}", self.channel_state); + } else { + // We got a reorg but not enough to trigger a force close, just ignore. + false + }; + + if need_commitment_update { + if self.channel_state & (ChannelState::MonitorUpdateFailed as u32) == 0 { + let next_per_commitment_point = self.holder_signer.get_per_commitment_point(self.cur_holder_commitment_transaction_number, &self.secp_ctx); + return Some(msgs::FundingLocked { + channel_id: self.channel_id, + next_per_commitment_point, + }); + } else { + self.monitor_pending_funding_locked = true; + } + } + None + } + + /// When a transaction is confirmed, we check whether it is or spends the funding transaction + /// In the first case, we store the confirmation height and calculating the short channel id. + /// In the second, we simply return an Err indicating we need to be force-closed now. + pub fn transactions_confirmed(&mut self, block_hash: &BlockHash, height: u32, txdata: &TransactionData, logger: &L) + -> Result, msgs::ErrorMessage> where L::Target: Logger { + let non_shutdown_state = self.channel_state & (!MULTI_STATE_FLAGS); + for &(index_in_block, tx) in txdata.iter() { + if let Some(funding_txo) = self.get_funding_txo() { + // If we haven't yet sent a funding_locked, but are in FundingSent (ignoring + // whether they've sent a funding_locked or not), check if we should send one. + if non_shutdown_state & !(ChannelState::TheirFundingLocked as u32) == ChannelState::FundingSent as u32 { + if tx.txid() == funding_txo.txid { + let txo_idx = funding_txo.index as usize; + if txo_idx >= tx.output.len() || tx.output[txo_idx].script_pubkey != self.get_funding_redeemscript().to_v0_p2wsh() || + tx.output[txo_idx].value != self.channel_value_satoshis { + if self.is_outbound() { + // If we generated the funding transaction and it doesn't match what it + // should, the client is really broken and we should just panic and + // tell them off. That said, because hash collisions happen with high + // probability in fuzztarget mode, if we're fuzzing we just close the + // channel and move on. + #[cfg(not(feature = "fuzztarget"))] + panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction!"); + } + self.update_time_counter += 1; + return Err(msgs::ErrorMessage { + channel_id: self.channel_id(), + data: "funding tx had wrong script/value or output index".to_owned() + }); + } else { + if self.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(feature = "fuzztarget"))] + panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction!"); + } + } + } + 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), + Err(_) => panic!("Block was bogus - either height was > 16 million, had > 16 million transactions, or had > 65k outputs"), + } + } + } + // If we allow 1-conf funding, we may need to check for funding_locked here and + // 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)); + } + } + for inp in tx.input.iter() { + if inp.previous_output == funding_txo.into_bitcoin_outpoint() { + log_trace!(logger, "Detected channel-closing tx {} spending {}:{}, closing channel {}", tx.txid(), inp.previous_output.txid, inp.previous_output.vout, log_bytes!(self.channel_id())); + return Err(msgs::ErrorMessage { + channel_id: self.channel_id(), + data: "Commitment or closing transaction was confirmed on chain.".to_owned() + }); + } + } + } + } + Ok(None) + } + + /// When a new block is connected, we check the height of the block against outbound holding + /// cell HTLCs in case we need to give up on them prematurely and time them out. Everything + /// else (e.g. commitment transaction broadcasts, HTLC transaction broadcasting, etc) is /// handled by the ChannelMonitor. /// /// If we return Err, the channel may have been closed, at which point the standard /// requirements apply - no calls may be made except those explicitly stated to be allowed /// post-shutdown. - /// Only returns an ErrorAction of DisconnectPeer, if Err. /// /// May return some HTLCs (and their payment_hash) which have timed out and should be failed /// back. - pub fn block_connected(&mut self, header: &BlockHeader, txdata: &TransactionData, height: u32) -> Result<(Option, Vec<(HTLCSource, PaymentHash)>), msgs::ErrorMessage> { + pub fn best_block_updated(&mut self, height: u32, highest_header_time: u32) -> Result<(Option, 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| { match htlc_update { &HTLCUpdateAwaitingACK::AddHTLC { ref payment_hash, ref source, ref cltv_expiry, .. } => { - if *cltv_expiry <= height + HTLC_FAIL_BACK_BUFFER { + if *cltv_expiry <= unforwarded_htlc_cltv_limit { timed_out_htlcs.push((source.clone(), payment_hash.clone())); false } else { true } @@ -3339,119 +3644,63 @@ impl Channel { _ => true } }); - let non_shutdown_state = self.channel_state & (!MULTI_STATE_FLAGS); - if header.block_hash() != self.last_block_connected { - if self.funding_tx_confirmations > 0 { - self.funding_tx_confirmations += 1; - } + + self.update_time_counter = cmp::max(self.update_time_counter, highest_header_time); + + if let Some(funding_locked) = self.check_get_funding_locked(height) { + return Ok((Some(funding_locked), timed_out_htlcs)); } - if non_shutdown_state & !(ChannelState::TheirFundingLocked as u32) == ChannelState::FundingSent as u32 { - for &(index_in_block, tx) in txdata.iter() { - let funding_txo = self.get_funding_txo().unwrap(); - if tx.txid() == funding_txo.txid { - let txo_idx = funding_txo.index as usize; - if txo_idx >= tx.output.len() || tx.output[txo_idx].script_pubkey != self.get_funding_redeemscript().to_v0_p2wsh() || - tx.output[txo_idx].value != self.channel_value_satoshis { - if self.is_outbound() { - // If we generated the funding transaction and it doesn't match what it - // should, the client is really broken and we should just panic and - // tell them off. That said, because hash collisions happen with high - // probability in fuzztarget mode, if we're fuzzing we just close the - // channel and move on. - #[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(), - data: "funding tx had wrong script/value".to_owned() - }); - } else { - if self.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(feature = "fuzztarget"))] - panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction!"); - } - } - } - if height > 0xff_ff_ff || (index_in_block) > 0xff_ff_ff { - panic!("Block was bogus - either height 16 million or had > 16 million transactions"); - } - assert!(txo_idx <= 0xffff); // txo_idx is a (u16 as usize), so this is just listed here for completeness - self.funding_tx_confirmations = 1; - self.short_channel_id = Some(((height as u64) << (5*8)) | - ((index_in_block as u64) << (2*8)) | - ((txo_idx as u64) << (0*8))); - } - } + + let non_shutdown_state = self.channel_state & (!MULTI_STATE_FLAGS); + if non_shutdown_state >= ChannelState::ChannelFunded as u32 || + (non_shutdown_state & ChannelState::OurFundingLocked as u32) == ChannelState::OurFundingLocked as u32 { + let mut funding_tx_confirmations = height as i64 - self.funding_tx_confirmation_height as i64 + 1; + if self.funding_tx_confirmation_height == 0 { + // Note that check_get_funding_locked may reset funding_tx_confirmation_height to + // zero if it has been reorged out, however in either case, our state flags + // indicate we've already sent a funding_locked + funding_tx_confirmations = 0; } - } - if header.block_hash() != self.last_block_connected { - self.last_block_connected = header.block_hash(); - self.update_time_counter = cmp::max(self.update_time_counter, header.time); - if self.funding_tx_confirmations > 0 { - if self.funding_tx_confirmations == self.minimum_depth as u64 { - let need_commitment_update = if non_shutdown_state == ChannelState::FundingSent as u32 { - self.channel_state |= ChannelState::OurFundingLocked as u32; - true - } else if non_shutdown_state == (ChannelState::FundingSent as u32 | ChannelState::TheirFundingLocked as u32) { - self.channel_state = ChannelState::ChannelFunded as u32 | (self.channel_state & MULTI_STATE_FLAGS); - self.update_time_counter += 1; - true - } else if non_shutdown_state == (ChannelState::FundingSent as u32 | ChannelState::OurFundingLocked as u32) { - // We got a reorg but not enough to trigger a force close, just update - // funding_tx_confirmed_in and return. - false - } else if self.channel_state < ChannelState::ChannelFunded as u32 { - panic!("Started confirming a channel in a state pre-FundingSent?: {}", self.channel_state); - } else { - // We got a reorg but not enough to trigger a force close, just update - // funding_tx_confirmed_in and return. - false - }; - self.funding_tx_confirmed_in = Some(self.last_block_connected); - - //TODO: Note that this must be a duplicate of the previous commitment point they sent us, - //as otherwise we will have a commitment transaction that they can't revoke (well, kinda, - //they can by sending two revoke_and_acks back-to-back, but not really). This appears to be - //a protocol oversight, but I assume I'm just missing something. - if need_commitment_update { - if self.channel_state & (ChannelState::MonitorUpdateFailed as u32) == 0 { - let next_per_commitment_point = self.holder_keys.get_per_commitment_point(self.cur_holder_commitment_transaction_number, &self.secp_ctx); - return Ok((Some(msgs::FundingLocked { - channel_id: self.channel_id, - next_per_commitment_point, - }), timed_out_htlcs)); - } else { - self.monitor_pending_funding_locked = true; - return Ok((None, timed_out_htlcs)); - } - } - } + + // If we've sent funding_locked (or have both sent and received funding_locked), and + // the funding transaction's confirmation count has dipped below minimum_depth / 2, + // close the channel and hope we can get the latest state on chain (because presumably + // the funding transaction is at least still in the mempool of most nodes). + if funding_tx_confirmations < self.minimum_depth as i64 / 2 { + return Err(msgs::ErrorMessage { + channel_id: self.channel_id(), + data: format!("Funding transaction was un-confirmed. Locked at {} confs, now have {} confs.", self.minimum_depth, funding_tx_confirmations), + }); } } + Ok((None, timed_out_htlcs)) } - /// Called by channelmanager based on chain blocks being disconnected. - /// Returns true if we need to close the channel now due to funding transaction - /// unconfirmation/reorg. - pub fn block_disconnected(&mut self, header: &BlockHeader) -> bool { - if self.funding_tx_confirmations > 0 { - self.funding_tx_confirmations -= 1; - if self.funding_tx_confirmations == UNCONF_THRESHOLD as u64 { - return true; + /// 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(()) } - self.last_block_connected = header.block_hash(); - if Some(self.last_block_connected) == self.funding_tx_confirmed_in { - self.funding_tx_confirmations = self.minimum_depth as u64 - 1; - } - false } // Methods to get unprompted messages to send to the remote end (or where we already returned @@ -3469,7 +3718,7 @@ impl Channel { panic!("Tried to send an open_channel for a channel that has already advanced"); } - let first_per_commitment_point = self.holder_keys.get_per_commitment_point(self.cur_holder_commitment_transaction_number, &self.secp_ctx); + let first_per_commitment_point = self.holder_signer.get_per_commitment_point(self.cur_holder_commitment_transaction_number, &self.secp_ctx); let keys = self.get_holder_pubkeys(); msgs::OpenChannel { @@ -3478,8 +3727,8 @@ impl Channel { funding_satoshis: self.channel_value_satoshis, push_msat: self.channel_value_satoshis * 1000 - self.value_to_self_msat, dust_limit_satoshis: self.holder_dust_limit_satoshis, - max_htlc_value_in_flight_msat: Channel::::get_holder_max_htlc_value_in_flight_msat(self.channel_value_satoshis), - channel_reserve_satoshis: Channel::::get_holder_selected_channel_reserve_satoshis(self.channel_value_satoshis), + max_htlc_value_in_flight_msat: Channel::::get_holder_max_htlc_value_in_flight_msat(self.channel_value_satoshis), + channel_reserve_satoshis: Channel::::get_holder_selected_channel_reserve_satoshis(self.channel_value_satoshis), htlc_minimum_msat: self.holder_htlc_minimum_msat, feerate_per_kw: self.feerate_per_kw as u32, to_self_delay: self.get_holder_selected_contest_delay(), @@ -3506,14 +3755,14 @@ impl Channel { panic!("Tried to send an accept_channel for a channel that has already advanced"); } - let first_per_commitment_point = self.holder_keys.get_per_commitment_point(self.cur_holder_commitment_transaction_number, &self.secp_ctx); + let first_per_commitment_point = self.holder_signer.get_per_commitment_point(self.cur_holder_commitment_transaction_number, &self.secp_ctx); let keys = self.get_holder_pubkeys(); msgs::AcceptChannel { temporary_channel_id: self.channel_id, dust_limit_satoshis: self.holder_dust_limit_satoshis, - max_htlc_value_in_flight_msat: Channel::::get_holder_max_htlc_value_in_flight_msat(self.channel_value_satoshis), - channel_reserve_satoshis: Channel::::get_holder_selected_channel_reserve_satoshis(self.channel_value_satoshis), + max_htlc_value_in_flight_msat: Channel::::get_holder_max_htlc_value_in_flight_msat(self.channel_value_satoshis), + channel_reserve_satoshis: Channel::::get_holder_selected_channel_reserve_satoshis(self.channel_value_satoshis), htlc_minimum_msat: self.holder_htlc_minimum_msat, minimum_depth: self.minimum_depth, to_self_delay: self.get_holder_selected_contest_delay(), @@ -3532,7 +3781,7 @@ impl Channel { fn get_outbound_funding_created_signature(&mut self, logger: &L) -> Result where L::Target: Logger { let counterparty_keys = self.build_remote_transaction_keys()?; let counterparty_initial_commitment_tx = self.build_commitment_transaction(self.cur_counterparty_commitment_transaction_number, &counterparty_keys, false, false, self.feerate_per_kw, logger).0; - Ok(self.holder_keys.sign_counterparty_commitment(&counterparty_initial_commitment_tx, &self.secp_ctx) + Ok(self.holder_signer.sign_counterparty_commitment(&counterparty_initial_commitment_tx, &self.secp_ctx) .map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed".to_owned()))?.0) } @@ -3543,7 +3792,7 @@ impl Channel { /// 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(&mut self, funding_txo: OutPoint, logger: &L) -> Result where L::Target: Logger { + pub fn get_outbound_funding_created(&mut self, funding_transaction: Transaction, funding_txo: OutPoint, logger: &L) -> Result where L::Target: Logger { if !self.is_outbound() { panic!("Tried to create outbound funding_created message on an inbound channel!"); } @@ -3557,7 +3806,7 @@ impl Channel { } self.channel_transaction_parameters.funding_outpoint = Some(funding_txo); - self.holder_keys.ready_channel(&self.channel_transaction_parameters); + self.holder_signer.ready_channel(&self.channel_transaction_parameters); let signature = match self.get_outbound_funding_created_signature(logger) { Ok(res) => res, @@ -3574,6 +3823,7 @@ impl 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, @@ -3615,7 +3865,7 @@ impl Channel { excess_data: Vec::new(), }; - let sig = self.holder_keys.sign_channel_announcement(&msg, &self.secp_ctx) + let sig = self.holder_signer.sign_channel_announcement(&msg, &self.secp_ctx) .map_err(|_| ChannelError::Ignore("Signer rejected channel_announcement".to_owned()))?; Ok((msg, sig)) @@ -3720,11 +3970,10 @@ impl Channel { if !self.is_outbound() { // Check that we won't violate the remote channel reserve by adding this HTLC. - let counterparty_balance_msat = self.channel_value_satoshis * 1000 - self.value_to_self_msat; - let holder_selected_chan_reserve_msat = Channel::::get_holder_selected_channel_reserve_satoshis(self.channel_value_satoshis); - // 1 additional HTLC corresponding to this HTLC. - let counterparty_commit_tx_fee_msat = self.next_remote_commit_tx_fee_msat(1); + let holder_selected_chan_reserve_msat = Channel::::get_holder_selected_channel_reserve_satoshis(self.channel_value_satoshis); + let htlc_candidate = HTLCCandidate::new(amount_msat, HTLCInitiator::LocalOffered); + let counterparty_commit_tx_fee_msat = self.next_remote_commit_tx_fee_msat(htlc_candidate, None); if counterparty_balance_msat < holder_selected_chan_reserve_msat + counterparty_commit_tx_fee_msat { return Err(ChannelError::Ignore("Cannot send value that would put counterparty balance under holder-announced channel reserve value".to_owned())); } @@ -3735,10 +3984,10 @@ impl Channel { return Err(ChannelError::Ignore(format!("Cannot send value that would overdraw remaining funds. Amount: {}, pending value to self {}", amount_msat, pending_value_to_self_msat))); } - // The `+1` is for the HTLC currently being added to the commitment tx and - // the `2 *` and `+1` are for the fee spike buffer. + // `2 *` and extra HTLC are for the fee spike buffer. let commit_tx_fee_msat = if self.is_outbound() { - 2 * self.next_local_commit_tx_fee_msat(1 + 1) + let htlc_candidate = HTLCCandidate::new(amount_msat, HTLCInitiator::LocalOffered); + 2 * self.next_local_commit_tx_fee_msat(htlc_candidate, Some(())) } else { 0 }; if pending_value_to_self_msat - amount_msat < commit_tx_fee_msat { return Err(ChannelError::Ignore(format!("Cannot send value that would not leave enough to pay for fees. Pending value to self: {}. local_commit_tx_fee {}", pending_value_to_self_msat, commit_tx_fee_msat))); @@ -3881,18 +4130,37 @@ impl Channel { let counterparty_commitment_txid = counterparty_commitment_tx.0.trust().txid(); let (signature, htlc_signatures); + #[cfg(any(test, feature = "fuzztarget"))] + { + if !self.is_outbound() { + let projected_commit_tx_info = self.next_remote_commitment_tx_fee_info_cached.lock().unwrap().take(); + *self.next_local_commitment_tx_fee_info_cached.lock().unwrap() = None; + if let Some(info) = projected_commit_tx_info { + let total_pending_htlcs = self.pending_inbound_htlcs.len() + self.pending_outbound_htlcs.len(); + if info.total_pending_htlcs == total_pending_htlcs + && info.next_holder_htlc_id == self.next_holder_htlc_id + && info.next_counterparty_htlc_id == self.next_counterparty_htlc_id + && info.feerate == self.feerate_per_kw { + let actual_fee = self.commit_tx_fee_msat(counterparty_commitment_tx.1); + assert_eq!(actual_fee, info.fee); + } + } + } + } + { let mut htlcs = Vec::with_capacity(counterparty_commitment_tx.2.len()); for &(ref htlc, _) in counterparty_commitment_tx.2.iter() { htlcs.push(htlc); } - let res = self.holder_keys.sign_counterparty_commitment(&counterparty_commitment_tx.0, &self.secp_ctx) + let res = self.holder_signer.sign_counterparty_commitment(&counterparty_commitment_tx.0, &self.secp_ctx) .map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed".to_owned()))?; signature = res.0; htlc_signatures = res.1; - log_trace!(logger, "Signed remote commitment tx {} with redeemscript {} -> {}", + log_trace!(logger, "Signed remote commitment tx {} (txid {}) with redeemscript {} -> {}", + encode::serialize_hex(&counterparty_commitment_tx.0.trust().built_transaction().transaction), &counterparty_commitment_txid, encode::serialize_hex(&self.get_funding_redeemscript()), log_bytes!(signature.serialize_compact()[..])); @@ -3927,6 +4195,25 @@ impl Channel { } } + /// Get forwarding information for the counterparty. + pub fn counterparty_forwarding_info(&self) -> Option { + self.counterparty_forwarding_info.clone() + } + + pub fn channel_update(&mut self, msg: &msgs::ChannelUpdate) -> Result<(), ChannelError> { + let usable_channel_value_msat = (self.channel_value_satoshis - self.counterparty_selected_channel_reserve_satoshis) * 1000; + if msg.contents.htlc_minimum_msat >= usable_channel_value_msat { + return Err(ChannelError::Close("Minimum htlc value is greater than channel value".to_string())); + } + self.counterparty_forwarding_info = Some(CounterpartyForwardingInfo { + fee_base_msat: msg.contents.fee_base_msat, + fee_proportional_millionths: msg.contents.fee_proportional_millionths, + cltv_expiry_delta: msg.contents.cltv_expiry_delta + }); + + Ok(()) + } + /// Begins the shutdown process, getting a message for the remote peer and returning all /// holding cell HTLCs for payment failure. pub fn get_shutdown(&mut self) -> Result<(msgs::Shutdown, Vec<(HTLCSource, PaymentHash)>), APIError> { @@ -3983,7 +4270,11 @@ impl Channel { /// those explicitly stated to be allowed after shutdown completes, eg some simple getters). /// Also returns the list of payment_hashes for channels which we can safely fail backwards /// immediately (others we will have to allow to time out). - pub fn force_shutdown(&mut self, should_broadcast: bool) -> (Option, ChannelMonitorUpdate, Vec<(HTLCSource, PaymentHash)>) { + pub fn force_shutdown(&mut self, should_broadcast: bool) -> (Option<(OutPoint, ChannelMonitorUpdate)>, Vec<(HTLCSource, PaymentHash)>) { + // Note that we MUST only generate a monitor update that indicates force-closure - we're + // called during initialization prior to the chain_monitor in the encompassing ChannelManager + // being fully configured in some cases. Thus, its likely any monitor events we generate will + // be delayed in being processed! See the docs for `ChannelManagerReadArgs` for more. assert!(self.channel_state != ChannelState::ShutdownComplete as u32); // We go ahead and "free" any holding cell HTLCs or HTLCs we haven't yet committed to and @@ -3997,7 +4288,7 @@ impl Channel { _ => {} } } - let funding_txo = if let Some(funding_txo) = self.get_funding_txo() { + let monitor_update = if let Some(funding_txo) = self.get_funding_txo() { // If we haven't yet exchanged funding signatures (ie channel_state < FundingSent), // returning a channel monitor update here would imply a channel monitor update before // we even registered the channel monitor to begin with, which is invalid. @@ -4006,18 +4297,36 @@ impl Channel { // monitor update to the user, even if we return one). // See test_duplicate_chan_id and test_pre_lockin_no_chan_closed_update for more. if self.channel_state & (ChannelState::FundingSent as u32 | ChannelState::ChannelFunded as u32 | ChannelState::ShutdownComplete as u32) != 0 { - Some(funding_txo.clone()) + self.latest_monitor_update_id += 1; + Some((funding_txo, ChannelMonitorUpdate { + update_id: self.latest_monitor_update_id, + updates: vec![ChannelMonitorUpdateStep::ChannelForceClosed { should_broadcast }], + })) } else { None } } else { None }; self.channel_state = ChannelState::ShutdownComplete as u32; self.update_time_counter += 1; - self.latest_monitor_update_id += 1; - (funding_txo, ChannelMonitorUpdate { - update_id: self.latest_monitor_update_id, - updates: vec![ChannelMonitorUpdateStep::ChannelForceClosed { should_broadcast }], - }, dropped_outbound_htlcs) + (monitor_update, dropped_outbound_htlcs) + } +} + +fn is_unsupported_shutdown_script(their_features: &InitFeatures, script: &Script) -> bool { + // We restrain shutdown scripts to standards forms to avoid transactions not propagating on the p2p tx-relay network + + // BOLT 2 says we must only send a scriptpubkey of certain standard forms, + // which for a a BIP-141-compliant witness program is at max 42 bytes in length. + // So don't let the remote peer feed us some super fee-heavy script. + let is_script_too_long = script.len() > 42; + if is_script_too_long { + return true; + } + + if their_features.supports_shutdown_anysegwit() && script.is_witness_program() && script.as_bytes()[0] != OP_PUSHBYTES_0.into_u8() { + return false; } + + return !script.is_p2pkh() && !script.is_p2sh() && !script.is_v0_p2wpkh() && !script.is_v0_p2wsh() } const SERIALIZATION_VERSION: u8 = 1; @@ -4055,7 +4364,7 @@ impl Readable for InboundHTLCRemovalReason { } } -impl Writeable for Channel { +impl Writeable for Channel { fn write(&self, writer: &mut W) -> Result<(), ::std::io::Error> { // Note that we write out as if remove_uncommitted_htlcs_and_mark_paused had just been // called but include holding cell updates (and obviously we don't modify self). @@ -4073,7 +4382,7 @@ impl Writeable for Channel { self.latest_monitor_update_id.write(writer)?; let mut key_data = VecWriter(Vec::new()); - self.holder_keys.write(&mut key_data)?; + self.holder_signer.write(&mut key_data)?; assert!(key_data.0.len() < std::usize::MAX); assert!(key_data.0.len() < std::u32::MAX as usize); (key_data.0.len() as u32).write(writer)?; @@ -4216,11 +4525,9 @@ impl Writeable for Channel { } self.funding_tx_confirmed_in.write(writer)?; + self.funding_tx_confirmation_height.write(writer)?; self.short_channel_id.write(writer)?; - self.last_block_connected.write(writer)?; - self.funding_tx_confirmations.write(writer)?; - self.counterparty_dust_limit_satoshis.write(writer)?; self.holder_dust_limit_satoshis.write(writer)?; self.counterparty_max_htlc_value_in_flight_msat.write(writer)?; @@ -4230,9 +4537,20 @@ impl Writeable for Channel { self.counterparty_max_accepted_htlcs.write(writer)?; self.minimum_depth.write(writer)?; + match &self.counterparty_forwarding_info { + Some(info) => { + 1u8.write(writer)?; + info.fee_base_msat.write(writer)?; + info.fee_proportional_millionths.write(writer)?; + info.cltv_expiry_delta.write(writer)?; + }, + None => 0u8.write(writer)? + } + 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)?; @@ -4244,8 +4562,8 @@ impl Writeable for Channel { } const MAX_ALLOC_SIZE: usize = 64*1024; -impl<'a, ChanSigner: ChannelKeys, K: Deref> ReadableArgs<&'a K> for Channel - where K::Target: KeysInterface { +impl<'a, Signer: Sign, K: Deref> ReadableArgs<&'a K> for Channel + where K::Target: KeysInterface { fn read(reader: &mut R, keys_source: &'a K) -> Result { let _ver: u8 = Readable::read(reader)?; let min_ver: u8 = Readable::read(reader)?; @@ -4271,7 +4589,7 @@ impl<'a, ChanSigner: ChannelKeys, K: Deref> ReadableArgs<&'a K> for Channel ReadableArgs<&'a K> for Channel ReadableArgs<&'a K> for Channel::read(reader)? { + 0 => None, + 1 => Some(CounterpartyForwardingInfo { + fee_base_msat: Readable::read(reader)?, + fee_proportional_millionths: Readable::read(reader)?, + cltv_expiry_delta: Readable::read(reader)?, + }), + _ => return Err(DecodeError::InvalidValue), + }; + 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)?; @@ -4401,18 +4729,21 @@ impl<'a, ChanSigner: ChannelKeys, K: Deref> ReadableArgs<&'a K> for Channel ReadableArgs<&'a K> for Channel ReadableArgs<&'a K> for Channel ReadableArgs<&'a K> for Channel SecretKey { panic!(); } fn get_destination_script(&self) -> Script { @@ -4542,11 +4881,11 @@ mod tests { PublicKey::from_secret_key(&secp_ctx, &channel_close_key) } - fn get_channel_keys(&self, _inbound: bool, _channel_value_satoshis: u64) -> InMemoryChannelKeys { - self.chan_keys.clone() + fn get_channel_signer(&self, _inbound: bool, _channel_value_satoshis: u64) -> InMemorySigner { + self.signer.clone() } fn get_secure_random_bytes(&self) -> [u8; 32] { [0; 32] } - fn read_chan_signer(&self, _data: &[u8]) -> Result { panic!(); } + fn read_chan_signer(&self, _data: &[u8]) -> Result { panic!(); } } fn public_from_secret_hex(secp_ctx: &Secp256k1, hex: &str) -> PublicKey { @@ -4566,7 +4905,7 @@ mod tests { let node_a_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); let config = UserConfig::default(); - let node_a_chan = Channel::::new_outbound(&&fee_est, &&keys_provider, node_a_node_id, 10000000, 100000, 42, &config).unwrap(); + let node_a_chan = Channel::::new_outbound(&&fee_est, &&keys_provider, node_a_node_id, 10000000, 100000, 42, &config).unwrap(); // Now change the fee so we can check that the fee in the open_channel message is the // same as the old fee. @@ -4575,6 +4914,122 @@ mod tests { assert_eq!(open_channel_msg.feerate_per_kw, original_fee); } + #[test] + fn test_holder_vs_counterparty_dust_limit() { + // Test that when calculating the local and remote commitment transaction fees, the correct + // dust limits are used. + let feeest = TestFeeEstimator{fee_est: 15000}; + let secp_ctx = Secp256k1::new(); + let seed = [42; 32]; + let network = Network::Testnet; + let keys_provider = test_utils::TestKeysInterface::new(&seed, network); + + // Go through the flow of opening a channel between two nodes, making sure + // they have different dust limits. + + // Create Node A's channel pointing to Node B's pubkey + let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); + let config = UserConfig::default(); + let mut node_a_chan = Channel::::new_outbound(&&feeest, &&keys_provider, node_b_node_id, 10000000, 100000, 42, &config).unwrap(); + + // Create Node B's channel by receiving Node A's open_channel message + // Make sure A's dust limit is as we expect. + let open_channel_msg = node_a_chan.get_open_channel(genesis_block(network).header.block_hash()); + assert_eq!(open_channel_msg.dust_limit_satoshis, 1560); + let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap()); + let node_b_chan = Channel::::new_from_req(&&feeest, &&keys_provider, node_b_node_id, InitFeatures::known(), &open_channel_msg, 7, &config).unwrap(); + + // Node B --> Node A: accept channel, explicitly setting B's dust limit. + let mut accept_channel_msg = node_b_chan.get_accept_channel(); + accept_channel_msg.dust_limit_satoshis = 546; + node_a_chan.accept_channel(&accept_channel_msg, &config, InitFeatures::known()).unwrap(); + + // Put some inbound and outbound HTLCs in A's channel. + let htlc_amount_msat = 11_092_000; // put an amount below A's effective dust limit but above B's. + node_a_chan.pending_inbound_htlcs.push(InboundHTLCOutput { + htlc_id: 0, + amount_msat: htlc_amount_msat, + payment_hash: PaymentHash(Sha256::hash(&[42; 32]).into_inner()), + cltv_expiry: 300000000, + state: InboundHTLCState::Committed, + }); + + node_a_chan.pending_outbound_htlcs.push(OutboundHTLCOutput { + htlc_id: 1, + amount_msat: htlc_amount_msat, // put an amount below A's dust amount but above B's. + payment_hash: PaymentHash(Sha256::hash(&[43; 32]).into_inner()), + cltv_expiry: 200000000, + state: OutboundHTLCState::Committed, + source: HTLCSource::OutboundRoute { + path: Vec::new(), + session_priv: SecretKey::from_slice(&hex::decode("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap(), + first_hop_htlc_msat: 548, + } + }); + + // Make sure when Node A calculates their local commitment transaction, none of the HTLCs pass + // the dust limit check. + let htlc_candidate = HTLCCandidate::new(htlc_amount_msat, HTLCInitiator::LocalOffered); + let local_commit_tx_fee = node_a_chan.next_local_commit_tx_fee_msat(htlc_candidate, None); + let local_commit_fee_0_htlcs = node_a_chan.commit_tx_fee_msat(0); + assert_eq!(local_commit_tx_fee, local_commit_fee_0_htlcs); + + // Finally, make sure that when Node A calculates the remote's commitment transaction fees, all + // of the HTLCs are seen to be above the dust limit. + node_a_chan.channel_transaction_parameters.is_outbound_from_holder = false; + let remote_commit_fee_3_htlcs = node_a_chan.commit_tx_fee_msat(3); + let htlc_candidate = HTLCCandidate::new(htlc_amount_msat, HTLCInitiator::LocalOffered); + let remote_commit_tx_fee = node_a_chan.next_remote_commit_tx_fee_msat(htlc_candidate, None); + assert_eq!(remote_commit_tx_fee, remote_commit_fee_3_htlcs); + } + + #[test] + fn test_timeout_vs_success_htlc_dust_limit() { + // Make sure that when `next_remote_commit_tx_fee_msat` and `next_local_commit_tx_fee_msat` + // calculate the real dust limits for HTLCs (i.e. the dust limit given by the counterparty + // *plus* the fees paid for the HTLC) they don't swap `HTLC_SUCCESS_TX_WEIGHT` for + // `HTLC_TIMEOUT_TX_WEIGHT`, and vice versa. + let fee_est = TestFeeEstimator{fee_est: 253 }; + let secp_ctx = Secp256k1::new(); + let seed = [42; 32]; + let network = Network::Testnet; + let keys_provider = test_utils::TestKeysInterface::new(&seed, network); + + let node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); + let config = UserConfig::default(); + let mut chan = Channel::::new_outbound(&&fee_est, &&keys_provider, node_id, 10000000, 100000, 42, &config).unwrap(); + + let commitment_tx_fee_0_htlcs = chan.commit_tx_fee_msat(0); + let commitment_tx_fee_1_htlc = chan.commit_tx_fee_msat(1); + + // If HTLC_SUCCESS_TX_WEIGHT and HTLC_TIMEOUT_TX_WEIGHT were swapped: then this HTLC would be + // counted as dust when it shouldn't be. + let htlc_amt_above_timeout = ((253 * HTLC_TIMEOUT_TX_WEIGHT / 1000) + chan.holder_dust_limit_satoshis + 1) * 1000; + let htlc_candidate = HTLCCandidate::new(htlc_amt_above_timeout, HTLCInitiator::LocalOffered); + let commitment_tx_fee = chan.next_local_commit_tx_fee_msat(htlc_candidate, None); + assert_eq!(commitment_tx_fee, commitment_tx_fee_1_htlc); + + // If swapped: this HTLC would be counted as non-dust when it shouldn't be. + let dust_htlc_amt_below_success = ((253 * HTLC_SUCCESS_TX_WEIGHT / 1000) + chan.holder_dust_limit_satoshis - 1) * 1000; + let htlc_candidate = HTLCCandidate::new(dust_htlc_amt_below_success, HTLCInitiator::RemoteOffered); + let commitment_tx_fee = chan.next_local_commit_tx_fee_msat(htlc_candidate, None); + assert_eq!(commitment_tx_fee, commitment_tx_fee_0_htlcs); + + chan.channel_transaction_parameters.is_outbound_from_holder = false; + + // If swapped: this HTLC would be counted as non-dust when it shouldn't be. + let dust_htlc_amt_above_timeout = ((253 * HTLC_TIMEOUT_TX_WEIGHT / 1000) + chan.counterparty_dust_limit_satoshis + 1) * 1000; + let htlc_candidate = HTLCCandidate::new(dust_htlc_amt_above_timeout, HTLCInitiator::LocalOffered); + let commitment_tx_fee = chan.next_remote_commit_tx_fee_msat(htlc_candidate, None); + assert_eq!(commitment_tx_fee, commitment_tx_fee_0_htlcs); + + // If swapped: this HTLC would be counted as dust when it shouldn't be. + let htlc_amt_below_success = ((253 * HTLC_SUCCESS_TX_WEIGHT / 1000) + chan.counterparty_dust_limit_satoshis - 1) * 1000; + let htlc_candidate = HTLCCandidate::new(htlc_amt_below_success, HTLCInitiator::RemoteOffered); + let commitment_tx_fee = chan.next_remote_commit_tx_fee_msat(htlc_candidate, None); + assert_eq!(commitment_tx_fee, commitment_tx_fee_1_htlc); + } + #[test] fn channel_reestablish_no_updates() { let feeest = TestFeeEstimator{fee_est: 15000}; @@ -4582,6 +5037,8 @@ mod tests { let secp_ctx = Secp256k1::new(); let seed = [42; 32]; let network = Network::Testnet; + 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. @@ -4589,12 +5046,12 @@ mod tests { // Create Node A's channel pointing to Node B's pubkey let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); let config = UserConfig::default(); - let mut node_a_chan = Channel::::new_outbound(&&feeest, &&keys_provider, node_b_node_id, 10000000, 100000, 42, &config).unwrap(); + let mut node_a_chan = Channel::::new_outbound(&&feeest, &&keys_provider, node_b_node_id, 10000000, 100000, 42, &config).unwrap(); // Create Node B's channel by receiving Node A's open_channel message - let open_channel_msg = node_a_chan.get_open_channel(genesis_block(network).header.block_hash()); + let open_channel_msg = node_a_chan.get_open_channel(chain_hash); let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap()); - let mut node_b_chan = Channel::::new_from_req(&&feeest, &&keys_provider, node_b_node_id, InitFeatures::known(), &open_channel_msg, 7, &config).unwrap(); + let mut node_b_chan = Channel::::new_from_req(&&feeest, &&keys_provider, node_b_node_id, InitFeatures::known(), &open_channel_msg, 7, &config).unwrap(); // Node B --> Node A: accept channel let accept_channel_msg = node_b_chan.get_accept_channel(); @@ -4606,11 +5063,11 @@ mod tests { 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, &&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, &&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. @@ -4639,6 +5096,54 @@ mod tests { } } + #[test] + fn channel_update() { + let feeest = TestFeeEstimator{fee_est: 15000}; + let secp_ctx = Secp256k1::new(); + let seed = [42; 32]; + let network = Network::Testnet; + let chain_hash = genesis_block(network).header.block_hash(); + let keys_provider = test_utils::TestKeysInterface::new(&seed, network); + + // Create a channel. + let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); + let config = UserConfig::default(); + let mut node_a_chan = Channel::::new_outbound(&&feeest, &&keys_provider, node_b_node_id, 10000000, 100000, 42, &config).unwrap(); + assert!(node_a_chan.counterparty_forwarding_info.is_none()); + assert_eq!(node_a_chan.holder_htlc_minimum_msat, 1); // the default + assert!(node_a_chan.counterparty_forwarding_info().is_none()); + + // Make sure that receiving a channel update will update the Channel as expected. + let update = ChannelUpdate { + contents: UnsignedChannelUpdate { + chain_hash, + short_channel_id: 0, + timestamp: 0, + flags: 0, + cltv_expiry_delta: 100, + htlc_minimum_msat: 5, + htlc_maximum_msat: OptionalField::Absent, + fee_base_msat: 110, + fee_proportional_millionths: 11, + excess_data: Vec::new(), + }, + signature: Signature::from(unsafe { FFISignature::new() }) + }; + node_a_chan.channel_update(&update).unwrap(); + + // The counterparty can send an update with a higher minimum HTLC, but that shouldn't + // change our official htlc_minimum_msat. + assert_eq!(node_a_chan.holder_htlc_minimum_msat, 1); + match node_a_chan.counterparty_forwarding_info() { + Some(info) => { + assert_eq!(info.cltv_expiry_delta, 100); + assert_eq!(info.fee_base_msat, 110); + assert_eq!(info.fee_proportional_millionths, 11); + }, + None => panic!("expected counterparty forwarding info to be Some") + } + } + #[test] fn outbound_commitment_test() { // Test vectors from BOLT 3 Appendix C: @@ -4646,7 +5151,7 @@ mod tests { let logger : Arc = Arc::new(test_utils::TestLogger::new()); let secp_ctx = Secp256k1::new(); - let mut chan_keys = InMemoryChannelKeys::new( + let mut signer = InMemorySigner::new( &secp_ctx, SecretKey::from_slice(&hex::decode("30ff4956bbdd3222d44cc5e8a1261dab1e07957bdac5ae88fe3261ef321f3749").unwrap()[..]).unwrap(), SecretKey::from_slice(&hex::decode("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap(), @@ -4660,14 +5165,14 @@ mod tests { [0; 32] ); - assert_eq!(chan_keys.pubkeys().funding_pubkey.serialize()[..], + assert_eq!(signer.pubkeys().funding_pubkey.serialize()[..], hex::decode("023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb").unwrap()[..]); - let keys_provider = Keys { chan_keys: chan_keys.clone() }; + let keys_provider = Keys { signer: signer.clone() }; let counterparty_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); let mut config = UserConfig::default(); config.channel_options.announced_channel = false; - let mut chan = Channel::::new_outbound(&&feeest, &&keys_provider, counterparty_node_id, 10_000_000, 100000, 42, &config).unwrap(); // Nothing uses their network key in this test + let mut chan = Channel::::new_outbound(&&feeest, &&keys_provider, counterparty_node_id, 10_000_000, 100000, 42, &config).unwrap(); // Nothing uses their network key in this test chan.holder_dust_limit_satoshis = 546; let funding_info = OutPoint{ txid: Txid::from_hex("8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be").unwrap(), index: 0 }; @@ -4685,7 +5190,7 @@ mod tests { selected_contest_delay: 144 }); chan.channel_transaction_parameters.funding_outpoint = Some(funding_info); - chan_keys.ready_channel(&chan.channel_transaction_parameters); + signer.ready_channel(&chan.channel_transaction_parameters); assert_eq!(counterparty_pubkeys.payment_point.serialize()[..], hex::decode("032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991").unwrap()[..]); @@ -4699,10 +5204,10 @@ mod tests { // We can't just use build_holder_transaction_keys here as the per_commitment_secret is not // derived from a commitment_seed, so instead we copy it here and call // build_commitment_transaction. - let delayed_payment_base = &chan.holder_keys.pubkeys().delayed_payment_basepoint; + let delayed_payment_base = &chan.holder_signer.pubkeys().delayed_payment_basepoint; let per_commitment_secret = SecretKey::from_slice(&hex::decode("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100").unwrap()[..]).unwrap(); let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret); - let htlc_basepoint = &chan.holder_keys.pubkeys().htlc_basepoint; + let htlc_basepoint = &chan.holder_signer.pubkeys().htlc_basepoint; let keys = TxCreationKeys::derive_new(&secp_ctx, &per_commitment_point, delayed_payment_base, htlc_basepoint, &counterparty_pubkeys.revocation_basepoint, &counterparty_pubkeys.htlc_basepoint).unwrap(); macro_rules! test_commitment { @@ -4739,10 +5244,10 @@ mod tests { commitment_tx.clone(), counterparty_signature, counterparty_htlc_sigs, - &chan.holder_keys.pubkeys().funding_pubkey, + &chan.holder_signer.pubkeys().funding_pubkey, chan.counterparty_funding_pubkey() ); - let (holder_sig, htlc_sigs) = chan_keys.sign_holder_commitment_and_htlcs(&holder_commitment_tx, &secp_ctx).unwrap(); + let (holder_sig, htlc_sigs) = signer.sign_holder_commitment_and_htlcs(&holder_commitment_tx, &secp_ctx).unwrap(); assert_eq!(Signature::from_der(&hex::decode($sig_hex).unwrap()[..]).unwrap(), holder_sig, "holder_sig"); let funding_redeemscript = chan.get_funding_redeemscript();