From a0be4d1758811d0ee17e092cb8b5496f4baffbec Mon Sep 17 00:00:00 2001 From: Schalk van Heerden Date: Wed, 31 Oct 2018 14:51:39 -0400 Subject: [PATCH] Added config interface to allow users to specify channel limits --- fuzz/fuzz_targets/full_stack_target.rs | 7 +- src/ln/channel.rs | 102 ++++++++++++++------ src/ln/channelmanager.rs | 41 ++++---- src/util/config.rs | 127 +++++++++++++++++++++++++ src/util/mod.rs | 3 +- 5 files changed, 232 insertions(+), 48 deletions(-) create mode 100644 src/util/config.rs diff --git a/fuzz/fuzz_targets/full_stack_target.rs b/fuzz/fuzz_targets/full_stack_target.rs index ecadd212d..19b3e843a 100644 --- a/fuzz/fuzz_targets/full_stack_target.rs +++ b/fuzz/fuzz_targets/full_stack_target.rs @@ -24,6 +24,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::config::UserConfig; mod utils; @@ -305,7 +306,11 @@ pub fn do_test(data: &[u8], logger: &Arc) { let monitor = channelmonitor::SimpleManyChannelMonitor::new(watch.clone(), broadcast.clone(), Arc::clone(&logger)); let keys_manager = Arc::new(KeyProvider { node_secret: our_network_key.clone() }); - let channelmanager = ChannelManager::new(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), keys_manager.clone()).unwrap(); + let mut config = UserConfig::new(); + config.channel_options.fee_proportional_millionths = slice_to_be32(get_slice!(4)); + config.channel_options.announced_channel = get_slice!(1)[0] != 0; + config.channel_limits.min_dust_limit_satoshis = 0; + let channelmanager = ChannelManager::new(Network::Bitcoin, fee_est.clone(), monitor.clone(), watch.clone(), broadcast.clone(), Arc::clone(&logger), keys_manager.clone(), config).unwrap(); let router = Arc::new(Router::new(PublicKey::from_secret_key(&secp_ctx, &keys_manager.get_node_secret()), watch.clone(), Arc::clone(&logger))); let peers = RefCell::new([false; 256]); diff --git a/src/ln/channel.rs b/src/ln/channel.rs index 4ab42f75a..1fbc605bb 100644 --- a/src/ln/channel.rs +++ b/src/ln/channel.rs @@ -28,6 +28,7 @@ use util::ser::{Readable, ReadableArgs, Writeable, Writer, WriterWriteAdaptor}; use util::sha2::Sha256; use util::logger::Logger; use util::errors::APIError; +use util::config::{UserConfig,ChannelConfig}; use std; use std::default::Default; @@ -230,13 +231,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: ChannelConfig, + 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, @@ -415,7 +417,7 @@ impl Channel { } // Constructors: - pub fn new_outbound(fee_estimator: &FeeEstimator, keys_provider: &Arc, their_node_id: PublicKey, channel_value_satoshis: u64, push_msat: u64, announce_publicly: bool, user_id: u64, logger: Arc) -> Result { + pub fn new_outbound(fee_estimator: &FeeEstimator, keys_provider: &Arc, their_node_id: PublicKey, channel_value_satoshis: u64, push_msat: u64, user_id: u64, logger: Arc, config: &UserConfig) -> Result { let chan_keys = keys_provider.get_channel_keys(false); if channel_value_satoshis >= MAX_FUNDING_SATOSHIS { @@ -441,12 +443,12 @@ impl Channel { Ok(Channel { user_id: user_id, + config: config.channel_options.clone(), 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, @@ -526,8 +528,9 @@ impl Channel { /// Creates a new channel from a remote sides' request for one. /// Assumes chain_hash has already been checked and corresponds with what we expect! - pub fn new_from_req(fee_estimator: &FeeEstimator, keys_provider: &Arc, their_node_id: PublicKey, msg: &msgs::OpenChannel, user_id: u64, require_announce: bool, allow_announce: bool, logger: Arc) -> Result { + pub fn new_from_req(fee_estimator: &FeeEstimator, keys_provider: &Arc, their_node_id: PublicKey, msg: &msgs::OpenChannel, user_id: u64, logger: Arc, config: &UserConfig) -> Result { let chan_keys = keys_provider.get_channel_keys(true); + let mut local_config = (*config).channel_options.clone(); // Check sanity of message fields: if msg.funding_satoshis >= MAX_FUNDING_SATOSHIS { @@ -560,22 +563,46 @@ impl Channel { return Err(ChannelError::Close("max_accpted_htlcs > 483")); } + // Now check against optional parameters as set by config... + if msg.funding_satoshis < config.channel_limits.min_funding_satoshis { + return Err(ChannelError::Close("funding satoshis is less than the user specified limit")); + } + if msg.htlc_minimum_msat > config.channel_limits.max_htlc_minimum_msat { + return Err(ChannelError::Close("htlc minimum msat is higher than the user specified limit")); + } + if msg.max_htlc_value_in_flight_msat < config.channel_limits.min_max_htlc_value_in_flight_msat { + return Err(ChannelError::Close("max htlc value in flight msat is less than the user specified limit")); + } + if msg.channel_reserve_satoshis > config.channel_limits.max_channel_reserve_satoshis { + return Err(ChannelError::Close("channel reserve satoshis is higher than the user specified limit")); + } + if msg.max_accepted_htlcs < config.channel_limits.min_max_accepted_htlcs { + return Err(ChannelError::Close("max accepted htlcs is less than the user specified limit")); + } + if msg.dust_limit_satoshis < config.channel_limits.min_dust_limit_satoshis { + return Err(ChannelError::Close("dust limit satoshis is less than the user specified limit")); + } + if msg.dust_limit_satoshis > config.channel_limits.max_dust_limit_satoshis { + return Err(ChannelError::Close("dust limit satoshis is greater than the user specified limit")); + } + // 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 Err(ChannelError::Close("Peer tried to open unannounced channel, but we require public ones")); - } - if !allow_announce && their_announce { - return Err(ChannelError::Close("Peer tried to open announced channel, but we require private ones")); + if config.channel_limits.force_announced_channel_preference { + if local_config.announced_channel != their_announce { + return Err(ChannelError::Close("Peer tried to open channel but their announcement preference is different from ours")); + } } + // 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); let our_dust_limit_satoshis = Channel::derive_our_dust_limit_satoshis(background_feerate); let our_channel_reserve_satoshis = Channel::get_our_channel_reserve_satoshis(msg.funding_satoshis); if our_channel_reserve_satoshis < our_dust_limit_satoshis { - return Err(ChannelError::Close("Suitalbe channel reserve not found. aborting")); + return Err(ChannelError::Close("Suitable channel reserve not found. aborting")); } if msg.channel_reserve_satoshis < our_dust_limit_satoshis { return Err(ChannelError::Close("channel_reserve_satoshis too small")); @@ -606,12 +633,12 @@ impl Channel { let mut chan = Channel { user_id: user_id, + config: local_config, 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, shutdown_pubkey: keys_provider.get_shutdown_pubkey(), @@ -1262,7 +1289,7 @@ impl Channel { // Message handlers: - pub fn accept_channel(&mut self, msg: &msgs::AcceptChannel) -> Result<(), ChannelError> { + pub fn accept_channel(&mut self, msg: &msgs::AcceptChannel, config: &UserConfig) -> Result<(), ChannelError> { // Check sanity of message fields: if !self.channel_outbound { return Err(ChannelError::Close("Got an accept_channel message from an inbound peer")); @@ -1298,14 +1325,28 @@ impl Channel { return Err(ChannelError::Close("max_accpted_htlcs > 483")); } - // TODO: Optional additional constraints mentioned in the spec - // MAY fail the channel if - // funding_satoshi is too small - // htlc_minimum_msat too large - // max_htlc_value_in_flight_msat too small - // channel_reserve_satoshis too large - // max_accepted_htlcs too small - // dust_limit_satoshis too small + // Now check against optional parameters as set by config... + if msg.htlc_minimum_msat > config.channel_limits.max_htlc_minimum_msat { + return Err(ChannelError::Close("htlc minimum msat is higher than the user specified limit")); + } + if msg.max_htlc_value_in_flight_msat < config.channel_limits.min_max_htlc_value_in_flight_msat { + return Err(ChannelError::Close("max htlc value in flight msat is less than the user specified limit")); + } + if msg.channel_reserve_satoshis > config.channel_limits.max_channel_reserve_satoshis { + return Err(ChannelError::Close("channel reserve satoshis is higher than the user specified limit")); + } + if msg.max_accepted_htlcs < config.channel_limits.min_max_accepted_htlcs { + return Err(ChannelError::Close("max accepted htlcs is less than the user specified limit")); + } + if msg.dust_limit_satoshis < config.channel_limits.min_dust_limit_satoshis { + return Err(ChannelError::Close("dust limit satoshis is less than the user specified limit")); + } + if msg.dust_limit_satoshis > config.channel_limits.max_dust_limit_satoshis { + return Err(ChannelError::Close("dust limit satoshis is greater than the user specified limit")); + } + if msg.minimum_depth > config.channel_limits.max_minimum_depth { + return Err(ChannelError::Close("We consider the minimum depth to be unreasonably large")); + } self.channel_monitor.set_their_base_keys(&msg.htlc_basepoint, &msg.delayed_payment_basepoint); @@ -2571,6 +2612,10 @@ impl Channel { self.channel_value_satoshis } + pub fn get_fee_proportional_millionths(&self) -> u32 { + self.config.fee_proportional_millionths + } + #[cfg(test)] pub fn get_feerate(&self) -> u64 { self.feerate_per_kw @@ -2624,7 +2669,7 @@ impl Channel { } pub fn should_announce(&self) -> bool { - self.announce_publicly + self.config.announced_channel } pub fn is_outbound(&self) -> bool { @@ -2823,7 +2868,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.announced_channel {1} else {0}, shutdown_scriptpubkey: None, } } @@ -2927,7 +2972,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), ChannelError> { - if !self.announce_publicly { + if !self.config.announced_channel { return Err(ChannelError::Ignore("Channel is not available for public announcements")); } if self.channel_state & (ChannelState::ChannelFunded as u32) == 0 { @@ -3303,11 +3348,11 @@ impl Writeable for Channel { writer.write_all(&[MIN_SERIALIZATION_VERSION; 1])?; self.user_id.write(writer)?; + self.config.write(writer)?; self.channel_id.write(writer)?; (self.channel_state | ChannelState::PeerDisconnected as u32).write(writer)?; self.channel_outbound.write(writer)?; - self.announce_publicly.write(writer)?; self.channel_value_satoshis.write(writer)?; self.local_keys.write(writer)?; @@ -3506,11 +3551,11 @@ impl ReadableArgs> for Channel { } let user_id = Readable::read(reader)?; + let config: ChannelConfig = Readable::read(reader)?; let channel_id = Readable::read(reader)?; let channel_state = Readable::read(reader)?; let channel_outbound = Readable::read(reader)?; - let announce_publicly = Readable::read(reader)?; let channel_value_satoshis = Readable::read(reader)?; let local_keys = Readable::read(reader)?; @@ -3675,11 +3720,11 @@ impl ReadableArgs> for Channel { Ok(Channel { user_id, + config, channel_id, channel_state, channel_outbound, secp_ctx: Secp256k1::new(), - announce_publicly, channel_value_satoshis, local_keys, @@ -3766,6 +3811,7 @@ mod tests { use chain::chaininterface::{FeeEstimator,ConfirmationTarget}; use chain::keysinterface::KeysInterface; use chain::transaction::OutPoint; + use util::config::UserConfig; use util::test_utils; use util::logger::Logger; use secp256k1::{Secp256k1,Message,Signature}; @@ -3832,7 +3878,9 @@ mod tests { let keys_provider: Arc = Arc::new(Keys { chan_keys }); 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, &keys_provider, their_node_id, 10000000, 100000, false, 42, Arc::clone(&logger)).unwrap(); // Nothing uses their network key in this test + let mut config = UserConfig::new(); + config.channel_options.announced_channel = false; + let mut chan = Channel::new_outbound(&feeest, &keys_provider, 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 74e0226b4..ed69e8a03 100644 --- a/src/ln/channelmanager.rs +++ b/src/ln/channelmanager.rs @@ -28,6 +28,7 @@ use ln::router::{Route,RouteHop}; use ln::msgs; use ln::msgs::{ChannelMessageHandler, DecodeError, HandleError}; use chain::keysinterface::KeysInterface; +use util::config::UserConfig; use util::{byte_utils, events, internal_traits, rng}; use util::sha2::Sha256; use util::ser::{Readable, ReadableArgs, Writeable, Writer}; @@ -320,14 +321,13 @@ const ERR: () = "You need at least 32 bit pointers (well, usize, but we'll assum /// block_connected() to step towards your best block) upon deserialization before using the /// object! pub struct ChannelManager { + default_configuration: UserConfig, genesis_hash: Sha256dHash, fee_estimator: Arc, monitor: Arc, chain_monitor: Arc, tx_broadcaster: Arc, - announce_channels_publicly: bool, - fee_proportional_millionths: u32, latest_block_height: AtomicUsize, last_block_hash: Mutex, secp_ctx: Secp256k1, @@ -411,22 +411,20 @@ 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(fee_proportional_millionths: u32, announce_channels_publicly: bool, network: Network, feeest: Arc, monitor: Arc, chain_monitor: Arc, tx_broadcaster: Arc, logger: Arc, keys_manager: Arc) -> Result, secp256k1::Error> { + pub fn new(network: Network, feeest: Arc, monitor: Arc, chain_monitor: Arc, tx_broadcaster: Arc, logger: Arc,keys_manager: Arc, config: UserConfig) -> Result, secp256k1::Error> { let secp_ctx = Secp256k1::new(); let res = Arc::new(ChannelManager { + default_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 last_block_hash: Mutex::new(Default::default()), secp_ctx, @@ -470,7 +468,7 @@ impl ChannelManager { return Err(APIError::APIMisuseError { err: "channel_value must be at least 1000 satoshis" }); } - let channel = Channel::new_outbound(&*self.fee_estimator, &self.keys_manager, their_network_key, channel_value_satoshis, push_msat, self.announce_channels_publicly, user_id, Arc::clone(&self.logger))?; + let channel = Channel::new_outbound(&*self.fee_estimator, &self.keys_manager, their_network_key, channel_value_satoshis, push_msat, user_id, Arc::clone(&self.logger), &self.default_configuration)?; let res = channel.get_open_channel(self.genesis_hash.clone(), &*self.fee_estimator); let _ = self.total_consistency_lock.read().unwrap(); @@ -1076,7 +1074,7 @@ impl ChannelManager { if *amt_to_forward < chan.get_their_htlc_minimum_msat() { // amount_below_minimum break Some(("HTLC amount was below the htlc_minimum_msat", 0x1000 | 11, Some(self.get_channel_update(chan).unwrap()))); } - 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(chan.get_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 { // fee_insufficient break Some(("Prior hop has deviated from specified fees parameters or origin node has obsolete ones", 0x1000 | 12, Some(self.get_channel_update(chan).unwrap()))); } @@ -1130,7 +1128,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: chan.get_fee_proportional_millionths(), excess_data: Vec::new(), }; @@ -1687,7 +1685,7 @@ impl ChannelManager { return Err(MsgHandleErrInternal::send_err_msg_no_close("Unknown genesis block hash", msg.temporary_channel_id.clone())); } - let channel = Channel::new_from_req(&*self.fee_estimator, &self.keys_manager, their_node_id.clone(), msg, 0, false, self.announce_channels_publicly, Arc::clone(&self.logger)) + let channel = Channel::new_from_req(&*self.fee_estimator, &self.keys_manager, their_node_id.clone(), msg, 0, Arc::clone(&self.logger), &self.default_configuration) .map_err(|e| MsgHandleErrInternal::from_chan_no_close(e, msg.temporary_channel_id))?; let mut channel_state_lock = self.channel_state.lock().unwrap(); let channel_state = channel_state_lock.borrow_parts(); @@ -1713,7 +1711,7 @@ impl ChannelManager { //TODO: see issue #153, need a consistent behavior on obnoxious behavior from random node return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!", msg.temporary_channel_id)); } - chan.accept_channel(&msg) + chan.accept_channel(&msg, &self.default_configuration) .map_err(|e| MsgHandleErrInternal::from_chan_maybe_close(e, msg.temporary_channel_id))?; (chan.get_value_satoshis(), chan.get_funding_redeemscript().to_v0_p2wsh(), chan.get_user_id()) }, @@ -2963,8 +2961,6 @@ impl Writeable for ChannelManager { writer.write_all(&[MIN_SERIALIZATION_VERSION; 1])?; self.genesis_hash.write(writer)?; - self.announce_channels_publicly.write(writer)?; - self.fee_proportional_millionths.write(writer)?; (self.latest_block_height.load(Ordering::Acquire) as u32).write(writer)?; self.last_block_hash.lock().unwrap().write(writer)?; @@ -3046,7 +3042,9 @@ pub struct ChannelManagerReadArgs<'a> { /// The Logger for use in the ChannelManager and which may be used to log information during /// deserialization. pub logger: Arc, - + /// Default settings used for new channels. Any existing channels will continue to use the + /// runtime settings which were stored when the ChannelManager was serialized. + pub default_config: UserConfig, /// A map from channel funding outpoints to ChannelMonitors for those channels (ie /// value.get_funding_txo() should be the key). @@ -3070,8 +3068,6 @@ impl<'a, R : ::std::io::Read> ReadableArgs> for (S } let genesis_hash: Sha256dHash = Readable::read(reader)?; - let announce_channels_publicly: bool = Readable::read(reader)?; - let fee_proportional_millionths: u32 = Readable::read(reader)?; let latest_block_height: u32 = Readable::read(reader)?; let last_block_hash: Sha256dHash = Readable::read(reader)?; @@ -3144,8 +3140,6 @@ impl<'a, R : ::std::io::Read> ReadableArgs> for (S chain_monitor: args.chain_monitor, tx_broadcaster: args.tx_broadcaster, - announce_channels_publicly, - fee_proportional_millionths, latest_block_height: AtomicUsize::new(latest_block_height as usize), last_block_hash: Mutex::new(last_block_hash), secp_ctx: Secp256k1::new(), @@ -3164,6 +3158,7 @@ impl<'a, R : ::std::io::Read> ReadableArgs> for (S total_consistency_lock: RwLock::new(()), keys_manager: args.keys_manager, logger: args.logger, + default_configuration: args.default_config, }; for close_res in closed_channels.drain(..) { @@ -3193,6 +3188,7 @@ mod tests { use util::errors::APIError; use util::logger::Logger; use util::ser::{Writeable, Writer, ReadableArgs}; + use util::config::UserConfig; use bitcoin::util::hash::Sha256dHash; use bitcoin::blockdata::block::{Block, BlockHeader}; @@ -4057,7 +4053,10 @@ mod tests { rng.fill_bytes(&mut seed); let keys_manager = Arc::new(keysinterface::KeysManager::new(&seed, Network::Testnet, Arc::clone(&logger))); let chan_monitor = Arc::new(test_utils::TestChannelMonitor::new(chain_monitor.clone(), tx_broadcaster.clone(), logger.clone())); - let node = ChannelManager::new(0, true, Network::Testnet, feeest.clone(), chan_monitor.clone(), chain_monitor.clone(), tx_broadcaster.clone(), Arc::clone(&logger), keys_manager.clone()).unwrap(); + let mut config = UserConfig::new(); + config.channel_options.announced_channel = true; + config.channel_limits.force_announced_channel_preference = false; + let node = ChannelManager::new(Network::Testnet, feeest.clone(), chan_monitor.clone(), chain_monitor.clone(), tx_broadcaster.clone(), Arc::clone(&logger), keys_manager.clone(), config).unwrap(); let router = Router::new(PublicKey::from_secret_key(&secp_ctx, &keys_manager.get_node_secret()), chain_monitor.clone(), Arc::clone(&logger)); nodes.push(Node { chain_monitor, tx_broadcaster, chan_monitor, node, router, node_seed: seed, network_payment_count: payment_count.clone(), @@ -6892,11 +6891,13 @@ mod tests { assert!(chan_0_monitor_read.is_empty()); let mut nodes_0_read = &nodes_0_serialized[..]; + let config = UserConfig::new(); let keys_manager = Arc::new(keysinterface::KeysManager::new(&nodes[0].node_seed, Network::Testnet, Arc::new(test_utils::TestLogger::new()))); let (_, nodes_0_deserialized) = { let mut channel_monitors = HashMap::new(); channel_monitors.insert(chan_0_monitor.get_funding_txo().unwrap(), &chan_0_monitor); <(Sha256dHash, ChannelManager)>::read(&mut nodes_0_read, ChannelManagerReadArgs { + default_config: config, keys_manager, fee_estimator: Arc::new(test_utils::TestFeeEstimator { sat_per_kw: 253 }), monitor: nodes[0].chan_monitor.clone(), @@ -6961,6 +6962,7 @@ mod tests { let mut channel_monitors = HashMap::new(); channel_monitors.insert(chan_0_monitor.get_funding_txo().unwrap(), &chan_0_monitor); <(Sha256dHash, ChannelManager)>::read(&mut nodes_0_read, ChannelManagerReadArgs { + default_config: UserConfig::new(), keys_manager, fee_estimator: Arc::new(test_utils::TestFeeEstimator { sat_per_kw: 253 }), monitor: nodes[0].chan_monitor.clone(), @@ -7021,6 +7023,7 @@ mod tests { let mut nodes_0_read = &nodes_0_serialized[..]; let keys_manager = Arc::new(keysinterface::KeysManager::new(&nodes[0].node_seed, Network::Testnet, Arc::new(test_utils::TestLogger::new()))); let (_, nodes_0_deserialized) = <(Sha256dHash, ChannelManager)>::read(&mut nodes_0_read, ChannelManagerReadArgs { + default_config: UserConfig::new(), keys_manager, fee_estimator: Arc::new(test_utils::TestFeeEstimator { sat_per_kw: 253 }), monitor: nodes[0].chan_monitor.clone(), diff --git a/src/util/config.rs b/src/util/config.rs new file mode 100644 index 000000000..a3abdbede --- /dev/null +++ b/src/util/config.rs @@ -0,0 +1,127 @@ +//! Various user-configurable channel limits and settings which ChannelManager +//! applies for you. + +/// Top-level config which holds ChannelHandshakeLimits and ChannelConfig. +#[derive(Clone, Debug)] +pub struct UserConfig { + /// Limits applied during channel creation. + pub channel_limits: ChannelHandshakeLimits, + /// Channel config which affects behavior during channel lifetime. + pub channel_options: ChannelConfig, +} + +impl UserConfig { + /// Provides sane defaults for most configurations (but with 0 relay fees!) + pub fn new() -> Self{ + UserConfig { + channel_limits: ChannelHandshakeLimits::new(), + channel_options: ChannelConfig::new(), + } + } +} + +/// Optional channel limits which are applied during channel creation. +/// +/// These limits are only applied to our counterparty's limits, not our own. +/// +/// Use 0/::max_value() as appropriate to skip checking. +#[derive(Copy, Clone, Debug)] +pub struct ChannelHandshakeLimits { + /// Minimum allowed satoshis when a channel is funded, this is supplied by the sender and so + /// only applies to inbound channels. + pub min_funding_satoshis: u64, + /// The remote node sets a limit on the minimum size of HTLCs we can send to them. This allows + /// you to limit the maximum minimum-size they can require. + pub max_htlc_minimum_msat: u64, + /// The remote node sets a limit on the maximum value of pending HTLCs to them at any given + /// time to limit their funds exposure to HTLCs. This allows you to set a minimum such value. + pub min_max_htlc_value_in_flight_msat: u64, + /// The remote node will require we keep a certain amount in direct payment to ourselves at all + /// time, ensuring that we are able to be punished if we broadcast an old state. This allows to + /// you limit the amount which we will have to keep to ourselves (and cannot use for HTLCs). + pub max_channel_reserve_satoshis: u64, + /// The remote node sets a limit on the maximum number of pending HTLCs to them at any given + /// time. This allows you to set a minimum such value. + pub min_max_accepted_htlcs: u16, + /// Outputs below a certain value will not be added to on-chain transactions. The dust value is + /// required to always be higher than this value so this only applies to HTLC outputs (and + /// potentially to-self outputs before any payments have been made). + /// Thus, HTLCs below this amount plus HTLC transaction fees are not enforceable on-chain. + /// This setting allows you to set a minimum dust limit for their commitment transactions, + /// reflecting the reality that tiny outputs are not considered standard transactions and will + /// not propagate through the Bitcoin network. + /// Defaults to 546, or the current dust limit on the Bitcoin network. + pub min_dust_limit_satoshis: u64, + /// Maximum allowed threshold above which outputs will not be generated in their commitment + /// transactions. + /// HTLCs below this amount plus HTLC transaction fees are not enforceable on-chain. + pub max_dust_limit_satoshis: u64, + /// Before a channel is usable the funding transaction will need to be confirmed by at least a + /// certain number of blocks, specified by the node which is not the funder (as the funder can + /// assume they aren't going to double-spend themselves). + /// This config allows you to set a limit on the maximum amount of time to wait. Defaults to + /// 144 blocks or roughly one day and only applies to outbound channels. + pub max_minimum_depth: u32, + /// Set to force the incoming channel to match our announced channel preference in + /// ChannelConfig. + /// Defaults to true to make the default that no announced channels are possible (which is + /// appropriate for any nodes which are not online very reliably). + pub force_announced_channel_preference: bool, +} + +impl ChannelHandshakeLimits { + /// Provides sane defaults for most configurations. + /// + /// Most additional limits are disabled except those with which specify a default in individual + /// field documentation. Note that this may result in barely-usable channels, but since they + /// are applied mostly only to incoming channels that's not much of a problem. + pub fn new() -> Self { + ChannelHandshakeLimits { + min_funding_satoshis: 0, + max_htlc_minimum_msat: ::max_value(), + min_max_htlc_value_in_flight_msat: 0, + max_channel_reserve_satoshis: ::max_value(), + min_max_accepted_htlcs: 0, + min_dust_limit_satoshis: 546, + max_dust_limit_satoshis: ::max_value(), + max_minimum_depth: 144, + force_announced_channel_preference: true, + } + } +} + +/// Options which apply on a per-channel basis and may change at runtime or based on negotiation +/// with our counterparty. +#[derive(Copy, Clone, Debug)] +pub struct ChannelConfig { + /// Amount (in millionths of a satoshi) the channel will charge per transferred satoshi. + /// This may be allowed to change at runtime in a later update, however doing so must result in + /// update messages sent to notify all nodes of our updated relay fee. + pub fee_proportional_millionths: u32, + /// Set to announce the channel publicly and notify all nodes that they can route via this + /// channel. + /// + /// This should only be set to true for nodes which expect to be online reliably. + /// + /// As the node which funds a channel picks this value this will only apply for new outbound + /// channels unless ChannelHandshakeLimits::force_announced_channel_preferences is set. + /// + /// This cannot be changed after the initial channel handshake. + pub announced_channel: bool, +} + +impl ChannelConfig { + /// Provides sane defaults for most configurations (but with zero relay fees!). + pub fn new() -> Self { + ChannelConfig { + fee_proportional_millionths: 0, + announced_channel: false, + } + } +} + +//Add write and readable traits to channelconfig +impl_writeable!(ChannelConfig, 8+1, { + fee_proportional_millionths, + announced_channel +}); diff --git a/src/util/mod.rs b/src/util/mod.rs index b91b4f25d..842fbea60 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -15,8 +15,9 @@ pub(crate) mod ser_macros; #[macro_use] pub(crate) mod macro_logger; -// Logger has to come after macro_logger for tests to build: +// These have to come after macro_logger to build pub mod logger; +pub mod config; #[cfg(feature = "fuzztarget")] pub mod sha2; -- 2.39.5