// licenses.
use bitcoin::blockdata::script::{Script,Builder};
-use bitcoin::blockdata::transaction::{Transaction, SigHashType};
-use bitcoin::util::bip143;
+use bitcoin::blockdata::transaction::{Transaction, EcdsaSighashType};
+use bitcoin::util::sighash;
use bitcoin::consensus::encode;
use bitcoin::hashes::Hash;
use bitcoin::hash_types::{Txid, BlockHash};
use bitcoin::secp256k1::constants::PUBLIC_KEY_SIZE;
-use bitcoin::secp256k1::key::{PublicKey,SecretKey};
-use bitcoin::secp256k1::{Secp256k1,Signature};
+use bitcoin::secp256k1::{PublicKey,SecretKey};
+use bitcoin::secp256k1::{Secp256k1,ecdsa::Signature};
use bitcoin::secp256k1;
use ln::{PaymentPreimage, PaymentHash};
use util::ser::{Readable, ReadableArgs, Writeable, Writer, VecWriter};
use util::logger::Logger;
use util::errors::APIError;
-use util::config::{UserConfig,ChannelConfig};
+use util::config::{UserConfig, ChannelConfig, ChannelHandshakeConfig, ChannelHandshakeLimits};
use util::scid_utils::scid_from_parts;
use io;
use prelude::*;
use core::{cmp,mem,fmt};
use core::ops::Deref;
-#[cfg(any(test, feature = "fuzztarget", debug_assertions))]
+#[cfg(any(test, fuzzing, debug_assertions))]
use sync::Mutex;
use bitcoin::hashes::hex::ToHex;
pub counterparty_dust_limit_msat: u64,
}
+pub struct AvailableBalances {
+ /// The amount that would go to us if we close the channel, ignoring any on-chain fees.
+ pub balance_msat: u64,
+ /// Total amount available for our counterparty to send to us.
+ pub inbound_capacity_msat: u64,
+ /// Total amount available for us to send to our counterparty.
+ pub outbound_capacity_msat: u64,
+ /// The maximum value we can assign to the next outbound HTLC
+ pub next_outbound_htlc_limit_msat: u64,
+}
+
#[derive(Debug, Clone, Copy, PartialEq)]
enum FeeUpdateState {
// Inbound states mirroring InboundHTLCState
Committed,
/// Remote removed this (outbound) HTLC. We're waiting on their commitment_signed to finalize
/// the change (though they'll need to revoke before we fail the payment).
- RemoteRemoved(Option<HTLCFailReason>),
+ RemoteRemoved(OutboundHTLCOutcome),
/// Remote removed this and sent a commitment_signed (implying we've revoke_and_ack'ed it), but
/// the remote side hasn't yet revoked their previous state, which we need them to do before we
/// can do any backwards failing. Implies AwaitingRemoteRevoke.
/// We also have not yet removed this HTLC in a commitment_signed message, and are waiting on a
/// remote revoke_and_ack on a previous state before we can do so.
- AwaitingRemoteRevokeToRemove(Option<HTLCFailReason>),
+ AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome),
/// Remote removed this and sent a commitment_signed (implying we've revoke_and_ack'ed it), but
/// the remote side hasn't yet revoked their previous state, which we need them to do before we
/// can do any backwards failing. Implies AwaitingRemoteRevoke.
/// We have removed this HTLC in our latest commitment_signed and are now just waiting on a
/// revoke_and_ack to drop completely.
- AwaitingRemovedRemoteRevoke(Option<HTLCFailReason>),
+ AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome),
+}
+
+#[derive(Clone)]
+enum OutboundHTLCOutcome {
+ Success(Option<PaymentPreimage>),
+ Failure(HTLCFailReason),
+}
+
+impl From<Option<HTLCFailReason>> for OutboundHTLCOutcome {
+ fn from(o: Option<HTLCFailReason>) -> Self {
+ match o {
+ None => OutboundHTLCOutcome::Success(None),
+ Some(r) => OutboundHTLCOutcome::Failure(r)
+ }
+ }
+}
+
+impl<'a> Into<Option<&'a HTLCFailReason>> for &'a OutboundHTLCOutcome {
+ fn into(self) -> Option<&'a HTLCFailReason> {
+ match self {
+ OutboundHTLCOutcome::Success(_) => None,
+ OutboundHTLCOutcome::Failure(ref r) => Some(r)
+ }
+ }
}
struct OutboundHTLCOutput {
Disabled,
}
+/// We track when we sent an `AnnouncementSignatures` to our peer in a few states, described here.
+#[derive(PartialEq)]
+pub enum AnnouncementSigsState {
+ /// We have not sent our peer an `AnnouncementSignatures` yet, or our peer disconnected since
+ /// we sent the last `AnnouncementSignatures`.
+ NotSent,
+ /// We sent an `AnnouncementSignatures` to our peer since the last time our peer disconnected.
+ /// This state never appears on disk - instead we write `NotSent`.
+ MessageSent,
+ /// We sent a `CommitmentSigned` after the last `AnnouncementSignatures` we sent. Because we
+ /// only ever have a single `CommitmentSigned` pending at once, if we sent one after sending
+ /// `AnnouncementSignatures` then we know the peer received our `AnnouncementSignatures` if
+ /// they send back a `RevokeAndACK`.
+ /// This state never appears on disk - instead we write `NotSent`.
+ Committed,
+ /// We received a `RevokeAndACK`, effectively ack-ing our `AnnouncementSignatures`, at this
+ /// point we no longer need to re-send our `AnnouncementSignatures` again on reconnect.
+ PeerReceived,
+}
+
/// An enum indicating whether the local or remote side offered a given HTLC.
enum HTLCInitiator {
LocalOffered,
htlcs_included: Vec<(HTLCOutputInCommitment, Option<&'a HTLCSource>)>, // the list of HTLCs (dust HTLCs *included*) which were not ignored when building the transaction
local_balance_msat: u64, // local balance before fees but considering dust limits
remote_balance_msat: u64, // remote balance before fees but considering dust limits
+ preimages: Vec<PaymentPreimage>, // preimages for successful offered HTLCs since last commitment
}
/// Used when calculating whether we or the remote can afford an additional HTLC.
pub finalized_claimed_htlcs: Vec<HTLCSource>,
pub funding_broadcastable: Option<Transaction>,
pub funding_locked: Option<msgs::FundingLocked>,
+ pub announcement_sigs: Option<msgs::AnnouncementSignatures>,
+}
+
+/// The return value of `channel_reestablish`
+pub(super) struct ReestablishResponses {
+ pub funding_locked: Option<msgs::FundingLocked>,
+ pub raa: Option<msgs::RevokeAndACK>,
+ pub commitment_update: Option<msgs::CommitmentUpdate>,
+ pub order: RAACommitmentOrder,
+ pub mon_update: Option<ChannelMonitorUpdate>,
+ pub holding_cell_failed_htlcs: Vec<(HTLCSource, PaymentHash)>,
+ pub announcement_sigs: Option<msgs::AnnouncementSignatures>,
+ pub shutdown_msg: Option<msgs::Shutdown>,
}
/// If the majority of the channels funds are to the fundee and the initiator holds only just
#[cfg(not(any(test, feature = "_test_utils")))]
config: ChannelConfig,
+ inbound_handshake_limits_override: Option<ChannelHandshakeLimits>,
+
user_id: u64,
channel_id: [u8; 32],
channel_state: u32,
+
+ // When we reach max(6 blocks, minimum_depth), we need to send an AnnouncementSigs message to
+ // our peer. However, we want to make sure they received it, or else rebroadcast it when we
+ // next connect.
+ // We do so here, see `AnnouncementSigsSent` for more details on the state(s).
+ // Note that a number of our tests were written prior to the behavior here which retransmits
+ // AnnouncementSignatures until after an RAA completes, so the behavior is short-circuited in
+ // many tests.
+ #[cfg(any(test, feature = "_test_utils"))]
+ pub(crate) announcement_sigs_state: AnnouncementSigsState,
+ #[cfg(not(any(test, feature = "_test_utils")))]
+ announcement_sigs_state: AnnouncementSigsState,
+
secp_ctx: Secp256k1<secp256k1::All>,
channel_value_satoshis: u64,
#[cfg(not(test))]
closing_fee_limits: Option<(u64, u64)>,
+ /// Flag that ensures that `accept_inbound_channel` must be called before `funding_created`
+ /// is executed successfully. The reason for this flag is that when the
+ /// `UserConfig::manually_accept_inbound_channels` config flag is set to true, inbound channels
+ /// are required to be manually accepted by the node operator before the `msgs::AcceptChannel`
+ /// message is created and sent out. During the manual accept process, `accept_inbound_channel`
+ /// is called by `ChannelManager::accept_inbound_channel`.
+ ///
+ /// The flag counteracts that a counterparty node could theoretically send a
+ /// `msgs::FundingCreated` message before the node operator has manually accepted an inbound
+ /// channel request made by the counterparty node. That would execute `funding_created` before
+ /// `accept_inbound_channel`, and `funding_created` should therefore not execute successfully.
+ inbound_awaiting_accept: bool,
+
/// The hash of the block in which the funding transaction was included.
funding_tx_confirmed_in: Option<BlockHash>,
funding_tx_confirmation_height: u32,
// `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"))]
+ #[cfg(any(test, fuzzing))]
next_local_commitment_tx_fee_info_cached: Mutex<Option<CommitmentTxInfoCached>>,
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
next_remote_commitment_tx_fee_info_cached: Mutex<Option<CommitmentTxInfoCached>>,
/// lnd has a long-standing bug where, upon reconnection, if the channel is not yet confirmed
/// See-also <https://github.com/lightningnetwork/lnd/issues/4006>
pub workaround_lnd_bug_4006: Option<msgs::FundingLocked>,
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
// When we receive an HTLC fulfill on an outbound path, we may immediately fulfill the
// corresponding HTLC on the inbound path. If, then, the outbound path channel is
// disconnected and reconnected (before we've exchange commitment_signed and revoke_and_ack
/// This channel's type, as negotiated during channel open
channel_type: ChannelTypeFeatures,
+
+ // Our counterparty can offer us SCID aliases which they will map to this channel when routing
+ // outbound payments. These can be used in invoice route hints to avoid explicitly revealing
+ // the channel's funding UTXO.
+ // We only bother storing the most recent SCID alias at any time, though our counterparty has
+ // to store all of them.
+ latest_inbound_scid_alias: Option<u64>,
+
+ // We always offer our counterparty a static SCID alias, which we recognize as for this channel
+ // if we see it in HTLC forwarding instructions. We don't bother rotating the alias given we
+ // don't currently support node id aliases and eventually privacy should be provided with
+ // blinded paths instead of simple scid+node_id aliases.
+ outbound_scid_alias: u64,
}
-#[cfg(any(test, feature = "fuzztarget"))]
+#[cfg(any(test, fuzzing))]
struct CommitmentTxInfoCached {
fee: u64,
total_pending_htlcs: usize,
pub const ANCHOR_OUTPUT_VALUE_SATOSHI: u64 = 330;
-/// Maximum `funding_satoshis` value, according to the BOLT #2 specification
-/// it's 2^24.
-pub const MAX_FUNDING_SATOSHIS: u64 = 1 << 24;
+/// The percentage of the channel value `holder_max_htlc_value_in_flight_msat` used to be set to,
+/// before this was made configurable. The percentage was made configurable in LDK 0.0.107,
+/// although LDK 0.0.104+ enabled serialization of channels with a different value set for
+/// `holder_max_htlc_value_in_flight_msat`.
+pub const MAX_IN_FLIGHT_PERCENT_LEGACY: u8 = 10;
+
+/// Maximum `funding_satoshis` value according to the BOLT #2 specification, if
+/// `option_support_large_channel` (aka wumbo channels) is not supported.
+/// It's 2^24 - 1.
+pub const MAX_FUNDING_SATOSHIS_NO_WUMBO: u64 = (1 << 24) - 1;
+
+/// Total bitcoin supply in satoshis.
+pub const TOTAL_BITCOIN_SUPPLY_SATOSHIS: u64 = 21_000_000 * 1_0000_0000;
/// The maximum network dust limit for standard script formats. This currently represents the
/// minimum output value for a P2SH output before Bitcoin Core 22 considers the entire
}
impl<Signer: Sign> Channel<Signer> {
- // 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
+ /// Returns the value to use for `holder_max_htlc_value_in_flight_msat` as a percentage of the
+ /// `channel_value_satoshis` in msat, set through
+ /// [`ChannelHandshakeConfig::max_inbound_htlc_value_in_flight_percent_of_channel`]
+ ///
+ /// The effective percentage is lower bounded by 1% and upper bounded by 100%.
+ ///
+ /// [`ChannelHandshakeConfig::max_inbound_htlc_value_in_flight_percent_of_channel`]: crate::util::config::ChannelHandshakeConfig::max_inbound_htlc_value_in_flight_percent_of_channel
+ fn get_holder_max_htlc_value_in_flight_msat(channel_value_satoshis: u64, config: &ChannelHandshakeConfig) -> u64 {
+ let configured_percent = if config.max_inbound_htlc_value_in_flight_percent_of_channel < 1 {
+ 1
+ } else if config.max_inbound_htlc_value_in_flight_percent_of_channel > 100 {
+ 100
+ } else {
+ config.max_inbound_htlc_value_in_flight_percent_of_channel as u64
+ };
+ channel_value_satoshis * 10 * configured_percent
}
/// Returns a minimum channel reserve value the remote needs to maintain,
self.channel_transaction_parameters.opt_anchors.is_some()
}
+ fn get_initial_channel_type(config: &UserConfig) -> ChannelTypeFeatures {
+ // The default channel type (ie the first one we try) depends on whether the channel is
+ // public - if it is, we just go with `only_static_remotekey` as it's the only option
+ // available. If it's private, we first try `scid_privacy` as it provides better privacy
+ // with no other changes, and fall back to `only_static_remotekey`
+ let mut ret = ChannelTypeFeatures::only_static_remote_key();
+ if !config.channel_options.announced_channel && config.own_channel_config.negotiate_scid_privacy {
+ ret.set_scid_privacy_required();
+ }
+ ret
+ }
+
+ /// If we receive an error message, it may only be a rejection of the channel type we tried,
+ /// not of our ability to open any channel at all. Thus, on error, we should first call this
+ /// and see if we get a new `OpenChannel` message, otherwise the channel is failed.
+ pub(crate) fn maybe_handle_error_without_close(&mut self, chain_hash: BlockHash) -> Result<msgs::OpenChannel, ()> {
+ if !self.is_outbound() || self.channel_state != ChannelState::OurInitSent as u32 { return Err(()); }
+ if self.channel_type == ChannelTypeFeatures::only_static_remote_key() {
+ // We've exhausted our options
+ return Err(());
+ }
+ self.channel_type = ChannelTypeFeatures::only_static_remote_key(); // We only currently support two types
+ Ok(self.get_open_channel(chain_hash))
+ }
+
// Constructors:
pub fn new_outbound<K: Deref, F: Deref>(
fee_estimator: &F, keys_provider: &K, counterparty_node_id: PublicKey, their_features: &InitFeatures,
- channel_value_satoshis: u64, push_msat: u64, user_id: u64, config: &UserConfig, current_chain_height: u32
+ channel_value_satoshis: u64, push_msat: u64, user_id: u64, config: &UserConfig, current_chain_height: u32,
+ outbound_scid_alias: u64
) -> Result<Channel<Signer>, APIError>
where K::Target: KeysInterface<Signer = Signer>,
F::Target: FeeEstimator,
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)});
+ if !their_features.supports_wumbo() && channel_value_satoshis > MAX_FUNDING_SATOSHIS_NO_WUMBO {
+ return Err(APIError::APIMisuseError{err: format!("funding_value must not exceed {}, it was {}", MAX_FUNDING_SATOSHIS_NO_WUMBO, channel_value_satoshis)});
+ }
+ if channel_value_satoshis >= TOTAL_BITCOIN_SUPPLY_SATOSHIS {
+ return Err(APIError::APIMisuseError{err: format!("funding_value must be smaller than the total bitcoin supply, it was {}", channel_value_satoshis)});
}
let channel_value_msat = channel_value_satoshis * 1000;
if push_msat > channel_value_msat {
Ok(Channel {
user_id,
config: config.channel_options.clone(),
+ inbound_handshake_limits_override: Some(config.peer_channel_config_limits.clone()),
channel_id: keys_provider.get_secure_random_bytes(),
channel_state: ChannelState::OurInitSent as u32,
+ announcement_sigs_state: AnnouncementSigsState::NotSent,
secp_ctx,
channel_value_satoshis,
closing_fee_limits: None,
target_closing_feerate_sats_per_kw: None,
+ inbound_awaiting_accept: false,
+
funding_tx_confirmed_in: None,
funding_tx_confirmation_height: 0,
short_channel_id: None,
counterparty_dust_limit_satoshis: 0,
holder_dust_limit_satoshis: MIN_CHAN_DUST_LIMIT_SATOSHIS,
counterparty_max_htlc_value_in_flight_msat: 0,
- holder_max_htlc_value_in_flight_msat: Self::get_holder_max_htlc_value_in_flight_msat(channel_value_satoshis),
+ holder_max_htlc_value_in_flight_msat: Self::get_holder_max_htlc_value_in_flight_msat(channel_value_satoshis, &config.own_channel_config),
counterparty_selected_channel_reserve_satoshis: None, // Filled in in accept_channel
holder_selected_channel_reserve_satoshis,
counterparty_htlc_minimum_msat: 0,
announcement_sigs: None,
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
next_local_commitment_tx_fee_info_cached: Mutex::new(None),
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
next_remote_commitment_tx_fee_info_cached: Mutex::new(None),
workaround_lnd_bug_4006: None,
- #[cfg(any(test, feature = "fuzztarget"))]
+ latest_inbound_scid_alias: None,
+ outbound_scid_alias,
+
+ #[cfg(any(test, fuzzing))]
historical_inbound_htlc_fulfills: HashSet::new(),
- // We currently only actually support one channel type, so don't retry with new types
- // on error messages. When we support more we'll need fallback support (assuming we
- // want to support old types).
- channel_type: ChannelTypeFeatures::only_static_remote_key(),
+ channel_type: Self::get_initial_channel_type(&config),
})
}
fn check_remote_fee<F: Deref>(fee_estimator: &F, feerate_per_kw: u32) -> Result<(), ChannelError>
where F::Target: FeeEstimator
{
- let lower_limit = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background);
- // Some fee estimators round up to the next full sat/vbyte (ie 250 sats per kw), causing
- // occasional issues with feerate disagreements between an initiator that wants a feerate
- // of 1.1 sat/vbyte and a receiver that wants 1.1 rounded up to 2. Thus, we always add 250
- // sat/kw before the comparison here.
- if feerate_per_kw + 250 < lower_limit {
- return Err(ChannelError::Close(format!("Peer's feerate much too low. Actual: {}. Our expected lower limit: {} (- 250)", feerate_per_kw, lower_limit)));
- }
// We only bound the fee updates on the upper side to prevent completely absurd feerates,
// always accepting up to 25 sat/vByte or 10x our fee estimator's "High Priority" fee.
// We generally don't care too much if they set the feerate to something very high, but it
if feerate_per_kw as u64 > upper_limit {
return Err(ChannelError::Close(format!("Peer's feerate much too high. Actual: {}. Our expected upper limit: {}", feerate_per_kw, upper_limit)));
}
+ let lower_limit = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background);
+ // Some fee estimators round up to the next full sat/vbyte (ie 250 sats per kw), causing
+ // occasional issues with feerate disagreements between an initiator that wants a feerate
+ // of 1.1 sat/vbyte and a receiver that wants 1.1 rounded up to 2. Thus, we always add 250
+ // sat/kw before the comparison here.
+ if feerate_per_kw + 250 < lower_limit {
+ return Err(ChannelError::Close(format!("Peer's feerate much too low. Actual: {}. Our expected lower limit: {} (- 250)", feerate_per_kw, lower_limit)));
+ }
Ok(())
}
/// Assumes chain_hash has already been checked and corresponds with what we expect!
pub fn new_from_req<K: Deref, F: Deref, L: Deref>(
fee_estimator: &F, keys_provider: &K, counterparty_node_id: PublicKey, their_features: &InitFeatures,
- msg: &msgs::OpenChannel, user_id: u64, config: &UserConfig, current_chain_height: u32, logger: &L
+ msg: &msgs::OpenChannel, user_id: u64, config: &UserConfig, current_chain_height: u32, logger: &L,
+ outbound_scid_alias: u64
) -> Result<Channel<Signer>, ChannelError>
where K::Target: KeysInterface<Signer = Signer>,
F::Target: FeeEstimator,
L::Target: Logger,
{
let opt_anchors = false; // TODO - should be based on features
+ let announced_channel = if (msg.channel_flags & 1) == 1 { true } else { false };
// First check the channel type is known, failing before we do anything else if we don't
// support this channel type.
if channel_type.supports_any_optional_bits() {
return Err(ChannelError::Close("Channel Type field contained optional bits - this is not allowed".to_owned()));
}
- if *channel_type != ChannelTypeFeatures::only_static_remote_key() {
- return Err(ChannelError::Close("Channel Type was not understood".to_owned()));
+ // We currently only allow two channel types, so write it all out here - we allow
+ // `only_static_remote_key` in all contexts, and further allow
+ // `static_remote_key|scid_privacy` if the channel is not publicly announced.
+ let mut allowed_type = ChannelTypeFeatures::only_static_remote_key();
+ if *channel_type != allowed_type {
+ allowed_type.set_scid_privacy_required();
+ if *channel_type != allowed_type {
+ return Err(ChannelError::Close("Channel Type was not understood".to_owned()));
+ }
+ if announced_channel {
+ return Err(ChannelError::Close("SCID Alias/Privacy Channel Type cannot be set on a public channel".to_owned()));
+ }
}
channel_type.clone()
} else {
}
// Check sanity of message fields:
- if msg.funding_satoshis >= MAX_FUNDING_SATOSHIS {
- return Err(ChannelError::Close(format!("Funding must be smaller than {}. It was {}", MAX_FUNDING_SATOSHIS, msg.funding_satoshis)));
+ if msg.funding_satoshis > config.peer_channel_config_limits.max_funding_satoshis {
+ return Err(ChannelError::Close(format!("Per our config, funding must be at most {}. It was {}", config.peer_channel_config_limits.max_funding_satoshis, msg.funding_satoshis)));
+ }
+ if msg.funding_satoshis >= TOTAL_BITCOIN_SUPPLY_SATOSHIS {
+ return Err(ChannelError::Close(format!("Funding must be smaller than the total bitcoin supply. It was {}", msg.funding_satoshis)));
}
if msg.channel_reserve_satoshis > msg.funding_satoshis {
return Err(ChannelError::Close(format!("Bogus channel_reserve_satoshis ({}). Must be not greater than funding_satoshis: {}", msg.channel_reserve_satoshis, msg.funding_satoshis)));
}
- let funding_value = (msg.funding_satoshis - msg.channel_reserve_satoshis) * 1000;
- if msg.push_msat > funding_value {
- return Err(ChannelError::Close(format!("push_msat {} was larger than funding value {}", msg.push_msat, funding_value)));
+ let full_channel_value_msat = (msg.funding_satoshis - msg.channel_reserve_satoshis) * 1000;
+ if msg.push_msat > full_channel_value_msat {
+ return Err(ChannelError::Close(format!("push_msat {} was larger than channel amount minus reserve ({})", msg.push_msat, full_channel_value_msat)));
}
if msg.dust_limit_satoshis > msg.funding_satoshis {
return Err(ChannelError::Close(format!("dust_limit_satoshis {} was larger than funding_satoshis {}. Peer never wants payout outputs?", msg.dust_limit_satoshis, msg.funding_satoshis)));
}
- let full_channel_value_msat = (msg.funding_satoshis - msg.channel_reserve_satoshis) * 1000;
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)));
}
// Convert things into internal flags and prep our state:
- let announce = if (msg.channel_flags & 1) == 1 { true } else { false };
if config.peer_channel_config_limits.force_announced_channel_preference {
- if local_config.announced_channel != announce {
+ if local_config.announced_channel != announced_channel {
return Err(ChannelError::Close("Peer tried to open channel but their announcement preference is different from ours".to_owned()));
}
}
// we either accept their preference or the preferences match
- local_config.announced_channel = announce;
+ local_config.announced_channel = announced_channel;
let holder_selected_channel_reserve_satoshis = Channel::<Signer>::get_holder_selected_channel_reserve_satoshis(msg.funding_satoshis);
if holder_selected_channel_reserve_satoshis < MIN_CHAN_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, MIN_CHAN_DUST_LIMIT_SATOSHIS)));
}
+ if holder_selected_channel_reserve_satoshis * 1000 >= full_channel_value_msat {
+ return Err(ChannelError::Close(format!("Suitable channel reserve not found. remote_channel_reserve was ({}). Channel value is ({} - {}).", holder_selected_channel_reserve_satoshis, full_channel_value_msat, msg.push_msat)));
+ }
if msg.channel_reserve_satoshis < MIN_CHAN_DUST_LIMIT_SATOSHIS {
log_debug!(logger, "channel_reserve_satoshis ({}) is smaller than our dust limit ({}). We can broadcast stale states without any risk, implying this channel is very insecure for our counterparty.",
msg.channel_reserve_satoshis, MIN_CHAN_DUST_LIMIT_SATOSHIS);
let chan = Channel {
user_id,
config: local_config,
+ inbound_handshake_limits_override: None,
channel_id: msg.temporary_channel_id,
channel_state: (ChannelState::OurInitSent as u32) | (ChannelState::TheirInitSent as u32),
+ announcement_sigs_state: AnnouncementSigsState::NotSent,
secp_ctx,
latest_monitor_update_id: 0,
closing_fee_limits: None,
target_closing_feerate_sats_per_kw: None,
+ inbound_awaiting_accept: true,
+
funding_tx_confirmed_in: None,
funding_tx_confirmation_height: 0,
short_channel_id: None,
counterparty_dust_limit_satoshis: msg.dust_limit_satoshis,
holder_dust_limit_satoshis: MIN_CHAN_DUST_LIMIT_SATOSHIS,
counterparty_max_htlc_value_in_flight_msat: cmp::min(msg.max_htlc_value_in_flight_msat, msg.funding_satoshis * 1000),
- holder_max_htlc_value_in_flight_msat: Self::get_holder_max_htlc_value_in_flight_msat(msg.funding_satoshis),
+ holder_max_htlc_value_in_flight_msat: Self::get_holder_max_htlc_value_in_flight_msat(msg.funding_satoshis, &config.own_channel_config),
counterparty_selected_channel_reserve_satoshis: Some(msg.channel_reserve_satoshis),
holder_selected_channel_reserve_satoshis,
counterparty_htlc_minimum_msat: msg.htlc_minimum_msat,
announcement_sigs: None,
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
next_local_commitment_tx_fee_info_cached: Mutex::new(None),
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
next_remote_commitment_tx_fee_info_cached: Mutex::new(None),
workaround_lnd_bug_4006: None,
- #[cfg(any(test, feature = "fuzztarget"))]
+ latest_inbound_scid_alias: None,
+ outbound_scid_alias,
+
+ #[cfg(any(test, fuzzing))]
historical_inbound_htlc_fulfills: HashSet::new(),
channel_type,
}
}
+ let mut preimages: Vec<PaymentPreimage> = Vec::new();
+
for ref htlc in self.pending_outbound_htlcs.iter() {
let (include, state_name) = match htlc.state {
OutboundHTLCState::LocalAnnounced(_) => (generated_by_local, "LocalAnnounced"),
OutboundHTLCState::AwaitingRemovedRemoteRevoke(_) => (false, "AwaitingRemovedRemoteRevoke"),
};
+ let preimage_opt = match htlc.state {
+ OutboundHTLCState::RemoteRemoved(OutboundHTLCOutcome::Success(p)) => p,
+ OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Success(p)) => p,
+ OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Success(p)) => p,
+ _ => None,
+ };
+
+ if let Some(preimage) = preimage_opt {
+ preimages.push(preimage);
+ }
+
if include {
add_htlc_output!(htlc, true, Some(&htlc.source), state_name);
local_htlc_total_msat += htlc.amount_msat;
} else {
log_trace!(logger, " ...not including outbound HTLC {} (hash {}) with value {} due to state ({})", htlc.htlc_id, log_bytes!(htlc.payment_hash.0), htlc.amount_msat, state_name);
match htlc.state {
- OutboundHTLCState::AwaitingRemoteRevokeToRemove(None)|OutboundHTLCState::AwaitingRemovedRemoteRevoke(None) => {
+ OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Success(_))|OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Success(_)) => {
value_to_self_msat_offset -= htlc.amount_msat as i64;
},
- OutboundHTLCState::RemoteRemoved(None) => {
+ OutboundHTLCState::RemoteRemoved(OutboundHTLCOutcome::Success(_)) => {
if !generated_by_local {
value_to_self_msat_offset -= htlc.amount_msat as i64;
}
htlcs_included,
local_balance_msat: value_to_self_msat as u64,
remote_balance_msat: value_to_remote_msat as u64,
+ preimages
}
}
}
}
if pending_idx == core::usize::MAX {
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
// If we failed to find an HTLC to fulfill, make sure it was previously fulfilled and
// this is simply a duplicate claim, not previously failed and we lost funds.
debug_assert!(self.historical_inbound_htlc_fulfills.contains(&htlc_id_arg));
if htlc_id_arg == htlc_id {
// Make sure we don't leave latest_monitor_update_id incremented here:
self.latest_monitor_update_id -= 1;
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
debug_assert!(self.historical_inbound_htlc_fulfills.contains(&htlc_id_arg));
return UpdateFulfillFetch::DuplicateClaim {};
}
self.holding_cell_htlc_updates.push(HTLCUpdateAwaitingACK::ClaimHTLC {
payment_preimage: payment_preimage_arg, htlc_id: htlc_id_arg,
});
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
self.historical_inbound_htlc_fulfills.insert(htlc_id_arg);
return UpdateFulfillFetch::NewClaim { monitor_update, htlc_value_msat, msg: None };
}
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
self.historical_inbound_htlc_fulfills.insert(htlc_id_arg);
{
}
}
if pending_idx == core::usize::MAX {
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
// If we failed to find an HTLC to fail, make sure it was previously fulfilled and this
// is simply a duplicate fail, not previously failed and we failed-back too early.
debug_assert!(self.historical_inbound_htlc_fulfills.contains(&htlc_id_arg));
match pending_update {
&HTLCUpdateAwaitingACK::ClaimHTLC { htlc_id, .. } => {
if htlc_id_arg == htlc_id {
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
debug_assert!(self.historical_inbound_htlc_fulfills.contains(&htlc_id_arg));
return Ok(None);
}
// Message handlers:
- pub fn accept_channel(&mut self, msg: &msgs::AcceptChannel, config: &UserConfig, their_features: &InitFeatures) -> Result<(), ChannelError> {
+ pub fn accept_channel(&mut self, msg: &msgs::AcceptChannel, default_limits: &ChannelHandshakeLimits, their_features: &InitFeatures) -> Result<(), ChannelError> {
+ let peer_limits = if let Some(ref limits) = self.inbound_handshake_limits_override { limits } else { default_limits };
+
// Check sanity of message fields:
if !self.is_outbound() {
return Err(ChannelError::Close("Got an accept_channel message from an inbound peer".to_owned()));
if msg.dust_limit_satoshis > self.holder_selected_channel_reserve_satoshis {
return Err(ChannelError::Close(format!("Dust limit ({}) is bigger than our channel reserve ({})", msg.dust_limit_satoshis, self.holder_selected_channel_reserve_satoshis)));
}
+ if msg.channel_reserve_satoshis > self.channel_value_satoshis - self.holder_selected_channel_reserve_satoshis {
+ return Err(ChannelError::Close(format!("Bogus channel_reserve_satoshis ({}). Must not be greater than channel value minus our reserve ({})",
+ msg.channel_reserve_satoshis, self.channel_value_satoshis - self.holder_selected_channel_reserve_satoshis)));
+ }
let full_channel_value_msat = (self.channel_value_satoshis - msg.channel_reserve_satoshis) * 1000;
if msg.htlc_minimum_msat >= full_channel_value_msat {
return Err(ChannelError::Close(format!("Minimum htlc value ({}) is full channel value ({})", msg.htlc_minimum_msat, full_channel_value_msat)));
}
- let max_delay_acceptable = u16::min(config.peer_channel_config_limits.their_to_self_delay, MAX_LOCAL_BREAKDOWN_TIMEOUT);
+ let max_delay_acceptable = u16::min(peer_limits.their_to_self_delay, MAX_LOCAL_BREAKDOWN_TIMEOUT);
if msg.to_self_delay > max_delay_acceptable {
return Err(ChannelError::Close(format!("They wanted our payments to be delayed by a needlessly long period. Upper limit: {}. Actual: {}", max_delay_acceptable, msg.to_self_delay)));
}
}
// Now check against optional parameters as set by config...
- if msg.htlc_minimum_msat > config.peer_channel_config_limits.max_htlc_minimum_msat {
- return Err(ChannelError::Close(format!("htlc_minimum_msat ({}) is higher than the user specified limit ({})", msg.htlc_minimum_msat, config.peer_channel_config_limits.max_htlc_minimum_msat)));
+ if msg.htlc_minimum_msat > peer_limits.max_htlc_minimum_msat {
+ return Err(ChannelError::Close(format!("htlc_minimum_msat ({}) is higher than the user specified limit ({})", msg.htlc_minimum_msat, peer_limits.max_htlc_minimum_msat)));
}
- if msg.max_htlc_value_in_flight_msat < config.peer_channel_config_limits.min_max_htlc_value_in_flight_msat {
- return Err(ChannelError::Close(format!("max_htlc_value_in_flight_msat ({}) is less than the user specified limit ({})", msg.max_htlc_value_in_flight_msat, config.peer_channel_config_limits.min_max_htlc_value_in_flight_msat)));
+ if msg.max_htlc_value_in_flight_msat < peer_limits.min_max_htlc_value_in_flight_msat {
+ return Err(ChannelError::Close(format!("max_htlc_value_in_flight_msat ({}) is less than the user specified limit ({})", msg.max_htlc_value_in_flight_msat, peer_limits.min_max_htlc_value_in_flight_msat)));
}
- if msg.channel_reserve_satoshis > config.peer_channel_config_limits.max_channel_reserve_satoshis {
- return Err(ChannelError::Close(format!("channel_reserve_satoshis ({}) is higher than the user specified limit ({})", msg.channel_reserve_satoshis, config.peer_channel_config_limits.max_channel_reserve_satoshis)));
+ if msg.channel_reserve_satoshis > peer_limits.max_channel_reserve_satoshis {
+ return Err(ChannelError::Close(format!("channel_reserve_satoshis ({}) is higher than the user specified limit ({})", msg.channel_reserve_satoshis, peer_limits.max_channel_reserve_satoshis)));
}
- if msg.max_accepted_htlcs < config.peer_channel_config_limits.min_max_accepted_htlcs {
- return Err(ChannelError::Close(format!("max_accepted_htlcs ({}) is less than the user specified limit ({})", msg.max_accepted_htlcs, config.peer_channel_config_limits.min_max_accepted_htlcs)));
+ if msg.max_accepted_htlcs < peer_limits.min_max_accepted_htlcs {
+ return Err(ChannelError::Close(format!("max_accepted_htlcs ({}) is less than the user specified limit ({})", msg.max_accepted_htlcs, peer_limits.min_max_accepted_htlcs)));
}
if msg.dust_limit_satoshis < MIN_CHAN_DUST_LIMIT_SATOSHIS {
return Err(ChannelError::Close(format!("dust_limit_satoshis ({}) is less than the implementation limit ({})", msg.dust_limit_satoshis, MIN_CHAN_DUST_LIMIT_SATOSHIS)));
if msg.dust_limit_satoshis > MAX_CHAN_DUST_LIMIT_SATOSHIS {
return Err(ChannelError::Close(format!("dust_limit_satoshis ({}) is greater than the implementation limit ({})", msg.dust_limit_satoshis, MAX_CHAN_DUST_LIMIT_SATOSHIS)));
}
- if msg.minimum_depth > config.peer_channel_config_limits.max_minimum_depth {
- return Err(ChannelError::Close(format!("We consider the minimum depth to be unreasonably large. Expected minimum: ({}). Actual: ({})", config.peer_channel_config_limits.max_minimum_depth, msg.minimum_depth)));
+ if msg.minimum_depth > peer_limits.max_minimum_depth {
+ return Err(ChannelError::Close(format!("We consider the minimum depth to be unreasonably large. Expected minimum: ({}). Actual: ({})", peer_limits.max_minimum_depth, msg.minimum_depth)));
}
if msg.minimum_depth == 0 {
// Note that if this changes we should update the serialization minimum version to
return Err(ChannelError::Close("Minimum confirmation depth must be at least 1".to_owned()));
}
+ if let Some(ty) = &msg.channel_type {
+ if *ty != self.channel_type {
+ return Err(ChannelError::Close("Channel Type in accept_channel didn't match the one sent in open_channel.".to_owned()));
+ }
+ } else if their_features.supports_channel_type() {
+ // Assume they've accepted the channel type as they said they understand it.
+ } else {
+ self.channel_type = ChannelTypeFeatures::from_counterparty_init(&their_features)
+ }
+
let counterparty_shutdown_scriptpubkey = if their_features.supports_upfront_shutdown_script() {
match &msg.shutdown_scriptpubkey {
&OptionalField::Present(ref script) => {
self.counterparty_shutdown_scriptpubkey = counterparty_shutdown_scriptpubkey;
self.channel_state = ChannelState::OurInitSent as u32 | ChannelState::TheirInitSent as u32;
+ self.inbound_handshake_limits_override = None; // We're done enforcing limits on our peer's handshake now.
Ok(())
}
log_bytes!(sig.serialize_compact()[..]), log_bytes!(self.counterparty_funding_pubkey().serialize()),
encode::serialize_hex(&initial_commitment_bitcoin_tx.transaction), log_bytes!(sighash[..]),
encode::serialize_hex(&funding_script), log_bytes!(self.channel_id()));
- secp_check!(self.secp_ctx.verify(&sighash, &sig, self.counterparty_funding_pubkey()), "Invalid funding_created signature from peer".to_owned());
+ secp_check!(self.secp_ctx.verify_ecdsa(&sighash, &sig, self.counterparty_funding_pubkey()), "Invalid funding_created signature from peer".to_owned());
}
let counterparty_keys = self.build_remote_transaction_keys()?;
log_trace!(logger, "Initial counterparty tx for channel {} is: txid {} tx {}",
log_bytes!(self.channel_id()), counterparty_initial_bitcoin_tx.txid, encode::serialize_hex(&counterparty_initial_bitcoin_tx.transaction));
- let counterparty_signature = self.holder_signer.sign_counterparty_commitment(&counterparty_initial_commitment_tx, &self.secp_ctx)
+ let counterparty_signature = self.holder_signer.sign_counterparty_commitment(&counterparty_initial_commitment_tx, Vec::new(), &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.
// channel.
return Err(ChannelError::Close("Received funding_created after we got the channel!".to_owned()));
}
+ if self.inbound_awaiting_accept {
+ return Err(ChannelError::Close("FundingCreated message received before the channel was accepted".to_owned()));
+ }
if self.commitment_secrets.get_min_seen_secret() != (1 << 48) ||
self.cur_counterparty_commitment_transaction_number != INITIAL_COMMITMENT_NUMBER ||
self.cur_holder_commitment_transaction_number != INITIAL_COMMITMENT_NUMBER {
self.counterparty_funding_pubkey()
);
- self.holder_signer.validate_holder_commitment(&holder_commitment_tx)
+ self.holder_signer.validate_holder_commitment(&holder_commitment_tx, Vec::new())
.map_err(|_| ChannelError::Close("Failed to validate our commitment".to_owned()))?;
// Now that we're past error-generating stuff, update our local state:
let initial_commitment_bitcoin_tx = trusted_tx.built_transaction();
let sighash = initial_commitment_bitcoin_tx.get_sighash_all(&funding_script, self.channel_value_satoshis);
// They sign our commitment transaction, allowing us to broadcast the tx if we wish.
- if let Err(_) = self.secp_ctx.verify(&sighash, &msg.signature, &self.get_counterparty_pubkeys().funding_pubkey) {
+ if let Err(_) = self.secp_ctx.verify_ecdsa(&sighash, &msg.signature, &self.get_counterparty_pubkeys().funding_pubkey) {
return Err(ChannelError::Close("Invalid funding_signed signature from peer".to_owned()));
}
}
self.counterparty_funding_pubkey()
);
- self.holder_signer.validate_holder_commitment(&holder_commitment_tx)
+ self.holder_signer.validate_holder_commitment(&holder_commitment_tx, Vec::new())
.map_err(|_| ChannelError::Close("Failed to validate our commitment".to_owned()))?;
Ok((channel_monitor, self.funding_transaction.as_ref().cloned().unwrap()))
}
- pub fn funding_locked<L: Deref>(&mut self, msg: &msgs::FundingLocked, logger: &L) -> Result<(), ChannelError> where L::Target: Logger {
+ /// Handles a funding_locked message from our peer. If we've already sent our funding_locked
+ /// and the channel is now usable (and public), this may generate an announcement_signatures to
+ /// reply with.
+ pub fn funding_locked<L: Deref>(&mut self, msg: &msgs::FundingLocked, node_pk: PublicKey, genesis_block_hash: BlockHash, best_block: &BestBlock, logger: &L) -> Result<Option<msgs::AnnouncementSignatures>, ChannelError> where L::Target: Logger {
if self.channel_state & (ChannelState::PeerDisconnected as u32) == ChannelState::PeerDisconnected as u32 {
self.workaround_lnd_bug_4006 = Some(msg.clone());
return Err(ChannelError::Ignore("Peer sent funding_locked when we needed a channel_reestablish. The peer is likely lnd, see https://github.com/lightningnetwork/lnd/issues/4006".to_owned()));
}
+ if let Some(scid_alias) = msg.short_channel_id_alias {
+ if Some(scid_alias) != self.short_channel_id {
+ // The scid alias provided can be used to route payments *from* our counterparty,
+ // i.e. can be used for inbound payments and provided in invoices, but is not used
+ // when routing outbound payments.
+ self.latest_inbound_scid_alias = Some(scid_alias);
+ }
+ }
+
let non_shutdown_state = self.channel_state & (!MULTI_STATE_FLAGS);
if non_shutdown_state == ChannelState::FundingSent as u32 {
} else if non_shutdown_state == (ChannelState::FundingSent as u32 | ChannelState::OurFundingLocked as u32) {
self.channel_state = ChannelState::ChannelFunded as u32 | (self.channel_state & MULTI_STATE_FLAGS);
self.update_time_counter += 1;
- } else if (self.channel_state & (ChannelState::ChannelFunded as u32) != 0 &&
- // Note that funding_signed/funding_created will have decremented both by 1!
- self.cur_holder_commitment_transaction_number == INITIAL_COMMITMENT_NUMBER - 1 &&
- self.cur_counterparty_commitment_transaction_number == INITIAL_COMMITMENT_NUMBER - 1) ||
- // If we reconnected before sending our funding locked they may still resend theirs:
- (self.channel_state & (ChannelState::FundingSent as u32 | ChannelState::TheirFundingLocked as u32) ==
- (ChannelState::FundingSent as u32 | ChannelState::TheirFundingLocked as u32)) {
- if self.counterparty_cur_commitment_point != Some(msg.next_per_commitment_point) {
+ } else if self.channel_state & (ChannelState::ChannelFunded as u32) != 0 ||
+ // If we reconnected before sending our funding locked they may still resend theirs:
+ (self.channel_state & (ChannelState::FundingSent as u32 | ChannelState::TheirFundingLocked as u32) ==
+ (ChannelState::FundingSent as u32 | ChannelState::TheirFundingLocked as u32))
+ {
+ // They probably disconnected/reconnected and re-sent the funding_locked, which is
+ // required, or they're sending a fresh SCID alias.
+ let expected_point =
+ if self.cur_counterparty_commitment_transaction_number == INITIAL_COMMITMENT_NUMBER - 1 {
+ // If they haven't ever sent an updated point, the point they send should match
+ // the current one.
+ self.counterparty_cur_commitment_point
+ } else {
+ // If they have sent updated points, funding_locked is always supposed to match
+ // their "first" point, which we re-derive here.
+ Some(PublicKey::from_secret_key(&self.secp_ctx, &SecretKey::from_slice(
+ &self.commitment_secrets.get_secret(INITIAL_COMMITMENT_NUMBER - 1).expect("We should have all prev secrets available")
+ ).expect("We already advanced, so previous secret keys should have been validated already")))
+ };
+ if expected_point != Some(msg.next_per_commitment_point) {
return Err(ChannelError::Close("Peer sent a reconnect funding_locked with a different point".to_owned()));
}
- // They probably disconnected/reconnected and re-sent the funding_locked, which is required
- return Ok(());
+ return Ok(None);
} else {
return Err(ChannelError::Close("Peer sent a funding_locked at a strange time".to_owned()));
}
log_info!(logger, "Received funding_locked from peer for channel {}", log_bytes!(self.channel_id()));
- Ok(())
+ Ok(self.get_announcement_sigs(node_pk, genesis_block_hash, best_block.height(), logger))
}
/// Returns transaction if there is pending funding transaction that is yet to broadcast
pub fn unbroadcasted_funding(&self) -> Option<Transaction> {
- if self.channel_state & (ChannelState::FundingCreated as u32) != 0 {
- self.funding_transaction.clone()
- } else {
- None
- }
+ if self.channel_state & (ChannelState::FundingCreated as u32) != 0 {
+ self.funding_transaction.clone()
+ } else {
+ None
+ }
}
/// Returns a HTLCStats about inbound pending htlcs
stats
}
- /// Get the available (ie not including pending HTLCs) inbound and outbound balance in msat.
+ /// Get the available balances, see [`AvailableBalances`]'s fields for more info.
/// Doesn't bother handling the
/// if-we-removed-it-already-but-haven't-fully-resolved-they-can-still-send-an-inbound-HTLC
/// corner case properly.
- /// The channel reserve is subtracted from each balance.
- /// See also [`Channel::get_balance_msat`]
- pub fn get_inbound_outbound_available_balance_msat(&self) -> (u64, u64) {
+ pub fn get_available_balances(&self) -> AvailableBalances {
// Note that we have to handle overflow due to the above case.
- (
- cmp::max(self.channel_value_satoshis as i64 * 1000
- - self.value_to_self_msat as i64
- - self.get_inbound_pending_htlc_stats(None).pending_htlcs_value_msat as i64
- - self.holder_selected_channel_reserve_satoshis as i64 * 1000,
- 0) as u64,
- cmp::max(self.value_to_self_msat as i64
- - self.get_outbound_pending_htlc_stats(None).pending_htlcs_value_msat as i64
- - self.counterparty_selected_channel_reserve_satoshis.unwrap_or(0) as i64 * 1000,
- 0) as u64
- )
- }
+ let outbound_stats = self.get_outbound_pending_htlc_stats(None);
- /// Get our total balance in msat.
- /// This is the amount that would go to us if we close the channel, ignoring any on-chain fees.
- /// See also [`Channel::get_inbound_outbound_available_balance_msat`]
- pub fn get_balance_msat(&self) -> u64 {
- self.value_to_self_msat
- - self.get_outbound_pending_htlc_stats(None).pending_htlcs_value_msat
+ let mut balance_msat = self.value_to_self_msat;
+ for ref htlc in self.pending_inbound_htlcs.iter() {
+ if let InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::Fulfill(_)) = htlc.state {
+ balance_msat += htlc.amount_msat;
+ }
+ }
+ balance_msat -= outbound_stats.pending_htlcs_value_msat;
+
+ let outbound_capacity_msat = cmp::max(self.value_to_self_msat as i64
+ - outbound_stats.pending_htlcs_value_msat as i64
+ - self.counterparty_selected_channel_reserve_satoshis.unwrap_or(0) as i64 * 1000,
+ 0) as u64;
+ AvailableBalances {
+ inbound_capacity_msat: cmp::max(self.channel_value_satoshis as i64 * 1000
+ - self.value_to_self_msat as i64
+ - self.get_inbound_pending_htlc_stats(None).pending_htlcs_value_msat as i64
+ - self.holder_selected_channel_reserve_satoshis as i64 * 1000,
+ 0) as u64,
+ outbound_capacity_msat,
+ next_outbound_htlc_limit_msat: cmp::max(cmp::min(outbound_capacity_msat as i64,
+ self.counterparty_max_htlc_value_in_flight_msat as i64
+ - outbound_stats.pending_htlcs_value_msat as i64),
+ 0) as u64,
+ balance_msat,
+ }
}
pub fn get_holder_counterparty_selected_channel_reserve_satoshis(&self) -> (u64, Option<u64>) {
let num_htlcs = included_htlcs + addl_htlcs;
let res = Self::commit_tx_fee_msat(self.feerate_per_kw, num_htlcs, self.opt_anchors());
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
{
let mut fee = res;
if fee_spike_buffer_htlc.is_some() {
let num_htlcs = included_htlcs + addl_htlcs;
let res = Self::commit_tx_fee_msat(self.feerate_per_kw, num_htlcs, self.opt_anchors());
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
{
let mut fee = res;
if fee_spike_buffer_htlc.is_some() {
// transaction).
let mut removed_outbound_total_msat = 0;
for ref htlc in self.pending_outbound_htlcs.iter() {
- if let OutboundHTLCState::AwaitingRemoteRevokeToRemove(None) = htlc.state {
+ if let OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Success(_)) = htlc.state {
removed_outbound_total_msat += htlc.amount_msat;
- } else if let OutboundHTLCState::AwaitingRemovedRemoteRevoke(None) = htlc.state {
+ } else if let OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Success(_)) = htlc.state {
removed_outbound_total_msat += htlc.amount_msat;
}
}
/// Marks an outbound HTLC which we have received update_fail/fulfill/malformed
#[inline]
- fn mark_outbound_htlc_removed(&mut self, htlc_id: u64, check_preimage: Option<PaymentHash>, fail_reason: Option<HTLCFailReason>) -> Result<&OutboundHTLCOutput, ChannelError> {
+ fn mark_outbound_htlc_removed(&mut self, htlc_id: u64, check_preimage: Option<PaymentPreimage>, fail_reason: Option<HTLCFailReason>) -> Result<&OutboundHTLCOutput, ChannelError> {
+ assert!(!(check_preimage.is_some() && fail_reason.is_some()), "cannot fail while we have a preimage");
for htlc in self.pending_outbound_htlcs.iter_mut() {
if htlc.htlc_id == htlc_id {
- match check_preimage {
- None => {},
- Some(payment_hash) =>
+ let outcome = match check_preimage {
+ None => fail_reason.into(),
+ Some(payment_preimage) => {
+ let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0[..]).into_inner());
if payment_hash != htlc.payment_hash {
return Err(ChannelError::Close(format!("Remote tried to fulfill HTLC ({}) with an incorrect preimage", htlc_id)));
}
+ OutboundHTLCOutcome::Success(Some(payment_preimage))
+ }
};
match htlc.state {
OutboundHTLCState::LocalAnnounced(_) =>
return Err(ChannelError::Close(format!("Remote tried to fulfill/fail HTLC ({}) before it had been committed", htlc_id))),
OutboundHTLCState::Committed => {
- htlc.state = OutboundHTLCState::RemoteRemoved(fail_reason);
+ htlc.state = OutboundHTLCState::RemoteRemoved(outcome);
},
OutboundHTLCState::AwaitingRemoteRevokeToRemove(_) | OutboundHTLCState::AwaitingRemovedRemoteRevoke(_) | OutboundHTLCState::RemoteRemoved(_) =>
return Err(ChannelError::Close(format!("Remote tried to fulfill/fail HTLC ({}) that they'd already fulfilled/failed", htlc_id))),
return Err(ChannelError::Close("Peer sent update_fulfill_htlc when we needed a channel_reestablish".to_owned()));
}
- let payment_hash = PaymentHash(Sha256::hash(&msg.payment_preimage.0[..]).into_inner());
- self.mark_outbound_htlc_removed(msg.htlc_id, Some(payment_hash), None).map(|htlc| (htlc.source.clone(), htlc.amount_msat))
+ self.mark_outbound_htlc_removed(msg.htlc_id, Some(msg.payment_preimage), None).map(|htlc| (htlc.source.clone(), htlc.amount_msat))
}
pub fn update_fail_htlc(&mut self, msg: &msgs::UpdateFailHTLC, fail_reason: HTLCFailReason) -> Result<(), ChannelError> {
log_bytes!(msg.signature.serialize_compact()[..]),
log_bytes!(self.counterparty_funding_pubkey().serialize()), encode::serialize_hex(&bitcoin_tx.transaction),
log_bytes!(sighash[..]), encode::serialize_hex(&funding_script), log_bytes!(self.channel_id()));
- if let Err(_) = self.secp_ctx.verify(&sighash, &msg.signature, &self.counterparty_funding_pubkey()) {
+ if let Err(_) = self.secp_ctx.verify_ecdsa(&sighash, &msg.signature, &self.counterparty_funding_pubkey()) {
return Err((None, ChannelError::Close("Invalid commitment tx signature from peer".to_owned())));
}
bitcoin_tx.txid
return Err((None, ChannelError::Close("Funding remote cannot afford proposed new fee".to_owned())));
}
}
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
{
if self.is_outbound() {
let projected_commit_tx_info = self.next_local_commitment_tx_fee_info_cached.lock().unwrap().take();
&keys.broadcaster_delayed_payment_key, &keys.revocation_key);
let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, self.opt_anchors(), &keys);
- let htlc_sighashtype = if self.opt_anchors() { SigHashType::SinglePlusAnyoneCanPay } else { SigHashType::All };
- let htlc_sighash = hash_to_message!(&bip143::SigHashCache::new(&htlc_tx).signature_hash(0, &htlc_redeemscript, htlc.amount_msat / 1000, htlc_sighashtype)[..]);
+ let htlc_sighashtype = if self.opt_anchors() { EcdsaSighashType::SinglePlusAnyoneCanPay } else { EcdsaSighashType::All };
+ let htlc_sighash = hash_to_message!(&sighash::SighashCache::new(&htlc_tx).segwit_signature_hash(0, &htlc_redeemscript, htlc.amount_msat / 1000, htlc_sighashtype).unwrap()[..]);
log_trace!(logger, "Checking HTLC tx signature {} by key {} against tx {} (sighash {}) with redeemscript {} in channel {}.",
log_bytes!(msg.htlc_signatures[idx].serialize_compact()[..]), log_bytes!(keys.countersignatory_htlc_key.serialize()),
encode::serialize_hex(&htlc_tx), log_bytes!(htlc_sighash[..]), encode::serialize_hex(&htlc_redeemscript), log_bytes!(self.channel_id()));
- if let Err(_) = self.secp_ctx.verify(&htlc_sighash, &msg.htlc_signatures[idx], &keys.countersignatory_htlc_key) {
+ if let Err(_) = self.secp_ctx.verify_ecdsa(&htlc_sighash, &msg.htlc_signatures[idx], &keys.countersignatory_htlc_key) {
return Err((None, ChannelError::Close("Invalid HTLC tx signature from peer".to_owned())));
}
htlcs_and_sigs.push((htlc, Some(msg.htlc_signatures[idx]), source));
);
let next_per_commitment_point = self.holder_signer.get_per_commitment_point(self.cur_holder_commitment_transaction_number - 1, &self.secp_ctx);
- self.holder_signer.validate_holder_commitment(&holder_commitment_tx)
+ self.holder_signer.validate_holder_commitment(&holder_commitment_tx, commitment_stats.preimages)
.map_err(|_| (None, ChannelError::Close("Failed to validate our commitment".to_owned())))?;
let per_commitment_secret = self.holder_signer.release_commitment_secret(self.cur_holder_commitment_transaction_number + 1);
}
}
for htlc in self.pending_outbound_htlcs.iter_mut() {
- if let Some(fail_reason) = if let &mut OutboundHTLCState::RemoteRemoved(ref mut fail_reason) = &mut htlc.state {
- Some(fail_reason.take())
- } else { None } {
+ if let &mut OutboundHTLCState::RemoteRemoved(ref mut outcome) = &mut htlc.state {
log_trace!(logger, "Updating HTLC {} to AwaitingRemoteRevokeToRemove due to commitment_signed in channel {}.",
log_bytes!(htlc.payment_hash.0), log_bytes!(self.channel_id));
- htlc.state = OutboundHTLCState::AwaitingRemoteRevokeToRemove(fail_reason);
+ // Grab the preimage, if it exists, instead of cloning
+ let mut reason = OutboundHTLCOutcome::Success(None);
+ mem::swap(outcome, &mut reason);
+ htlc.state = OutboundHTLCState::AwaitingRemoteRevokeToRemove(reason);
need_commitment = true;
}
}
return Err(ChannelError::Close("Received an unexpected revoke_and_ack".to_owned()));
}
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
{
*self.next_local_commitment_tx_fee_info_cached.lock().unwrap() = None;
*self.next_remote_commitment_tx_fee_info_cached.lock().unwrap() = None;
self.counterparty_cur_commitment_point = Some(msg.next_per_commitment_point);
self.cur_counterparty_commitment_transaction_number -= 1;
+ if self.announcement_sigs_state == AnnouncementSigsState::Committed {
+ self.announcement_sigs_state = AnnouncementSigsState::PeerReceived;
+ }
+
log_trace!(logger, "Updating HTLCs on receipt of RAA in channel {}...", log_bytes!(self.channel_id()));
let mut to_forward_infos = Vec::new();
let mut revoked_htlcs = Vec::new();
} else { true }
});
pending_outbound_htlcs.retain(|htlc| {
- if let &OutboundHTLCState::AwaitingRemovedRemoteRevoke(ref fail_reason) = &htlc.state {
+ if let &OutboundHTLCState::AwaitingRemovedRemoteRevoke(ref outcome) = &htlc.state {
log_trace!(logger, " ...removing outbound AwaitingRemovedRemoteRevoke {}", log_bytes!(htlc.payment_hash.0));
- if let Some(reason) = fail_reason.clone() { // We really want take() here, but, again, non-mut ref :(
+ if let OutboundHTLCOutcome::Failure(reason) = outcome.clone() { // We really want take() here, but, again, non-mut ref :(
revoked_htlcs.push((htlc.source.clone(), htlc.payment_hash, reason));
} else {
finalized_claimed_htlcs.push(htlc.source.clone());
log_trace!(logger, " ...promoting outbound LocalAnnounced {} to Committed", log_bytes!(htlc.payment_hash.0));
htlc.state = OutboundHTLCState::Committed;
}
- if let Some(fail_reason) = if let &mut OutboundHTLCState::AwaitingRemoteRevokeToRemove(ref mut fail_reason) = &mut htlc.state {
- Some(fail_reason.take())
- } else { None } {
+ if let &mut OutboundHTLCState::AwaitingRemoteRevokeToRemove(ref mut outcome) = &mut htlc.state {
log_trace!(logger, " ...promoting outbound AwaitingRemoteRevokeToRemove {} to AwaitingRemovedRemoteRevoke", log_bytes!(htlc.payment_hash.0));
- htlc.state = OutboundHTLCState::AwaitingRemovedRemoteRevoke(fail_reason);
+ // Grab the preimage, if it exists, instead of cloning
+ let mut reason = OutboundHTLCOutcome::Success(None);
+ mem::swap(outcome, &mut reason);
+ htlc.state = OutboundHTLCState::AwaitingRemovedRemoteRevoke(reason);
require_commitment = true;
}
}
self.channel_state = ChannelState::ShutdownComplete as u32;
return;
}
+
+ if self.announcement_sigs_state == AnnouncementSigsState::MessageSent || self.announcement_sigs_state == AnnouncementSigsState::Committed {
+ self.announcement_sigs_state = AnnouncementSigsState::NotSent;
+ }
+
// Upon reconnect we have to start the closing_signed dance over, but shutdown messages
// will be retransmitted.
self.last_sent_closing_fee = None;
/// Indicates that the latest ChannelMonitor update has been committed by the client
/// successfully and we should restore normal operation. Returns messages which should be sent
/// to the remote side.
- pub fn monitor_updating_restored<L: Deref>(&mut self, logger: &L) -> MonitorRestoreUpdates where L::Target: Logger {
+ pub fn monitor_updating_restored<L: Deref>(&mut self, logger: &L, node_pk: PublicKey, genesis_block_hash: BlockHash, best_block_height: u32) -> MonitorRestoreUpdates where L::Target: Logger {
assert_eq!(self.channel_state & ChannelState::MonitorUpdateFailed as u32, ChannelState::MonitorUpdateFailed as u32);
self.channel_state &= !(ChannelState::MonitorUpdateFailed as u32);
Some(msgs::FundingLocked {
channel_id: self.channel_id(),
next_per_commitment_point,
+ short_channel_id_alias: Some(self.outbound_scid_alias),
})
} else { None };
+ let announcement_sigs = self.get_announcement_sigs(node_pk, genesis_block_hash, best_block_height, logger);
+
let mut accepted_htlcs = Vec::new();
mem::swap(&mut accepted_htlcs, &mut self.monitor_pending_forwards);
let mut failed_htlcs = Vec::new();
self.monitor_pending_commitment_signed = false;
return MonitorRestoreUpdates {
raa: None, commitment_update: None, order: RAACommitmentOrder::RevokeAndACKFirst,
- accepted_htlcs, failed_htlcs, finalized_claimed_htlcs, funding_broadcastable, funding_locked
+ accepted_htlcs, failed_htlcs, finalized_claimed_htlcs, funding_broadcastable, funding_locked, announcement_sigs
};
}
if commitment_update.is_some() { "a" } else { "no" }, if raa.is_some() { "an" } else { "no" },
match order { RAACommitmentOrder::CommitmentFirst => "commitment", RAACommitmentOrder::RevokeAndACKFirst => "RAA"});
MonitorRestoreUpdates {
- raa, commitment_update, order, accepted_htlcs, failed_htlcs, finalized_claimed_htlcs, funding_broadcastable, funding_locked
+ raa, commitment_update, order, accepted_htlcs, failed_htlcs, finalized_claimed_htlcs, funding_broadcastable, funding_locked, announcement_sigs
}
}
/// May panic if some calls other than message-handling calls (which will all Err immediately)
/// have been called between remove_uncommitted_htlcs_and_mark_paused and this call.
- pub fn channel_reestablish<L: Deref>(&mut self, msg: &msgs::ChannelReestablish, logger: &L) -> Result<(Option<msgs::FundingLocked>, Option<msgs::RevokeAndACK>, Option<msgs::CommitmentUpdate>, Option<ChannelMonitorUpdate>, RAACommitmentOrder, Vec<(HTLCSource, PaymentHash)>, Option<msgs::Shutdown>), ChannelError> where L::Target: Logger {
+ pub fn channel_reestablish<L: Deref>(&mut self, msg: &msgs::ChannelReestablish, logger: &L,
+ node_pk: PublicKey, genesis_block_hash: BlockHash, best_block: &BestBlock)
+ -> Result<ReestablishResponses, ChannelError> where L::Target: Logger {
if self.channel_state & (ChannelState::PeerDisconnected as u32) == 0 {
// While BOLT 2 doesn't indicate explicitly we should error this channel here, it
// almost certainly indicates we are going to end up out-of-sync in some way, so we
}
}
+ // Before we change the state of the channel, we check if the peer is sending a very old
+ // commitment transaction number, if yes we send a warning message.
+ let our_commitment_transaction = INITIAL_COMMITMENT_NUMBER - self.cur_holder_commitment_transaction_number - 1;
+ if msg.next_remote_commitment_number + 1 < our_commitment_transaction {
+ return Err(
+ ChannelError::Warn(format!("Peer attempted to reestablish channel with a very old local commitment transaction: {} (received) vs {} (expected)", msg.next_remote_commitment_number, our_commitment_transaction))
+ );
+ }
+
// Go ahead and unmark PeerDisconnected as various calls we may make check for it (and all
// remaining cases either succeed or ErrorMessage-fail).
self.channel_state &= !(ChannelState::PeerDisconnected as u32);
})
} else { None };
+ let announcement_sigs = self.get_announcement_sigs(node_pk, genesis_block_hash, best_block.height(), logger);
+
if self.channel_state & (ChannelState::FundingSent as u32) == ChannelState::FundingSent as u32 {
// If we're waiting on a monitor update, we shouldn't re-send any funding_locked's.
if self.channel_state & (ChannelState::OurFundingLocked as u32) == 0 ||
return Err(ChannelError::Close("Peer claimed they saw a revoke_and_ack but we haven't sent funding_locked yet".to_owned()));
}
// Short circuit the whole handler as there is nothing we can resend them
- return Ok((None, None, None, None, RAACommitmentOrder::CommitmentFirst, Vec::new(), shutdown_msg));
+ return Ok(ReestablishResponses {
+ funding_locked: None,
+ raa: None, commitment_update: None, mon_update: None,
+ order: RAACommitmentOrder::CommitmentFirst,
+ holding_cell_failed_htlcs: Vec::new(),
+ shutdown_msg, announcement_sigs,
+ });
}
// We have OurFundingLocked set!
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,
- }), None, None, None, RAACommitmentOrder::CommitmentFirst, Vec::new(), shutdown_msg));
+ return Ok(ReestablishResponses {
+ funding_locked: Some(msgs::FundingLocked {
+ channel_id: self.channel_id(),
+ next_per_commitment_point,
+ short_channel_id_alias: Some(self.outbound_scid_alias),
+ }),
+ raa: None, commitment_update: None, mon_update: None,
+ order: RAACommitmentOrder::CommitmentFirst,
+ holding_cell_failed_htlcs: Vec::new(),
+ shutdown_msg, announcement_sigs,
+ });
}
let required_revoke = if msg.next_remote_commitment_number + 1 == INITIAL_COMMITMENT_NUMBER - self.cur_holder_commitment_transaction_number {
// the corresponding revoke_and_ack back yet.
let next_counterparty_commitment_number = INITIAL_COMMITMENT_NUMBER - self.cur_counterparty_commitment_transaction_number + if (self.channel_state & ChannelState::AwaitingRemoteRevoke as u32) != 0 { 1 } else { 0 };
- let resend_funding_locked = if msg.next_local_commitment_number == 1 && INITIAL_COMMITMENT_NUMBER - self.cur_holder_commitment_transaction_number == 1 {
+ let 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_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,
+ short_channel_id_alias: Some(self.outbound_scid_alias),
})
} else { None };
// have received some updates while we were disconnected. Free the holding cell
// now!
match self.free_holding_cell_htlcs(logger) {
- Err(ChannelError::Close(msg)) => return Err(ChannelError::Close(msg)),
+ Err(ChannelError::Close(msg)) => Err(ChannelError::Close(msg)),
Err(ChannelError::Warn(_)) | Err(ChannelError::Ignore(_)) | Err(ChannelError::CloseDelayBroadcast(_)) =>
panic!("Got non-channel-failing result from free_holding_cell_htlcs"),
- Ok((Some((commitment_update, monitor_update)), htlcs_to_fail)) => {
- return Ok((resend_funding_locked, required_revoke, Some(commitment_update), Some(monitor_update), self.resend_order.clone(), htlcs_to_fail, shutdown_msg));
+ Ok((Some((commitment_update, monitor_update)), holding_cell_failed_htlcs)) => {
+ Ok(ReestablishResponses {
+ funding_locked, shutdown_msg, announcement_sigs,
+ raa: required_revoke,
+ commitment_update: Some(commitment_update),
+ order: self.resend_order.clone(),
+ mon_update: Some(monitor_update),
+ holding_cell_failed_htlcs,
+ })
},
- Ok((None, htlcs_to_fail)) => {
- return Ok((resend_funding_locked, required_revoke, None, None, self.resend_order.clone(), htlcs_to_fail, shutdown_msg));
+ Ok((None, holding_cell_failed_htlcs)) => {
+ Ok(ReestablishResponses {
+ funding_locked, shutdown_msg, announcement_sigs,
+ raa: required_revoke,
+ commitment_update: None,
+ order: self.resend_order.clone(),
+ mon_update: None,
+ holding_cell_failed_htlcs,
+ })
},
}
} else {
- return Ok((resend_funding_locked, required_revoke, None, None, self.resend_order.clone(), Vec::new(), shutdown_msg));
+ Ok(ReestablishResponses {
+ funding_locked, shutdown_msg, announcement_sigs,
+ raa: required_revoke,
+ commitment_update: None,
+ order: self.resend_order.clone(),
+ mon_update: None,
+ holding_cell_failed_htlcs: Vec::new(),
+ })
}
} else if msg.next_local_commitment_number == next_counterparty_commitment_number - 1 {
if required_revoke.is_some() {
if self.channel_state & (ChannelState::MonitorUpdateFailed as u32) != 0 {
self.monitor_pending_commitment_signed = true;
- return Ok((resend_funding_locked, None, None, None, self.resend_order.clone(), Vec::new(), shutdown_msg));
+ Ok(ReestablishResponses {
+ funding_locked, shutdown_msg, announcement_sigs,
+ commitment_update: None, raa: None, mon_update: None,
+ order: self.resend_order.clone(),
+ holding_cell_failed_htlcs: Vec::new(),
+ })
+ } else {
+ Ok(ReestablishResponses {
+ funding_locked, shutdown_msg, announcement_sigs,
+ raa: required_revoke,
+ commitment_update: Some(self.get_last_commitment_update(logger)),
+ order: self.resend_order.clone(),
+ mon_update: None,
+ holding_cell_failed_htlcs: Vec::new(),
+ })
}
-
- return Ok((resend_funding_locked, required_revoke, Some(self.get_last_commitment_update(logger)), None, self.resend_order.clone(), Vec::new(), shutdown_msg));
} else {
- return Err(ChannelError::Close("Peer attempted to reestablish channel with a very old remote commitment transaction".to_owned()));
+ Err(ChannelError::Close("Peer attempted to reestablish channel with a very old remote commitment transaction".to_owned()))
}
}
let funding_key = self.get_holder_pubkeys().funding_pubkey.serialize();
let counterparty_funding_key = self.counterparty_funding_pubkey().serialize();
+ let mut holder_sig = sig.serialize_der().to_vec();
+ holder_sig.push(EcdsaSighashType::All as u8);
+ let mut cp_sig = counterparty_sig.serialize_der().to_vec();
+ cp_sig.push(EcdsaSighashType::All as u8);
if funding_key[..] < counterparty_funding_key[..] {
- tx.input[0].witness.push(sig.serialize_der().to_vec());
- tx.input[0].witness.push(counterparty_sig.serialize_der().to_vec());
+ tx.input[0].witness.push(holder_sig);
+ tx.input[0].witness.push(cp_sig);
} else {
- tx.input[0].witness.push(counterparty_sig.serialize_der().to_vec());
- tx.input[0].witness.push(sig.serialize_der().to_vec());
+ tx.input[0].witness.push(cp_sig);
+ tx.input[0].witness.push(holder_sig);
}
- tx.input[0].witness[1].push(SigHashType::All as u8);
- tx.input[0].witness[2].push(SigHashType::All as u8);
tx.input[0].witness.push(self.get_funding_redeemscript().into_bytes());
tx
if !self.pending_inbound_htlcs.is_empty() || !self.pending_outbound_htlcs.is_empty() {
return Err(ChannelError::Close("Remote end sent us a closing_signed while there were still pending HTLCs".to_owned()));
}
- if msg.fee_satoshis > 21_000_000 * 1_0000_0000 { //this is required to stop potential overflow in build_closing_transaction
+ if msg.fee_satoshis > TOTAL_BITCOIN_SUPPLY_SATOSHIS { // this is required to stop potential overflow in build_closing_transaction
return Err(ChannelError::Close("Remote tried to send us a closing tx with > 21 million BTC fee".to_owned()));
}
}
let sighash = closing_tx.trust().get_sighash_all(&funding_redeemscript, self.channel_value_satoshis);
- match self.secp_ctx.verify(&sighash, &msg.signature, &self.get_counterparty_pubkeys().funding_pubkey) {
+ match self.secp_ctx.verify_ecdsa(&sighash, &msg.signature, &self.get_counterparty_pubkeys().funding_pubkey) {
Ok(_) => {},
Err(_e) => {
// The remote end may have decided to revoke their output due to inconsistent dust
// limits, so check for that case by re-checking the signature here.
closing_tx = self.build_closing_transaction(msg.fee_satoshis, true).0;
let sighash = closing_tx.trust().get_sighash_all(&funding_redeemscript, self.channel_value_satoshis);
- secp_check!(self.secp_ctx.verify(&sighash, &msg.signature, self.counterparty_funding_pubkey()), "Invalid closing tx signature from peer".to_owned());
+ secp_check!(self.secp_ctx.verify_ecdsa(&sighash, &msg.signature, self.counterparty_funding_pubkey()), "Invalid closing tx signature from peer".to_owned());
},
};
self.user_id
}
+ /// Gets the channel's type
+ pub fn get_channel_type(&self) -> &ChannelTypeFeatures {
+ &self.channel_type
+ }
+
/// Guaranteed to be Some after both FundingLocked messages have been exchanged (and, thus,
/// is_usable() returns true).
/// Allowed in any state (including after shutdown)
self.short_channel_id
}
+ /// Allowed in any state (including after shutdown)
+ pub fn latest_inbound_scid_alias(&self) -> Option<u64> {
+ self.latest_inbound_scid_alias
+ }
+
+ /// Allowed in any state (including after shutdown)
+ pub fn outbound_scid_alias(&self) -> u64 {
+ self.outbound_scid_alias
+ }
+ /// Only allowed immediately after deserialization if get_outbound_scid_alias returns 0,
+ /// indicating we were written by LDK prior to 0.0.106 which did not set outbound SCID aliases.
+ pub fn set_outbound_scid_alias(&mut self, outbound_scid_alias: u64) {
+ assert_eq!(self.outbound_scid_alias, 0);
+ self.outbound_scid_alias = outbound_scid_alias;
+ }
+
/// Returns the funding_txo we either got from our peer, or were given by
/// get_outbound_funding_created.
pub fn get_funding_txo(&self) -> Option<OutPoint> {
}
/// Allowed in any state (including after shutdown)
- #[cfg(test)]
pub fn get_holder_htlc_minimum_msat(&self) -> u64 {
self.holder_htlc_minimum_msat
}
+ /// Allowed in any state (including after shutdown), but will return none before TheirInitSent
+ pub fn get_holder_htlc_maximum_msat(&self) -> Option<u64> {
+ self.get_htlc_maximum_msat(self.holder_max_htlc_value_in_flight_msat)
+ }
+
/// Allowed in any state (including after shutdown)
pub fn get_announced_htlc_max_msat(&self) -> u64 {
return cmp::min(
// channel might have been used to route very small values (either by honest users or as DoS).
self.channel_value_satoshis * 1000 * 9 / 10,
- self.holder_max_htlc_value_in_flight_msat
+ self.counterparty_max_htlc_value_in_flight_msat
);
}
self.counterparty_htlc_minimum_msat
}
+ /// Allowed in any state (including after shutdown), but will return none before TheirInitSent
+ pub fn get_counterparty_htlc_maximum_msat(&self) -> Option<u64> {
+ self.get_htlc_maximum_msat(self.counterparty_max_htlc_value_in_flight_msat)
+ }
+
+ fn get_htlc_maximum_msat(&self, party_max_htlc_value_in_flight_msat: u64) -> Option<u64> {
+ self.counterparty_selected_channel_reserve_satoshis.map(|counterparty_reserve| {
+ let holder_reserve = self.holder_selected_channel_reserve_satoshis;
+ cmp::min(
+ (self.channel_value_satoshis - counterparty_reserve - holder_reserve) * 1000,
+ party_max_htlc_value_in_flight_msat
+ )
+ })
+ }
+
pub fn get_value_satoshis(&self) -> u64 {
self.channel_value_satoshis
}
/// Allowed in any state (including after shutdown)
pub fn is_usable(&self) -> bool {
let mask = ChannelState::ChannelFunded as u32 | BOTH_SIDES_SHUTDOWN_MASK;
- (self.channel_state & mask) == (ChannelState::ChannelFunded as u32)
+ (self.channel_state & mask) == (ChannelState::ChannelFunded as u32) && !self.monitor_pending_funding_locked
}
/// Returns true if this channel is currently available for use. This is a superset of
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,
- });
+ if self.channel_state & (ChannelState::PeerDisconnected as u32) == 0 {
+ let next_per_commitment_point =
+ self.holder_signer.get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 1, &self.secp_ctx);
+ return Some(msgs::FundingLocked {
+ channel_id: self.channel_id,
+ next_per_commitment_point,
+ short_channel_id_alias: Some(self.outbound_scid_alias),
+ });
+ }
} else {
self.monitor_pending_funding_locked = true;
}
/// 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<L: Deref>(&mut self, block_hash: &BlockHash, height: u32, txdata: &TransactionData, logger: &L)
- -> Result<Option<msgs::FundingLocked>, ClosureReason> where L::Target: Logger {
+ pub fn transactions_confirmed<L: Deref>(&mut self, block_hash: &BlockHash, height: u32,
+ txdata: &TransactionData, genesis_block_hash: BlockHash, node_pk: PublicKey, logger: &L)
+ -> Result<(Option<msgs::FundingLocked>, Option<msgs::AnnouncementSignatures>), ClosureReason> 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 let Some(funding_txo) = self.get_funding_txo() {
+ for &(index_in_block, tx) in txdata.iter() {
// 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 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
+ // probability in fuzzing mode, if we're fuzzing we just close the
// channel and move on.
- #[cfg(not(feature = "fuzztarget"))]
+ #[cfg(not(fuzzing))]
panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction!");
}
self.update_time_counter += 1;
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"))]
+ #[cfg(not(fuzzing))]
panic!("Client called ChannelManager::funding_transaction_generated with bogus transaction!");
}
}
// may have already happened for this block).
if let Some(funding_locked) = self.check_get_funding_locked(height) {
log_info!(logger, "Sending a funding_locked to our peer for channel {}", log_bytes!(self.channel_id));
- return Ok(Some(funding_locked));
+ let announcement_sigs = self.get_announcement_sigs(node_pk, genesis_block_hash, height, logger);
+ return Ok((Some(funding_locked), announcement_sigs));
}
}
for inp in tx.input.iter() {
}
}
}
- Ok(None)
+ Ok((None, None))
}
/// When a new block is connected, we check the height of the block against outbound holding
///
/// May return some HTLCs (and their payment_hash) which have timed out and should be failed
/// back.
- pub fn best_block_updated<L: Deref>(&mut self, height: u32, highest_header_time: u32, logger: &L)
- -> Result<(Option<msgs::FundingLocked>, Vec<(HTLCSource, PaymentHash)>), ClosureReason> where L::Target: Logger {
+ pub fn best_block_updated<L: Deref>(&mut self, height: u32, highest_header_time: u32, genesis_block_hash: BlockHash, node_pk: PublicKey, logger: &L)
+ -> Result<(Option<msgs::FundingLocked>, Vec<(HTLCSource, PaymentHash)>, Option<msgs::AnnouncementSignatures>), ClosureReason> where L::Target: Logger {
+ self.do_best_block_updated(height, highest_header_time, Some((genesis_block_hash, node_pk)), logger)
+ }
+
+ fn do_best_block_updated<L: Deref>(&mut self, height: u32, highest_header_time: u32, genesis_node_pk: Option<(BlockHash, PublicKey)>, logger: &L)
+ -> Result<(Option<msgs::FundingLocked>, Vec<(HTLCSource, PaymentHash)>, Option<msgs::AnnouncementSignatures>), ClosureReason> where L::Target: Logger {
let mut timed_out_htlcs = Vec::new();
// This mirrors the check in ChannelManager::decode_update_add_htlc_onion, refusing to
// forward an HTLC when our counterparty should almost certainly just fail it for expiring
self.update_time_counter = cmp::max(self.update_time_counter, highest_header_time);
if let Some(funding_locked) = self.check_get_funding_locked(height) {
+ let announcement_sigs = if let Some((genesis_block_hash, node_pk)) = genesis_node_pk {
+ self.get_announcement_sigs(node_pk, genesis_block_hash, height, logger)
+ } else { None };
log_info!(logger, "Sending a funding_locked to our peer for channel {}", log_bytes!(self.channel_id));
- return Ok((Some(funding_locked), timed_out_htlcs));
+ return Ok((Some(funding_locked), timed_out_htlcs, announcement_sigs));
}
let non_shutdown_state = self.channel_state & (!MULTI_STATE_FLAGS);
}
// 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,
+ // the funding transaction has become unconfirmed,
// 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.unwrap() as i64 / 2 {
+ //
+ // Note that ideally we wouldn't force-close if we see *any* reorg on a 1-conf channel,
+ // but not doing so may lead to the `ChannelManager::short_to_id` map being
+ // inconsistent, so we currently have to.
+ if funding_tx_confirmations == 0 && self.funding_tx_confirmed_in.is_some() {
let err_reason = format!("Funding transaction was un-confirmed. Locked at {} confs, now have {} confs.",
self.minimum_depth.unwrap(), funding_tx_confirmations);
return Err(ClosureReason::ProcessingError { err: err_reason });
return Err(ClosureReason::FundingTimedOut);
}
- Ok((None, timed_out_htlcs))
+ let announcement_sigs = if let Some((genesis_block_hash, node_pk)) = genesis_node_pk {
+ self.get_announcement_sigs(node_pk, genesis_block_hash, height, logger)
+ } else { None };
+ Ok((None, timed_out_htlcs, announcement_sigs))
}
/// Indicates the funding transaction is no longer confirmed in the main chain. This may
// 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, logger) {
- Ok((funding_locked, timed_out_htlcs)) => {
+ match self.do_best_block_updated(reorg_height, best_time, None, logger) {
+ Ok((funding_locked, timed_out_htlcs, announcement_sigs)) => {
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?");
+ assert!(announcement_sigs.is_none(), "We can't generate an announcement_sigs with 0 confirmations?");
Ok(())
},
Err(e) => Err(e)
}
}
- pub fn get_accept_channel(&self) -> msgs::AcceptChannel {
+ pub fn inbound_is_awaiting_accept(&self) -> bool {
+ self.inbound_awaiting_accept
+ }
+
+ /// Marks an inbound channel as accepted and generates a [`msgs::AcceptChannel`] message which
+ /// should be sent back to the counterparty node.
+ ///
+ /// [`msgs::AcceptChannel`]: crate::ln::msgs::AcceptChannel
+ pub fn accept_inbound_channel(&mut self, user_id: u64) -> msgs::AcceptChannel {
if self.is_outbound() {
panic!("Tried to send accept_channel for an outbound channel?");
}
if self.cur_holder_commitment_transaction_number != INITIAL_COMMITMENT_NUMBER {
panic!("Tried to send an accept_channel for a channel that has already advanced");
}
+ if !self.inbound_awaiting_accept {
+ panic!("The inbound channel has already been accepted");
+ }
+
+ self.user_id = user_id;
+ self.inbound_awaiting_accept = false;
+
+ self.generate_accept_channel_message()
+ }
+ /// This function is used to explicitly generate a [`msgs::AcceptChannel`] message for an
+ /// inbound channel. If the intention is to accept an inbound channel, use
+ /// [`Channel::accept_inbound_channel`] instead.
+ ///
+ /// [`msgs::AcceptChannel`]: crate::ln::msgs::AcceptChannel
+ fn generate_accept_channel_message(&self) -> msgs::AcceptChannel {
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();
Some(script) => script.clone().into_inner(),
None => Builder::new().into_script(),
}),
+ channel_type: Some(self.channel_type.clone()),
}
}
+ /// Enables the possibility for tests to extract a [`msgs::AcceptChannel`] message for an
+ /// inbound channel without accepting it.
+ ///
+ /// [`msgs::AcceptChannel`]: crate::ln::msgs::AcceptChannel
+ #[cfg(test)]
+ pub fn get_accept_channel_message(&self) -> msgs::AcceptChannel {
+ self.generate_accept_channel_message()
+ }
+
/// If an Err is returned, it is a ChannelError::Close (for get_outbound_funding_created)
fn get_outbound_funding_created_signature<L: Deref>(&mut self, logger: &L) -> Result<Signature, ChannelError> 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, logger).tx;
- Ok(self.holder_signer.sign_counterparty_commitment(&counterparty_initial_commitment_tx, &self.secp_ctx)
+ Ok(self.holder_signer.sign_counterparty_commitment(&counterparty_initial_commitment_tx, Vec::new(), &self.secp_ctx)
.map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed".to_owned()))?.0)
}
})
}
- /// Gets an UnsignedChannelAnnouncement, as well as a signature covering it using our
- /// bitcoin_key, if available, for this channel. The channel must be publicly announceable and
- /// available for use (have exchanged FundingLocked messages in both directions). Should be used
- /// for both loose and in response to an AnnouncementSignatures message from the remote peer.
+ /// Gets an UnsignedChannelAnnouncement for this channel. The channel must be publicly
+ /// announceable and available for use (have exchanged FundingLocked messages in both
+ /// directions). Should be used for both broadcasted announcements and in response to an
+ /// AnnouncementSignatures message from the remote peer.
+ ///
/// Will only fail if we're not in a state where channel_announcement may be sent (including
/// closing).
- /// Note that the "channel must be funded" requirement is stricter than BOLT 7 requires - see
- /// https://github.com/lightningnetwork/lightning-rfc/issues/468
///
/// This will only return ChannelError::Ignore upon failure.
- pub fn get_channel_announcement(&self, node_id: PublicKey, chain_hash: BlockHash) -> Result<(msgs::UnsignedChannelAnnouncement, Signature), ChannelError> {
+ fn get_channel_announcement(&self, node_id: PublicKey, chain_hash: BlockHash) -> Result<msgs::UnsignedChannelAnnouncement, ChannelError> {
if !self.config.announced_channel {
return Err(ChannelError::Ignore("Channel is not available for public announcements".to_owned()));
}
- if self.channel_state & (ChannelState::ChannelFunded as u32) == 0 {
- return Err(ChannelError::Ignore("Cannot get a ChannelAnnouncement until the channel funding has been locked".to_owned()));
- }
- if (self.channel_state & (ChannelState::LocalShutdownSent as u32 | ChannelState::ShutdownComplete as u32)) != 0 {
- return Err(ChannelError::Ignore("Cannot get a ChannelAnnouncement once the channel is closing".to_owned()));
+ if !self.is_usable() {
+ return Err(ChannelError::Ignore("Cannot get a ChannelAnnouncement if the channel is not currently usable".to_owned()));
}
let were_node_one = node_id.serialize()[..] < self.counterparty_node_id.serialize()[..];
excess_data: Vec::new(),
};
- let sig = self.holder_signer.sign_channel_announcement(&msg, &self.secp_ctx)
- .map_err(|_| ChannelError::Ignore("Signer rejected channel_announcement".to_owned()))?;
+ Ok(msg)
+ }
+
+ fn get_announcement_sigs<L: Deref>(&mut self, node_pk: PublicKey, genesis_block_hash: BlockHash, best_block_height: u32, logger: &L)
+ -> Option<msgs::AnnouncementSignatures> where L::Target: Logger {
+ if self.funding_tx_confirmation_height == 0 || self.funding_tx_confirmation_height + 5 > best_block_height {
+ return None;
+ }
+
+ if !self.is_usable() {
+ return None;
+ }
+
+ if self.channel_state & ChannelState::PeerDisconnected as u32 != 0 {
+ log_trace!(logger, "Cannot create an announcement_signatures as our peer is disconnected");
+ return None;
+ }
+
+ if self.announcement_sigs_state != AnnouncementSigsState::NotSent {
+ return None;
+ }
- Ok((msg, sig))
+ log_trace!(logger, "Creating an announcement_signatures message for channel {}", log_bytes!(self.channel_id()));
+ let announcement = match self.get_channel_announcement(node_pk, genesis_block_hash) {
+ Ok(a) => a,
+ Err(_) => {
+ log_trace!(logger, "Cannot create an announcement_signatures as channel is not public.");
+ return None;
+ }
+ };
+ let (our_node_sig, our_bitcoin_sig) = match self.holder_signer.sign_channel_announcement(&announcement, &self.secp_ctx) {
+ Err(_) => {
+ log_error!(logger, "Signer rejected channel_announcement signing. Channel will not be announced!");
+ return None;
+ },
+ Ok(v) => v
+ };
+ self.announcement_sigs_state = AnnouncementSigsState::MessageSent;
+
+ Some(msgs::AnnouncementSignatures {
+ channel_id: self.channel_id(),
+ short_channel_id: self.get_short_channel_id().unwrap(),
+ node_signature: our_node_sig,
+ bitcoin_signature: our_bitcoin_sig,
+ })
}
/// Signs the given channel announcement, returning a ChannelError::Ignore if no keys are
/// available.
- fn sign_channel_announcement(&self, our_node_secret: &SecretKey, our_node_id: PublicKey, msghash: secp256k1::Message, announcement: msgs::UnsignedChannelAnnouncement, our_bitcoin_sig: Signature) -> Result<msgs::ChannelAnnouncement, ChannelError> {
+ fn sign_channel_announcement(&self, our_node_id: PublicKey, announcement: msgs::UnsignedChannelAnnouncement) -> Result<msgs::ChannelAnnouncement, ChannelError> {
if let Some((their_node_sig, their_bitcoin_sig)) = self.announcement_sigs {
let were_node_one = announcement.node_id_1 == our_node_id;
- let our_node_sig = self.secp_ctx.sign(&msghash, our_node_secret);
+ let (our_node_sig, our_bitcoin_sig) = self.holder_signer.sign_channel_announcement(&announcement, &self.secp_ctx)
+ .map_err(|_| ChannelError::Ignore("Signer rejected channel_announcement".to_owned()))?;
Ok(msgs::ChannelAnnouncement {
node_signature_1: if were_node_one { our_node_sig } else { their_node_sig },
node_signature_2: if were_node_one { their_node_sig } else { our_node_sig },
/// Processes an incoming announcement_signatures message, providing a fully-signed
/// channel_announcement message which we can broadcast and storing our counterparty's
/// signatures for later reconstruction/rebroadcast of the channel_announcement.
- pub fn announcement_signatures(&mut self, our_node_secret: &SecretKey, our_node_id: PublicKey, chain_hash: BlockHash, msg: &msgs::AnnouncementSignatures) -> Result<msgs::ChannelAnnouncement, ChannelError> {
- let (announcement, our_bitcoin_sig) = self.get_channel_announcement(our_node_id.clone(), chain_hash)?;
+ pub fn announcement_signatures(&mut self, our_node_id: PublicKey, chain_hash: BlockHash, best_block_height: u32, msg: &msgs::AnnouncementSignatures) -> Result<msgs::ChannelAnnouncement, ChannelError> {
+ let announcement = self.get_channel_announcement(our_node_id.clone(), chain_hash)?;
let msghash = hash_to_message!(&Sha256d::hash(&announcement.encode()[..])[..]);
- if self.secp_ctx.verify(&msghash, &msg.node_signature, &self.get_counterparty_node_id()).is_err() {
+ if self.secp_ctx.verify_ecdsa(&msghash, &msg.node_signature, &self.get_counterparty_node_id()).is_err() {
return Err(ChannelError::Close(format!(
"Bad announcement_signatures. Failed to verify node_signature. UnsignedChannelAnnouncement used for verification is {:?}. their_node_key is {:?}",
&announcement, self.get_counterparty_node_id())));
}
- if self.secp_ctx.verify(&msghash, &msg.bitcoin_signature, self.counterparty_funding_pubkey()).is_err() {
+ if self.secp_ctx.verify_ecdsa(&msghash, &msg.bitcoin_signature, self.counterparty_funding_pubkey()).is_err() {
return Err(ChannelError::Close(format!(
"Bad announcement_signatures. Failed to verify bitcoin_signature. UnsignedChannelAnnouncement used for verification is {:?}. their_bitcoin_key is ({:?})",
&announcement, self.counterparty_funding_pubkey())));
}
self.announcement_sigs = Some((msg.node_signature, msg.bitcoin_signature));
+ if self.funding_tx_confirmation_height == 0 || self.funding_tx_confirmation_height + 5 > best_block_height {
+ return Err(ChannelError::Ignore(
+ "Got announcement_signatures prior to the required six confirmations - we may not have received a block yet that our peer has".to_owned()));
+ }
- self.sign_channel_announcement(our_node_secret, our_node_id, msghash, announcement, our_bitcoin_sig)
+ self.sign_channel_announcement(our_node_id, announcement)
}
/// Gets a signed channel_announcement for this channel, if we previously received an
/// announcement_signatures from our counterparty.
- pub fn get_signed_channel_announcement(&self, our_node_secret: &SecretKey, our_node_id: PublicKey, chain_hash: BlockHash) -> Option<msgs::ChannelAnnouncement> {
- let (announcement, our_bitcoin_sig) = match self.get_channel_announcement(our_node_id.clone(), chain_hash) {
+ pub fn get_signed_channel_announcement(&self, our_node_id: PublicKey, chain_hash: BlockHash, best_block_height: u32) -> Option<msgs::ChannelAnnouncement> {
+ if self.funding_tx_confirmation_height == 0 || self.funding_tx_confirmation_height + 5 > best_block_height {
+ return None;
+ }
+ let announcement = match self.get_channel_announcement(our_node_id.clone(), chain_hash) {
Ok(res) => res,
Err(_) => return None,
};
- let msghash = hash_to_message!(&Sha256d::hash(&announcement.encode()[..])[..]);
- match self.sign_channel_announcement(our_node_secret, our_node_id, msghash, announcement, our_bitcoin_sig) {
+ match self.sign_channel_announcement(our_node_id, announcement) {
Ok(res) => Some(res),
Err(_) => None,
}
// Prior to static_remotekey, my_current_per_commitment_point was critical to claiming
// current to_remote balances. However, it no longer has any use, and thus is now simply
// set to a dummy (but valid, as required by the spec) public key.
- // fuzztarget mode marks a subset of pubkeys as invalid so that we can hit "invalid pubkey"
+ // fuzzing mode marks a subset of pubkeys as invalid so that we can hit "invalid pubkey"
// branches, but we unwrap it below, so we arbitrarily select a dummy pubkey which is both
- // valid, and valid in fuzztarget mode's arbitrary validity criteria:
+ // valid, and valid in fuzzing mode's arbitrary validity criteria:
let mut pk = [2; 33]; pk[1] = 0xff;
let dummy_pubkey = PublicKey::from_slice(&pk).unwrap();
let data_loss_protect = if self.cur_counterparty_commitment_transaction_number + 1 < INITIAL_COMMITMENT_NUMBER {
}
}
for htlc in self.pending_outbound_htlcs.iter_mut() {
- if let Some(fail_reason) = if let &mut OutboundHTLCState::AwaitingRemoteRevokeToRemove(ref mut fail_reason) = &mut htlc.state {
- Some(fail_reason.take())
- } else { None } {
+ if let &mut OutboundHTLCState::AwaitingRemoteRevokeToRemove(ref mut outcome) = &mut htlc.state {
log_trace!(logger, " ...promoting outbound AwaitingRemoteRevokeToRemove {} to AwaitingRemovedRemoteRevoke", log_bytes!(htlc.payment_hash.0));
- htlc.state = OutboundHTLCState::AwaitingRemovedRemoteRevoke(fail_reason);
+ // Grab the preimage, if it exists, instead of cloning
+ let mut reason = OutboundHTLCOutcome::Success(None);
+ mem::swap(outcome, &mut reason);
+ htlc.state = OutboundHTLCState::AwaitingRemovedRemoteRevoke(reason);
}
}
if let Some((feerate, update_state)) = self.pending_update_fee {
Err(e) => return Err(e),
};
+ if self.announcement_sigs_state == AnnouncementSigsState::MessageSent {
+ self.announcement_sigs_state = AnnouncementSigsState::Committed;
+ }
+
self.latest_monitor_update_id += 1;
let monitor_update = ChannelMonitorUpdate {
update_id: self.latest_monitor_update_id,
let counterparty_commitment_txid = commitment_stats.tx.trust().txid();
let (signature, htlc_signatures);
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
{
if !self.is_outbound() {
let projected_commit_tx_info = self.next_remote_commitment_tx_fee_info_cached.lock().unwrap().take();
htlcs.push(htlc);
}
- let res = self.holder_signer.sign_counterparty_commitment(&commitment_stats.tx, &self.secp_ctx)
+ let res = self.holder_signer.sign_counterparty_commitment(&commitment_stats.tx, commitment_stats.preimages, &self.secp_ctx)
.map_err(|_| ChannelError::Close("Failed to get signatures for new commitment_signed".to_owned()))?;
signature = res.0;
htlc_signatures = res.1;
}
}
+impl Writeable for AnnouncementSigsState {
+ fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
+ // We only care about writing out the current state as if we had just disconnected, at
+ // which point we always set anything but AnnouncementSigsReceived to NotSent.
+ match self {
+ AnnouncementSigsState::NotSent => 0u8.write(writer),
+ AnnouncementSigsState::MessageSent => 0u8.write(writer),
+ AnnouncementSigsState::Committed => 0u8.write(writer),
+ AnnouncementSigsState::PeerReceived => 1u8.write(writer),
+ }
+ }
+}
+
+impl Readable for AnnouncementSigsState {
+ fn read<R: io::Read>(reader: &mut R) -> Result<Self, DecodeError> {
+ Ok(match <u8 as Readable>::read(reader)? {
+ 0 => AnnouncementSigsState::NotSent,
+ 1 => AnnouncementSigsState::PeerReceived,
+ _ => return Err(DecodeError::InvalidValue),
+ })
+ }
+}
+
impl<Signer: Sign> Writeable for Channel<Signer> {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
// Note that we write out as if remove_uncommitted_htlcs_and_mark_paused had just been
}
}
+ let mut preimages: Vec<&Option<PaymentPreimage>> = vec![];
+
(self.pending_outbound_htlcs.len() as u64).write(writer)?;
for htlc in self.pending_outbound_htlcs.iter() {
htlc.htlc_id.write(writer)?;
// resend the claim/fail on reconnect as we all (hopefully) the missing CS.
1u8.write(writer)?;
},
- &OutboundHTLCState::AwaitingRemoteRevokeToRemove(ref fail_reason) => {
+ &OutboundHTLCState::AwaitingRemoteRevokeToRemove(ref outcome) => {
3u8.write(writer)?;
- fail_reason.write(writer)?;
- },
- &OutboundHTLCState::AwaitingRemovedRemoteRevoke(ref fail_reason) => {
+ if let OutboundHTLCOutcome::Success(preimage) = outcome {
+ preimages.push(preimage);
+ }
+ let reason: Option<&HTLCFailReason> = outcome.into();
+ reason.write(writer)?;
+ }
+ &OutboundHTLCState::AwaitingRemovedRemoteRevoke(ref outcome) => {
4u8.write(writer)?;
- fail_reason.write(writer)?;
- },
+ if let OutboundHTLCOutcome::Success(preimage) = outcome {
+ preimages.push(preimage);
+ }
+ let reason: Option<&HTLCFailReason> = outcome.into();
+ reason.write(writer)?;
+ }
}
}
self.channel_update_status.write(writer)?;
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
(self.historical_inbound_htlc_fulfills.len() as u64).write(writer)?;
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
for htlc in self.historical_inbound_htlc_fulfills.iter() {
htlc.write(writer)?;
}
let chan_type = if self.channel_type != ChannelTypeFeatures::only_static_remote_key() {
Some(&self.channel_type) } else { None };
- // The same logic applies for `holder_selected_channel_reserve_satoshis` and
- // `holder_max_htlc_value_in_flight_msat` values other than the defaults.
+ // The same logic applies for `holder_selected_channel_reserve_satoshis` values other than
+ // the default, and when `holder_max_htlc_value_in_flight_msat` is configured to be set to
+ // a different percentage of the channel value then 10%, which older versions of LDK used
+ // to set it to before the percentage was made configurable.
let serialized_holder_selected_reserve =
if self.holder_selected_channel_reserve_satoshis != Self::get_holder_selected_channel_reserve_satoshis(self.channel_value_satoshis)
{ Some(self.holder_selected_channel_reserve_satoshis) } else { None };
+
+ let mut old_max_in_flight_percent_config = UserConfig::default().own_channel_config;
+ old_max_in_flight_percent_config.max_inbound_htlc_value_in_flight_percent_of_channel = MAX_IN_FLIGHT_PERCENT_LEGACY;
let serialized_holder_htlc_max_in_flight =
- if self.holder_max_htlc_value_in_flight_msat != Self::get_holder_max_htlc_value_in_flight_msat(self.channel_value_satoshis)
+ if self.holder_max_htlc_value_in_flight_msat != Self::get_holder_max_htlc_value_in_flight_msat(self.channel_value_satoshis, &old_max_in_flight_percent_config)
{ Some(self.holder_max_htlc_value_in_flight_msat) } else { None };
write_tlv_fields!(writer, {
(9, self.target_closing_feerate_sats_per_kw, option),
(11, self.monitor_pending_finalized_fulfills, vec_type),
(13, self.channel_creation_height, required),
+ (15, preimages, vec_type),
+ (17, self.announcement_sigs_state, required),
+ (19, self.latest_inbound_scid_alias, option),
+ (21, self.outbound_scid_alias, required),
});
Ok(())
state: match <u8 as Readable>::read(reader)? {
0 => OutboundHTLCState::LocalAnnounced(Box::new(Readable::read(reader)?)),
1 => OutboundHTLCState::Committed,
- 2 => OutboundHTLCState::RemoteRemoved(Readable::read(reader)?),
- 3 => OutboundHTLCState::AwaitingRemoteRevokeToRemove(Readable::read(reader)?),
- 4 => OutboundHTLCState::AwaitingRemovedRemoteRevoke(Readable::read(reader)?),
+ 2 => {
+ let option: Option<HTLCFailReason> = Readable::read(reader)?;
+ OutboundHTLCState::RemoteRemoved(option.into())
+ },
+ 3 => {
+ let option: Option<HTLCFailReason> = Readable::read(reader)?;
+ OutboundHTLCState::AwaitingRemoteRevokeToRemove(option.into())
+ },
+ 4 => {
+ let option: Option<HTLCFailReason> = Readable::read(reader)?;
+ OutboundHTLCState::AwaitingRemovedRemoteRevoke(option.into())
+ },
_ => return Err(DecodeError::InvalidValue),
},
});
let channel_update_status = Readable::read(reader)?;
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
let mut historical_inbound_htlc_fulfills = HashSet::new();
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
{
let htlc_fulfills_len: u64 = Readable::read(reader)?;
for _ in 0..htlc_fulfills_len {
let mut target_closing_feerate_sats_per_kw = None;
let mut monitor_pending_finalized_fulfills = Some(Vec::new());
let mut holder_selected_channel_reserve_satoshis = Some(Self::get_holder_selected_channel_reserve_satoshis(channel_value_satoshis));
- let mut holder_max_htlc_value_in_flight_msat = Some(Self::get_holder_max_htlc_value_in_flight_msat(channel_value_satoshis));
+ let mut holder_max_htlc_value_in_flight_msat = Some(Self::get_holder_max_htlc_value_in_flight_msat(channel_value_satoshis, &UserConfig::default().own_channel_config));
// Prior to supporting channel type negotiation, all of our channels were static_remotekey
// only, so we default to that if none was written.
let mut channel_type = Some(ChannelTypeFeatures::only_static_remote_key());
let mut channel_creation_height = Some(serialized_height);
+ let mut preimages_opt: Option<Vec<Option<PaymentPreimage>>> = None;
+
+ // If we read an old Channel, for simplicity we just treat it as "we never sent an
+ // AnnouncementSignatures" which implies we'll re-send it on reconnect, but that's fine.
+ let mut announcement_sigs_state = Some(AnnouncementSigsState::NotSent);
+ let mut latest_inbound_scid_alias = None;
+ let mut outbound_scid_alias = None;
+
read_tlv_fields!(reader, {
(0, announcement_sigs, option),
(1, minimum_depth, option),
(9, target_closing_feerate_sats_per_kw, option),
(11, monitor_pending_finalized_fulfills, vec_type),
(13, channel_creation_height, option),
+ (15, preimages_opt, vec_type),
+ (17, announcement_sigs_state, option),
+ (19, latest_inbound_scid_alias, option),
+ (21, outbound_scid_alias, option),
});
+ if let Some(preimages) = preimages_opt {
+ let mut iter = preimages.into_iter();
+ for htlc in pending_outbound_htlcs.iter_mut() {
+ match &htlc.state {
+ OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Success(None)) => {
+ htlc.state = OutboundHTLCState::AwaitingRemoteRevokeToRemove(OutboundHTLCOutcome::Success(iter.next().ok_or(DecodeError::InvalidValue)?));
+ }
+ OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Success(None)) => {
+ htlc.state = OutboundHTLCState::AwaitingRemovedRemoteRevoke(OutboundHTLCOutcome::Success(iter.next().ok_or(DecodeError::InvalidValue)?));
+ }
+ _ => {}
+ }
+ }
+ // We expect all preimages to be consumed above
+ if iter.next().is_some() {
+ return Err(DecodeError::InvalidValue);
+ }
+ }
+
let chan_features = channel_type.as_ref().unwrap();
if chan_features.supports_unknown_bits() || chan_features.requires_unknown_bits() {
// If the channel was written by a new version and negotiated with features we don't
user_id,
config: config.unwrap(),
+
+ // Note that we don't care about serializing handshake limits as we only ever serialize
+ // channel data after the handshake has completed.
+ inbound_handshake_limits_override: None,
+
channel_id,
channel_state,
+ announcement_sigs_state: announcement_sigs_state.unwrap(),
secp_ctx,
channel_value_satoshis,
closing_fee_limits: None,
target_closing_feerate_sats_per_kw,
+ inbound_awaiting_accept: false,
+
funding_tx_confirmed_in,
funding_tx_confirmation_height,
short_channel_id,
announcement_sigs,
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
next_local_commitment_tx_fee_info_cached: Mutex::new(None),
- #[cfg(any(test, feature = "fuzztarget"))]
+ #[cfg(any(test, fuzzing))]
next_remote_commitment_tx_fee_info_cached: Mutex::new(None),
workaround_lnd_bug_4006: None,
- #[cfg(any(test, feature = "fuzztarget"))]
+ latest_inbound_scid_alias,
+ // Later in the ChannelManager deserialization phase we scan for channels and assign scid aliases if its missing
+ outbound_scid_alias: outbound_scid_alias.unwrap_or(0),
+
+ #[cfg(any(test, fuzzing))]
historical_inbound_htlc_fulfills,
channel_type: channel_type.unwrap(),
#[cfg(test)]
mod tests {
- use bitcoin::util::bip143;
- use bitcoin::consensus::encode::serialize;
use bitcoin::blockdata::script::{Script, Builder};
- use bitcoin::blockdata::transaction::{Transaction, TxOut, SigHashType};
+ use bitcoin::blockdata::transaction::{Transaction, TxOut};
use bitcoin::blockdata::constants::genesis_block;
use bitcoin::blockdata::opcodes;
use bitcoin::network::constants::Network;
- use bitcoin::hashes::hex::FromHex;
use hex;
- use ln::{PaymentPreimage, PaymentHash};
+ use ln::PaymentHash;
use ln::channelmanager::{HTLCSource, PaymentId};
- use ln::channel::{Channel,InboundHTLCOutput,OutboundHTLCOutput,InboundHTLCState,OutboundHTLCState,HTLCOutputInCommitment,HTLCCandidate,HTLCInitiator,TxCreationKeys};
- use ln::channel::MAX_FUNDING_SATOSHIS;
+ use ln::channel::{Channel, InboundHTLCOutput, OutboundHTLCOutput, InboundHTLCState, OutboundHTLCState, HTLCCandidate, HTLCInitiator};
+ use ln::channel::{MAX_FUNDING_SATOSHIS_NO_WUMBO, TOTAL_BITCOIN_SUPPLY_SATOSHIS};
use ln::features::InitFeatures;
use ln::msgs::{ChannelUpdate, DataLossProtect, DecodeError, OptionalField, UnsignedChannelUpdate};
use ln::script::ShutdownScript;
use ln::chan_utils;
- use ln::chan_utils::{ChannelPublicKeys, HolderCommitmentTransaction, CounterpartyChannelTransactionParameters, htlc_success_tx_weight, htlc_timeout_tx_weight};
+ use ln::chan_utils::{htlc_success_tx_weight, htlc_timeout_tx_weight};
use chain::BestBlock;
use chain::chaininterface::{FeeEstimator,ConfirmationTarget};
- use chain::keysinterface::{InMemorySigner, KeyMaterial, KeysInterface, BaseSign};
+ use chain::keysinterface::{InMemorySigner, Recipient, KeyMaterial, KeysInterface};
use chain::transaction::OutPoint;
use util::config::UserConfig;
use util::enforcing_trait_impls::EnforcingSigner;
use util::errors::APIError;
use util::test_utils;
use util::test_utils::OnGetShutdownScriptpubkey;
- use util::logger::Logger;
- use bitcoin::secp256k1::{Secp256k1, Message, Signature, All};
+ use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature};
use bitcoin::secp256k1::ffi::Signature as FFISignature;
- use bitcoin::secp256k1::key::{SecretKey,PublicKey};
- use bitcoin::secp256k1::recovery::RecoverableSignature;
+ use bitcoin::secp256k1::{SecretKey,PublicKey};
+ use bitcoin::secp256k1::ecdsa::RecoverableSignature;
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::Hash;
- use bitcoin::hash_types::{Txid, WPubkeyHash};
- use core::num::NonZeroU8;
+ use bitcoin::hash_types::WPubkeyHash;
use bitcoin::bech32::u5;
- use sync::Arc;
+ use bitcoin::util::address::WitnessVersion;
use prelude::*;
struct TestFeeEstimator {
}
#[test]
- fn test_max_funding_satoshis() {
- assert!(MAX_FUNDING_SATOSHIS <= 21_000_000 * 100_000_000,
- "MAX_FUNDING_SATOSHIS is greater than all satoshis in existence");
+ fn test_max_funding_satoshis_no_wumbo() {
+ assert_eq!(TOTAL_BITCOIN_SUPPLY_SATOSHIS, 21_000_000 * 100_000_000);
+ assert!(MAX_FUNDING_SATOSHIS_NO_WUMBO <= TOTAL_BITCOIN_SUPPLY_SATOSHIS,
+ "MAX_FUNDING_SATOSHIS_NO_WUMBO is greater than all satoshis in existence");
+ }
+
+ #[test]
+ fn test_no_fee_check_overflow() {
+ // Previously, calling `check_remote_fee` with a fee of 0xffffffff would overflow in
+ // arithmetic, causing a panic with debug assertions enabled.
+ assert!(Channel::<InMemorySigner>::check_remote_fee(&&TestFeeEstimator { fee_est: 42 }, u32::max_value()).is_err());
}
struct Keys {
impl KeysInterface for Keys {
type Signer = InMemorySigner;
- fn get_node_secret(&self) -> SecretKey { panic!(); }
+ fn get_node_secret(&self, _recipient: Recipient) -> Result<SecretKey, ()> { panic!(); }
fn get_inbound_payment_key_material(&self) -> KeyMaterial { panic!(); }
fn get_destination_script(&self) -> Script {
let secp_ctx = Secp256k1::signing_only();
}
fn get_secure_random_bytes(&self) -> [u8; 32] { [0; 32] }
fn read_chan_signer(&self, _data: &[u8]) -> Result<Self::Signer, DecodeError> { panic!(); }
- fn sign_invoice(&self, _hrp_bytes: &[u8], _invoice_data: &[u5]) -> Result<RecoverableSignature, ()> { panic!(); }
+ fn sign_invoice(&self, _hrp_bytes: &[u8], _invoice_data: &[u5], _recipient: Recipient) -> Result<RecoverableSignature, ()> { panic!(); }
}
- fn public_from_secret_hex(secp_ctx: &Secp256k1<All>, hex: &str) -> PublicKey {
+ #[cfg(not(feature = "grind_signatures"))]
+ fn public_from_secret_hex(secp_ctx: &Secp256k1<bitcoin::secp256k1::All>, hex: &str) -> PublicKey {
PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&hex::decode(hex).unwrap()[..]).unwrap())
}
fn upfront_shutdown_script_incompatibility() {
let features = InitFeatures::known().clear_shutdown_anysegwit();
let non_v0_segwit_shutdown_script =
- ShutdownScript::new_witness_program(NonZeroU8::new(16).unwrap(), &[0, 40]).unwrap();
+ ShutdownScript::new_witness_program(WitnessVersion::V16, &[0, 40]).unwrap();
let seed = [42; 32];
let network = Network::Testnet;
let secp_ctx = Secp256k1::new();
let node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let config = UserConfig::default();
- match Channel::<EnforcingSigner>::new_outbound(&&fee_estimator, &&keys_provider, node_id, &features, 10000000, 100000, 42, &config, 0) {
+ match Channel::<EnforcingSigner>::new_outbound(&&fee_estimator, &&keys_provider, node_id, &features, 10000000, 100000, 42, &config, 0, 42) {
Err(APIError::IncompatibleShutdownScript { script }) => {
assert_eq!(script.into_inner(), non_v0_segwit_shutdown_script.into_inner());
},
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::<EnforcingSigner>::new_outbound(&&fee_est, &&keys_provider, node_a_node_id, &InitFeatures::known(), 10000000, 100000, 42, &config, 0).unwrap();
+ let node_a_chan = Channel::<EnforcingSigner>::new_outbound(&&fee_est, &&keys_provider, node_a_node_id, &InitFeatures::known(), 10000000, 100000, 42, &config, 0, 42).unwrap();
// Now change the fee so we can check that the fee in the open_channel message is the
// same as the old fee.
// 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::<EnforcingSigner>::new_outbound(&&feeest, &&keys_provider, node_b_node_id, &InitFeatures::known(), 10000000, 100000, 42, &config, 0).unwrap();
+ let mut node_a_chan = Channel::<EnforcingSigner>::new_outbound(&&feeest, &&keys_provider, node_b_node_id, &InitFeatures::known(), 10000000, 100000, 42, &config, 0, 42).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());
let node_b_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap());
- let node_b_chan = Channel::<EnforcingSigner>::new_from_req(&&feeest, &&keys_provider, node_b_node_id, &InitFeatures::known(), &open_channel_msg, 7, &config, 0, &&logger).unwrap();
+ let mut node_b_chan = Channel::<EnforcingSigner>::new_from_req(&&feeest, &&keys_provider, node_b_node_id, &InitFeatures::known(), &open_channel_msg, 7, &config, 0, &&logger, 42).unwrap();
// Node B --> Node A: accept channel, explicitly setting B's dust limit.
- let mut accept_channel_msg = node_b_chan.get_accept_channel();
+ let mut accept_channel_msg = node_b_chan.accept_inbound_channel(0);
accept_channel_msg.dust_limit_satoshis = 546;
- node_a_chan.accept_channel(&accept_channel_msg, &config, &InitFeatures::known()).unwrap();
+ node_a_chan.accept_channel(&accept_channel_msg, &config.peer_channel_config_limits, &InitFeatures::known()).unwrap();
node_a_chan.holder_dust_limit_satoshis = 1560;
// Put some inbound and outbound HTLCs in A's channel.
let node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
let config = UserConfig::default();
- let mut chan = Channel::<EnforcingSigner>::new_outbound(&&fee_est, &&keys_provider, node_id, &InitFeatures::known(), 10000000, 100000, 42, &config, 0).unwrap();
+ let mut chan = Channel::<EnforcingSigner>::new_outbound(&&fee_est, &&keys_provider, node_id, &InitFeatures::known(), 10000000, 100000, 42, &config, 0, 42).unwrap();
let commitment_tx_fee_0_htlcs = Channel::<EnforcingSigner>::commit_tx_fee_msat(chan.feerate_per_kw, 0, chan.opt_anchors());
let commitment_tx_fee_1_htlc = Channel::<EnforcingSigner>::commit_tx_fee_msat(chan.feerate_per_kw, 1, chan.opt_anchors());
// 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::<EnforcingSigner>::new_outbound(&&feeest, &&keys_provider, node_b_node_id, &InitFeatures::known(), 10000000, 100000, 42, &config, 0).unwrap();
+ let mut node_a_chan = Channel::<EnforcingSigner>::new_outbound(&&feeest, &&keys_provider, node_b_node_id, &InitFeatures::known(), 10000000, 100000, 42, &config, 0, 42).unwrap();
// Create Node B's channel by receiving Node A's open_channel message
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::<EnforcingSigner>::new_from_req(&&feeest, &&keys_provider, node_b_node_id, &InitFeatures::known(), &open_channel_msg, 7, &config, 0, &&logger).unwrap();
+ let mut node_b_chan = Channel::<EnforcingSigner>::new_from_req(&&feeest, &&keys_provider, node_b_node_id, &InitFeatures::known(), &open_channel_msg, 7, &config, 0, &&logger, 42).unwrap();
// Node B --> Node A: accept channel
- let accept_channel_msg = node_b_chan.get_accept_channel();
- node_a_chan.accept_channel(&accept_channel_msg, &config, &InitFeatures::known()).unwrap();
+ let accept_channel_msg = node_b_chan.accept_inbound_channel(0);
+ node_a_chan.accept_channel(&accept_channel_msg, &config.peer_channel_config_limits, &InitFeatures::known()).unwrap();
// Node A --> Node B: funding created
let output_script = node_a_chan.get_funding_redeemscript();
}
}
+ #[test]
+ fn test_configured_holder_max_htlc_value_in_flight() {
+ let feeest = TestFeeEstimator{fee_est: 15000};
+ let logger = test_utils::TestLogger::new();
+ let secp_ctx = Secp256k1::new();
+ let seed = [42; 32];
+ let network = Network::Testnet;
+ let keys_provider = test_utils::TestKeysInterface::new(&seed, network);
+ let outbound_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
+ let inbound_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap());
+
+ let mut config_2_percent = UserConfig::default();
+ config_2_percent.own_channel_config.max_inbound_htlc_value_in_flight_percent_of_channel = 2;
+ let mut config_99_percent = UserConfig::default();
+ config_99_percent.own_channel_config.max_inbound_htlc_value_in_flight_percent_of_channel = 99;
+ let mut config_0_percent = UserConfig::default();
+ config_0_percent.own_channel_config.max_inbound_htlc_value_in_flight_percent_of_channel = 0;
+ let mut config_101_percent = UserConfig::default();
+ config_101_percent.own_channel_config.max_inbound_htlc_value_in_flight_percent_of_channel = 101;
+
+ // Test that `new_outbound` creates a channel with the correct value for
+ // `holder_max_htlc_value_in_flight_msat`, when configured with a valid percentage value,
+ // which is set to the lower bound + 1 (2%) of the `channel_value`.
+ let chan_1 = Channel::<EnforcingSigner>::new_outbound(&&feeest, &&keys_provider, outbound_node_id, &InitFeatures::known(), 10000000, 100000, 42, &config_2_percent, 0, 42).unwrap();
+ let chan_1_value_msat = chan_1.channel_value_satoshis * 1000;
+ assert_eq!(chan_1.holder_max_htlc_value_in_flight_msat, (chan_1_value_msat as f64 * 0.02) as u64);
+
+ // Test with the upper bound - 1 of valid values (99%).
+ let chan_2 = Channel::<EnforcingSigner>::new_outbound(&&feeest, &&keys_provider, outbound_node_id, &InitFeatures::known(), 10000000, 100000, 42, &config_99_percent, 0, 42).unwrap();
+ let chan_2_value_msat = chan_2.channel_value_satoshis * 1000;
+ assert_eq!(chan_2.holder_max_htlc_value_in_flight_msat, (chan_2_value_msat as f64 * 0.99) as u64);
+
+ let chan_1_open_channel_msg = chan_1.get_open_channel(genesis_block(network).header.block_hash());
+
+ // Test that `new_from_req` creates a channel with the correct value for
+ // `holder_max_htlc_value_in_flight_msat`, when configured with a valid percentage value,
+ // which is set to the lower bound - 1 (2%) of the `channel_value`.
+ let chan_3 = Channel::<EnforcingSigner>::new_from_req(&&feeest, &&keys_provider, inbound_node_id, &InitFeatures::known(), &chan_1_open_channel_msg, 7, &config_2_percent, 0, &&logger, 42).unwrap();
+ let chan_3_value_msat = chan_3.channel_value_satoshis * 1000;
+ assert_eq!(chan_3.holder_max_htlc_value_in_flight_msat, (chan_3_value_msat as f64 * 0.02) as u64);
+
+ // Test with the upper bound - 1 of valid values (99%).
+ let chan_4 = Channel::<EnforcingSigner>::new_from_req(&&feeest, &&keys_provider, inbound_node_id, &InitFeatures::known(), &chan_1_open_channel_msg, 7, &config_99_percent, 0, &&logger, 42).unwrap();
+ let chan_4_value_msat = chan_4.channel_value_satoshis * 1000;
+ assert_eq!(chan_4.holder_max_htlc_value_in_flight_msat, (chan_4_value_msat as f64 * 0.99) as u64);
+
+ // Test that `new_outbound` uses the lower bound of the configurable percentage values (1%)
+ // if `max_inbound_htlc_value_in_flight_percent_of_channel` is set to a value less than 1.
+ let chan_5 = Channel::<EnforcingSigner>::new_outbound(&&feeest, &&keys_provider, outbound_node_id, &InitFeatures::known(), 10000000, 100000, 42, &config_0_percent, 0, 42).unwrap();
+ let chan_5_value_msat = chan_5.channel_value_satoshis * 1000;
+ assert_eq!(chan_5.holder_max_htlc_value_in_flight_msat, (chan_5_value_msat as f64 * 0.01) as u64);
+
+ // Test that `new_outbound` uses the upper bound of the configurable percentage values
+ // (100%) if `max_inbound_htlc_value_in_flight_percent_of_channel` is set to a larger value
+ // than 100.
+ let chan_6 = Channel::<EnforcingSigner>::new_outbound(&&feeest, &&keys_provider, outbound_node_id, &InitFeatures::known(), 10000000, 100000, 42, &config_101_percent, 0, 42).unwrap();
+ let chan_6_value_msat = chan_6.channel_value_satoshis * 1000;
+ assert_eq!(chan_6.holder_max_htlc_value_in_flight_msat, chan_6_value_msat);
+
+ // Test that `new_from_req` uses the lower bound of the configurable percentage values (1%)
+ // if `max_inbound_htlc_value_in_flight_percent_of_channel` is set to a value less than 1.
+ let chan_7 = Channel::<EnforcingSigner>::new_from_req(&&feeest, &&keys_provider, inbound_node_id, &InitFeatures::known(), &chan_1_open_channel_msg, 7, &config_0_percent, 0, &&logger, 42).unwrap();
+ let chan_7_value_msat = chan_7.channel_value_satoshis * 1000;
+ assert_eq!(chan_7.holder_max_htlc_value_in_flight_msat, (chan_7_value_msat as f64 * 0.01) as u64);
+
+ // Test that `new_from_req` uses the upper bound of the configurable percentage values
+ // (100%) if `max_inbound_htlc_value_in_flight_percent_of_channel` is set to a larger value
+ // than 100.
+ let chan_8 = Channel::<EnforcingSigner>::new_from_req(&&feeest, &&keys_provider, inbound_node_id, &InitFeatures::known(), &chan_1_open_channel_msg, 7, &config_101_percent, 0, &&logger, 42).unwrap();
+ let chan_8_value_msat = chan_8.channel_value_satoshis * 1000;
+ assert_eq!(chan_8.holder_max_htlc_value_in_flight_msat, chan_8_value_msat);
+ }
+
#[test]
fn channel_update() {
let feeest = TestFeeEstimator{fee_est: 15000};
// 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::<EnforcingSigner>::new_outbound(&&feeest, &&keys_provider, node_b_node_id, &InitFeatures::known(), 10000000, 100000, 42, &config, 0).unwrap();
+ let mut node_a_chan = Channel::<EnforcingSigner>::new_outbound(&&feeest, &&keys_provider, node_b_node_id, &InitFeatures::known(), 10000000, 100000, 42, &config, 0, 42).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());
}
}
+ #[cfg(not(feature = "grind_signatures"))]
#[test]
fn outbound_commitment_test() {
+ use bitcoin::util::sighash;
+ use bitcoin::consensus::encode::serialize;
+ use bitcoin::blockdata::transaction::EcdsaSighashType;
+ use bitcoin::hashes::hex::FromHex;
+ use bitcoin::hash_types::Txid;
+ use bitcoin::secp256k1::Message;
+ use chain::keysinterface::BaseSign;
+ use ln::PaymentPreimage;
+ use ln::channel::{HTLCOutputInCommitment ,TxCreationKeys};
+ use ln::chan_utils::{ChannelPublicKeys, HolderCommitmentTransaction, CounterpartyChannelTransactionParameters};
+ use util::logger::Logger;
+ use sync::Arc;
+
// Test vectors from BOLT 3 Appendices C and F (anchors):
let feeest = TestFeeEstimator{fee_est: 15000};
let logger : Arc<Logger> = Arc::new(test_utils::TestLogger::new());
let mut signer = InMemorySigner::new(
&secp_ctx,
+ SecretKey::from_slice(&hex::decode("4242424242424242424242424242424242424242424242424242424242424242").unwrap()[..]).unwrap(),
SecretKey::from_slice(&hex::decode("30ff4956bbdd3222d44cc5e8a1261dab1e07957bdac5ae88fe3261ef321f3749").unwrap()[..]).unwrap(),
SecretKey::from_slice(&hex::decode("0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap()[..]).unwrap(),
SecretKey::from_slice(&hex::decode("1111111111111111111111111111111111111111111111111111111111111111").unwrap()[..]).unwrap(),
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::<InMemorySigner>::new_outbound(&&feeest, &&keys_provider, counterparty_node_id, &InitFeatures::known(), 10_000_000, 100000, 42, &config, 0).unwrap(); // Nothing uses their network key in this test
+ let mut chan = Channel::<InMemorySigner>::new_outbound(&&feeest, &&keys_provider, counterparty_node_id, &InitFeatures::known(), 10_000_000, 100000, 42, &config, 0, 42).unwrap(); // Nothing uses their network key in this test
chan.holder_dust_limit_satoshis = 546;
chan.counterparty_selected_channel_reserve_satoshis = Some(0); // Filled in in accept_channel
let counterparty_signature = Signature::from_der(&hex::decode($counterparty_sig_hex).unwrap()[..]).unwrap();
let sighash = unsigned_tx.get_sighash_all(&redeemscript, chan.channel_value_satoshis);
log_trace!(logger, "unsigned_tx = {}", hex::encode(serialize(&unsigned_tx.transaction)));
- assert!(secp_ctx.verify(&sighash, &counterparty_signature, chan.counterparty_funding_pubkey()).is_ok(), "verify counterparty commitment sig");
+ assert!(secp_ctx.verify_ecdsa(&sighash, &counterparty_signature, chan.counterparty_funding_pubkey()).is_ok(), "verify counterparty commitment sig");
let mut per_htlc: Vec<(HTLCOutputInCommitment, Option<Signature>)> = Vec::new();
per_htlc.clear(); // Don't warn about excess mut for no-HTLC calls
chan.get_counterparty_selected_contest_delay().unwrap(),
&htlc, $opt_anchors, &keys.broadcaster_delayed_payment_key, &keys.revocation_key);
let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, $opt_anchors, &keys);
- let htlc_sighashtype = if $opt_anchors { SigHashType::SinglePlusAnyoneCanPay } else { SigHashType::All };
- let htlc_sighash = Message::from_slice(&bip143::SigHashCache::new(&htlc_tx).signature_hash(0, &htlc_redeemscript, htlc.amount_msat / 1000, htlc_sighashtype)[..]).unwrap();
- assert!(secp_ctx.verify(&htlc_sighash, &remote_signature, &keys.countersignatory_htlc_key).is_ok(), "verify counterparty htlc sig");
+ let htlc_sighashtype = if $opt_anchors { EcdsaSighashType::SinglePlusAnyoneCanPay } else { EcdsaSighashType::All };
+ let htlc_sighash = Message::from_slice(&sighash::SighashCache::new(&htlc_tx).segwit_signature_hash(0, &htlc_redeemscript, htlc.amount_msat / 1000, htlc_sighashtype).unwrap()[..]).unwrap();
+ assert!(secp_ctx.verify_ecdsa(&htlc_sighash, &remote_signature, &keys.countersignatory_htlc_key).is_ok(), "verify counterparty htlc sig");
let mut preimage: Option<PaymentPreimage> = None;
if !htlc.offered {