From 86c84edaf5fd02346c06839b042e499e40c76cd1 Mon Sep 17 00:00:00 2001 From: Schalk van Heerden Date: Fri, 14 Sep 2018 11:27:47 +0200 Subject: [PATCH] Added config file to allow users to specify channel limits as per bolt 2 --- fuzz/fuzz_targets/full_stack_target.rs | 7 +++- src/ln/channel.rs | 53 +++++++++++++++---------- src/ln/channelmanager.rs | 29 +++++++------- src/ln/router.rs | 2 - src/util/configurations.rs | 55 ++++++++++++++++++++++---- src/util/mod.rs | 4 +- 6 files changed, 103 insertions(+), 47 deletions(-) diff --git a/fuzz/fuzz_targets/full_stack_target.rs b/fuzz/fuzz_targets/full_stack_target.rs index c74201e5f..2f3a660c2 100644 --- a/fuzz/fuzz_targets/full_stack_target.rs +++ b/fuzz/fuzz_targets/full_stack_target.rs @@ -22,6 +22,7 @@ use lightning::util::events::{EventsProvider,Event}; use lightning::util::reset_rng_state; use lightning::util::logger::Logger; use lightning::util::sha2::Sha256; +use lightning::util::UserConfigurations; mod utils; @@ -235,8 +236,10 @@ pub fn do_test(data: &[u8], logger: &Arc) { let watch = Arc::new(ChainWatchInterfaceUtil::new(Network::Bitcoin, Arc::clone(&logger))); let broadcast = Arc::new(TestBroadcaster{}); let monitor = channelmonitor::SimpleManyChannelMonitor::new(watch.clone(), broadcast.clone()); - - let channelmanager = ChannelManager::new(our_network_key, slice_to_be32(get_slice!(4)), get_slice!(1)[0] != 0, Network::Bitcoin, fee_est.clone(), monitor.clone(), watch.clone(), broadcast.clone(), Arc::clone(&logger)).unwrap(); + let mut config = UserConfigurations::new(); + config.channel_options.fee_proportional_millionths = slice_to_be32(get_slice!(4)); + config.channel_options.announced_channel = (get_slice!(1)[0] != 0); + let channelmanager = ChannelManager::new(our_network_key,Network::Bitcoin, fee_est.clone(), monitor.clone(), watch.clone(), broadcast.clone(), Arc::clone(&logger), config).unwrap(); let router = Arc::new(Router::new(PublicKey::from_secret_key(&secp_ctx, &our_network_key), watch.clone(), Arc::clone(&logger))); let peers = RefCell::new([false; 256]); diff --git a/src/ln/channel.rs b/src/ln/channel.rs index e7c395e3d..b40b17fde 100644 --- a/src/ln/channel.rs +++ b/src/ln/channel.rs @@ -26,7 +26,7 @@ use util::ser::Writeable; use util::sha2::Sha256; use util::logger::Logger; use util::errors::APIError; -use util::configurations::UserConfigurations; +use util::configurations::{UserConfigurations,ChannelLimits,ChannelOptions}; use std; use std::default::Default; @@ -266,15 +266,14 @@ const INITIAL_COMMITMENT_NUMBER: u64 = (1 << 48) - 1; // calling channel_id() before we're set up or things like get_outbound_funding_signed on an // inbound channel. pub(super) struct Channel { + config : UserConfigurations, - config : UserConfigurations, user_id: u64, channel_id: [u8; 32], channel_state: u32, channel_outbound: bool, secp_ctx: Secp256k1, - announce_publicly: bool, channel_value_satoshis: u64, local_keys: ChannelKeys, @@ -407,7 +406,7 @@ impl Channel { } // Constructors: - pub fn new_outbound(fee_estimator: &FeeEstimator, chan_keys: ChannelKeys, their_node_id: PublicKey, channel_value_satoshis: u64, push_msat: u64, announce_publicly: bool, user_id: u64, logger: Arc, configurations: &UserConfigurations) -> Result { + pub fn new_outbound(fee_estimator: &FeeEstimator, chan_keys: ChannelKeys, their_node_id: PublicKey, channel_value_satoshis: u64, push_msat: u64, user_id: u64, logger: Arc, configurations: &UserConfigurations) -> Result { if channel_value_satoshis >= MAX_FUNDING_SATOSHIS { return Err(APIError::APIMisuseError{err: "funding value > 2^24"}); } @@ -434,12 +433,14 @@ impl Channel { Ok(Channel { user_id: user_id, - config : configurations.clone(), + config: UserConfigurations{ + channel_options: configurations.channel_options.clone(), + channel_limits : Arc::clone(&configurations.channel_limits),}, + channel_id: rng::rand_u832(), channel_state: ChannelState::OurInitSent as u32, channel_outbound: true, secp_ctx: secp_ctx, - announce_publicly: announce_publicly, channel_value_satoshis: channel_value_satoshis, local_keys: chan_keys, @@ -504,12 +505,13 @@ impl Channel { /// Assumes chain_hash has already been checked and corresponds with what we expect! /// Generally prefers to take the DisconnectPeer action on failure, as a notice to the sender /// that we're rejecting the new channel. - pub fn new_from_req(fee_estimator: &FeeEstimator, chan_keys: ChannelKeys, their_node_id: PublicKey, msg: &msgs::OpenChannel, user_id: u64, require_announce: bool, allow_announce: bool, logger: Arc, configurations : &UserConfigurations) -> Result { + pub fn new_from_req(fee_estimator: &FeeEstimator, chan_keys: ChannelKeys, their_node_id: PublicKey, msg: &msgs::OpenChannel, user_id: u64, logger: Arc, configurations : &UserConfigurations) -> Result { macro_rules! return_error_message { ( $msg: expr ) => { return Err(HandleError{err: $msg, action: Some(msgs::ErrorAction::SendErrorMessage{ msg: msgs::ErrorMessage { channel_id: msg.temporary_channel_id, data: $msg.to_string() }})}); } } + let mut local_config = (*configurations).channel_options.clone(); // Check sanity of message fields: if msg.funding_satoshis >= MAX_FUNDING_SATOSHIS { @@ -543,7 +545,7 @@ impl Channel { if msg.max_accepted_htlcs > 483 { return_error_message!("max_accpted_htlcs > 483"); } - //optional parameter checking + //optional parameter checking // MAY fail the channel if if msg.funding_satoshis < configurations.channel_limits.funding_satoshis { return_error_message!("funding satoshis is less than the user specified limit"); @@ -567,12 +569,16 @@ impl Channel { // Convert things into internal flags and prep our state: let their_announce = if (msg.channel_flags & 1) == 1 { true } else { false }; - if require_announce && !their_announce { - return_error_message!("Peer tried to open unannounced channel, but we require public ones"); - } - if !allow_announce && their_announce { - return_error_message!("Peer tried to open announced channel, but we require private ones"); + if local_config.force_announced_channel_preference{ + if local_config.announced_channel && !their_announce { + return_error_message!("Peer tried to open unannounced channel, but we require public ones"); + } + if !local_config.announced_channel && their_announce { + return_error_message!("Peer tried to open announced channel, but we require private ones"); + } } + //we either accept their preference or the preferences match + local_config.announced_channel = their_announce; let background_feerate = fee_estimator.get_est_sat_per_1000_weight(ConfirmationTarget::Background); @@ -613,12 +619,14 @@ impl Channel { let mut chan = Channel { user_id: user_id, - config: (*configurations).clone(), + config: UserConfigurations{ + channel_options: local_config, + channel_limits : Arc::clone(&configurations.channel_limits),}, + channel_id: msg.temporary_channel_id, channel_state: (ChannelState::OurInitSent as u32) | (ChannelState::TheirInitSent as u32), channel_outbound: false, secp_ctx: secp_ctx, - announce_publicly: their_announce, local_keys: chan_keys, cur_local_commitment_transaction_number: INITIAL_COMMITMENT_NUMBER, @@ -1268,7 +1276,10 @@ impl Channel { if msg.max_accepted_htlcs > 483 { return_error_message!("max_accpted_htlcs > 483"); } - + //Optional user definined limits + if msg.minimum_depth > self.config.channel_limits.minimum_depth { + return_error_message!("We consider the minimum depth to be unreasonably large"); + } self.channel_monitor.set_their_base_keys(&msg.htlc_basepoint, &msg.delayed_payment_basepoint); self.their_dust_limit_satoshis = msg.dust_limit_satoshis; @@ -2257,7 +2268,7 @@ impl Channel { } pub fn should_announce(&self) -> bool { - self.announce_publicly + self.config.channel_options.announced_channel } /// Gets the fee we'd want to charge for adding an HTLC output to this Channel @@ -2443,7 +2454,7 @@ impl Channel { delayed_payment_basepoint: PublicKey::from_secret_key(&self.secp_ctx, &self.local_keys.delayed_payment_base_key), htlc_basepoint: PublicKey::from_secret_key(&self.secp_ctx, &self.local_keys.htlc_base_key), first_per_commitment_point: PublicKey::from_secret_key(&self.secp_ctx, &local_commitment_secret), - channel_flags: if self.announce_publicly {1} else {0}, + channel_flags: if self.config.channel_options.announced_channel {1} else {0}, shutdown_scriptpubkey: None, } } @@ -2547,7 +2558,7 @@ impl Channel { /// Note that the "channel must be funded" requirement is stricter than BOLT 7 requires - see /// https://github.com/lightningnetwork/lightning-rfc/issues/468 pub fn get_channel_announcement(&self, our_node_id: PublicKey, chain_hash: Sha256dHash) -> Result<(msgs::UnsignedChannelAnnouncement, Signature), HandleError> { - if !self.announce_publicly { + if !self.config.channel_options.announced_channel { return Err(HandleError{err: "Channel is not available for public announcements", action: Some(msgs::ErrorAction::IgnoreError)}); } if self.channel_state & (ChannelState::ChannelFunded as u32) == 0 { @@ -2909,7 +2920,9 @@ mod tests { hex::decode("023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb").unwrap()[..]); let their_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&secp_ctx, &[42; 32]).unwrap()); - let mut chan = Channel::new_outbound(&feeest, chan_keys, their_node_id, 10000000, 100000, false, 42, Arc::clone(&logger), &UserConfigurations::new()).unwrap(); // Nothing uses their network key in this test + let mut config = UserConfigurations::new(); + config.channel_options.announced_channel= false; + let mut chan = Channel::new_outbound(&feeest, chan_keys, their_node_id, 10000000, 100000, 42, Arc::clone(&logger), &config).unwrap(); // Nothing uses their network key in this test chan.their_to_self_delay = 144; chan.our_dust_limit_satoshis = 546; diff --git a/src/ln/channelmanager.rs b/src/ln/channelmanager.rs index 077caa0e7..9c85993e0 100644 --- a/src/ln/channelmanager.rs +++ b/src/ln/channelmanager.rs @@ -237,8 +237,6 @@ pub struct ChannelManager { chain_monitor: Arc, tx_broadcaster: Arc, - announce_channels_publicly: bool, - fee_proportional_millionths: u32, latest_block_height: AtomicUsize, secp_ctx: Secp256k1, @@ -295,23 +293,19 @@ impl ChannelManager { /// This is the main "logic hub" for all channel-related actions, and implements /// ChannelMessageHandler. /// - /// fee_proportional_millionths is an optional fee to charge any payments routed through us. /// Non-proportional fees are fixed according to our risk using the provided fee estimator. /// /// panics if channel_value_satoshis is >= `MAX_FUNDING_SATOSHIS`! - pub fn new(our_network_key: SecretKey, fee_proportional_millionths: u32, announce_channels_publicly: bool, network: Network, feeest: Arc, monitor: Arc, chain_monitor: Arc, tx_broadcaster: Arc, logger: Arc) -> Result, secp256k1::Error> { + pub fn new(our_network_key: SecretKey, network: Network, feeest: Arc, monitor: Arc, chain_monitor: Arc, tx_broadcaster: Arc, logger: Arc, config : UserConfigurations) -> Result, secp256k1::Error> { let secp_ctx = Secp256k1::new(); - let res = Arc::new(ChannelManager { - configuration : UserConfigurations::new(), + configuration : config.clone(), genesis_hash: genesis_block(network).header.bitcoin_hash(), fee_estimator: feeest.clone(), monitor: monitor.clone(), chain_monitor, tx_broadcaster, - announce_channels_publicly, - fee_proportional_millionths, latest_block_height: AtomicUsize::new(0), //TODO: Get an init value (generally need to replay recent chain on chain_monitor registration) secp_ctx, @@ -365,7 +359,7 @@ impl ChannelManager { } }; - let channel = Channel::new_outbound(&*self.fee_estimator, chan_keys, their_network_key, channel_value_satoshis, push_msat, self.announce_channels_publicly, user_id, Arc::clone(&self.logger), &self.configuration)?; + let channel = Channel::new_outbound(&*self.fee_estimator, chan_keys, their_network_key, channel_value_satoshis, push_msat, user_id, Arc::clone(&self.logger), &self.configuration)?; let res = channel.get_open_channel(self.genesis_hash.clone(), &*self.fee_estimator); let mut channel_state = self.channel_state.lock().unwrap(); match channel_state.by_id.insert(channel.channel_id(), channel) { @@ -910,7 +904,7 @@ impl ChannelManager { if !chan.is_live() { Some(("Forwarding channel is not in a ready state.", 0x1000 | 7, self.get_channel_update(chan).unwrap())) } else { - let fee = amt_to_forward.checked_mul(self.fee_proportional_millionths as u64).and_then(|prop_fee| { (prop_fee / 1000000).checked_add(chan.get_our_fee_base_msat(&*self.fee_estimator) as u64) }); + let fee = amt_to_forward.checked_mul(self.configuration.channel_options.fee_proportional_millionths as u64).and_then(|prop_fee| { (prop_fee / 1000000).checked_add(chan.get_our_fee_base_msat(&*self.fee_estimator) as u64) }); if fee.is_none() || msg.amount_msat < fee.unwrap() || (msg.amount_msat - fee.unwrap()) < *amt_to_forward { Some(("Prior hop has deviated from specified fees parameters or origin node has obsolete ones", 0x1000 | 12, self.get_channel_update(chan).unwrap())) } else { @@ -947,7 +941,7 @@ impl ChannelManager { cltv_expiry_delta: CLTV_EXPIRY_DELTA, htlc_minimum_msat: chan.get_our_htlc_minimum_msat(), fee_base_msat: chan.get_our_fee_base_msat(&*self.fee_estimator), - fee_proportional_millionths: self.fee_proportional_millionths, + fee_proportional_millionths: self.configuration.channel_options.fee_proportional_millionths, excess_data: Vec::new(), }; @@ -1460,7 +1454,7 @@ impl ChannelManager { } }; - let channel = Channel::new_from_req(&*self.fee_estimator, chan_keys, their_node_id.clone(), msg, 0, false, self.announce_channels_publicly, Arc::clone(&self.logger), &self.configuration).map_err(|e| MsgHandleErrInternal::from_no_close(e))?; + let channel = Channel::new_from_req(&*self.fee_estimator, chan_keys, their_node_id.clone(), msg, 0, Arc::clone(&self.logger), &self.configuration).map_err(|e| MsgHandleErrInternal::from_no_close(e))?; let accept_msg = channel.get_accept_channel(); channel_state.by_id.insert(channel.channel_id(), channel); Ok(accept_msg) @@ -1486,7 +1480,8 @@ impl ChannelManager { pending_events.push(events::Event::FundingGenerationReady { temporary_channel_id: msg.temporary_channel_id, channel_value_satoshis: value, - output_script: output_script, user_channel_id: user_id, + output_script: output_script, + user_channel_id: user_id, }); Ok(()) } @@ -2996,6 +2991,8 @@ mod tests { } fn create_network(node_count: usize) -> Vec { + use util::UserConfigurations; + let mut nodes = Vec::new(); let mut rng = thread_rng(); let secp_ctx = Secp256k1::new(); @@ -3014,7 +3011,11 @@ mod tests { rng.fill_bytes(&mut key_slice); SecretKey::from_slice(&secp_ctx, &key_slice).unwrap() }; - let node = ChannelManager::new(node_id.clone(), 0, true, Network::Testnet, feeest.clone(), chan_monitor.clone(), chain_monitor.clone(), tx_broadcaster.clone(), Arc::clone(&logger)).unwrap(); + let mut config = UserConfigurations::new(); + config.channel_options.announced_channel = true; + config.channel_options.fee_proportional_millionths = 0; + config.channel_options.force_announced_channel_preference = false; + let node = ChannelManager::new(node_id.clone(), Network::Testnet, feeest.clone(), chan_monitor.clone(), chain_monitor.clone(), tx_broadcaster.clone(), Arc::clone(&logger), config).unwrap(); let router = Router::new(PublicKey::from_secret_key(&secp_ctx, &node_id), chain_monitor.clone(), Arc::clone(&logger)); nodes.push(Node { chain_monitor, tx_broadcaster, chan_monitor, node, router, network_payment_count: payment_count.clone(), diff --git a/src/ln/router.rs b/src/ln/router.rs index 5d5948dd7..4a55df88c 100644 --- a/src/ln/router.rs +++ b/src/ln/router.rs @@ -77,8 +77,6 @@ impl std::fmt::Display for ChannelInfo { } } - - struct NodeInfo { #[cfg(feature = "non_bitcoin_chain_hash_routing")] channels: Vec<(u64, Sha256dHash)>, diff --git a/src/util/configurations.rs b/src/util/configurations.rs index aef81950d..dab09b30f 100644 --- a/src/util/configurations.rs +++ b/src/util/configurations.rs @@ -1,29 +1,47 @@ -#[derive(Copy, Clone)] +use std::sync::Arc; + +/// This is the main user configuration +/// This struct should contain all user customizable options as this is passed to the channel to be accessed +#[derive(Clone, Debug)] pub struct UserConfigurations{ - pub channel_limits : ChannelLimits, + /// optional user spesefied channel limits + /// These are only used on startup of channels, and are referanced to a single instance + pub channel_limits : Arc, + /// Channel options can change afterwords and are unique to each channel + pub channel_options : ChannelOptions, } impl UserConfigurations { pub fn new() -> Self{ UserConfigurations { - channel_limits : ChannelLimits::new(), + channel_limits : Arc::new(ChannelLimits::new()), + channel_options : ChannelOptions::new(), } } } -#[derive(Copy, Clone)] -pub struct ChannelLimits -{ +/// This struct contains all the optional bolt 2 channel limits. +/// If the user wants to check a value, the value needs to be filled in, as by default they are not checked +#[derive(Copy, Clone, Debug)] +pub struct ChannelLimits{ + /// minimum allowed funding_satoshis pub funding_satoshis :u64, + /// maximum allowed htlc_minimum_msat pub htlc_minimum_msat : u64, + /// min allowed max_htlc_value_in_flight_msat pub max_htlc_value_in_flight_msat : u64, + /// max allowed channel_reserve_satashis pub channel_reserve_satoshis : u64, + /// min allowed max_accepted_htlcs pub max_accepted_htlcs : u16, + /// min allowed dust_limit_satashis pub dust_limit_satoshis : u64, + ///minimum depth to a number of blocks that is considered reasonable to avoid double-spending of the funding transaction + pub minimum_depth : u32, } impl ChannelLimits { - //creating max and min possible values because if they are not set, means we should not check them. +//creating max and min possible values because if they are not set, means we should not check them. pub fn new() -> Self{ ChannelLimits { funding_satoshis : 0, @@ -32,6 +50,29 @@ impl ChannelLimits { channel_reserve_satoshis : ::max_value(), max_accepted_htlcs : 0, dust_limit_satoshis : 0, + minimum_depth : ::max_value(), + } + } +} + +/// This struct contains all the custom channel options. +#[derive(Copy, Clone, Debug)] +pub struct ChannelOptions{ + /// Amount (in millionths of a satoshi) channel will charge per transferred satoshi. + pub fee_proportional_millionths : u32, + ///Is this channel an annouced channe; + pub announced_channel : bool, + ///do we force the incomming channel to match our announced channel preference + pub force_announced_channel_preference : bool, +} +impl ChannelOptions { + /// creating a struct with values. + /// fee_proportional_millionths should be changed afterwords + pub fn new() -> Self{ + ChannelOptions { + fee_proportional_millionths : 0, + announced_channel : true, + force_announced_channel_preference : false, } } } \ No newline at end of file diff --git a/src/util/mod.rs b/src/util/mod.rs index 02e873aaa..eb25dc7e8 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -29,5 +29,5 @@ pub use self::rng::reset_rng_state; #[cfg(test)] pub(crate) mod test_utils; -pub use self::configurations::{UserConfigurations, ChannelLimits}; -pub mod configurations; +pub use self::configurations::UserConfigurations; +pub mod configurations; \ No newline at end of file -- 2.39.5