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,
#[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),
})
}
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 {
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);
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)
}
/// 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(
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
}
/// should be sent back to the counterparty node.
///
/// [`msgs::AcceptChannel`]: crate::ln::msgs::AcceptChannel
- pub fn accept_inbound_channel(&mut self) -> 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?");
}
panic!("The inbound channel has already been accepted");
}
+ self.user_id = user_id;
self.inbound_awaiting_accept = false;
self.generate_accept_channel_message()
#[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::{Channel, InboundHTLCOutput, OutboundHTLCOutput, InboundHTLCState, OutboundHTLCState, HTLCCandidate, HTLCInitiator};
use ln::channel::MAX_FUNDING_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, Recipient, 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, Signature};
use bitcoin::secp256k1::ffi::Signature as FFISignature;
use bitcoin::secp256k1::key::{SecretKey,PublicKey};
use bitcoin::secp256k1::recovery::RecoverableSignature;
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::hashes::Hash;
- use bitcoin::hash_types::{Txid, WPubkeyHash};
+ use bitcoin::hash_types::WPubkeyHash;
use core::num::NonZeroU8;
use bitcoin::bech32::u5;
- use sync::Arc;
use prelude::*;
struct TestFeeEstimator {
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())
}
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.accept_inbound_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.peer_channel_config_limits, &InitFeatures::known()).unwrap();
node_a_chan.holder_dust_limit_satoshis = 1560;
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.accept_inbound_channel();
+ 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
}
}
+ #[cfg(not(feature = "grind_signatures"))]
#[test]
fn outbound_commitment_test() {
+ use bitcoin::util::bip143;
+ use bitcoin::consensus::encode::serialize;
+ use bitcoin::blockdata::transaction::SigHashType;
+ 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());