Enable wumbo channels to be created
authorValentine Wallace <vwallace@protonmail.com>
Fri, 15 Apr 2022 22:10:39 +0000 (18:10 -0400)
committerValentine Wallace <vwallace@protonmail.com>
Thu, 28 Apr 2022 19:01:21 +0000 (15:01 -0400)
Also redefine MAX_FUNDING_SATOSHIS_NO_WUMBO to no longer be off-by-one.

lightning/src/ln/channel.rs
lightning/src/ln/features.rs
lightning/src/ln/functional_tests.rs

index d680095fc1ba06f842921cd604589f48a5386ceb..b172b8c52ad8a0fc78e44e3869e6820d68e2a83e 100644 (file)
@@ -736,8 +736,8 @@ pub const ANCHOR_OUTPUT_VALUE_SATOSHI: u64 = 330;
 
 /// 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.
-pub const MAX_FUNDING_SATOSHIS_NO_WUMBO: u64 = 1 << 24;
+/// 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;
@@ -854,8 +854,11 @@ impl<Signer: Sign> Channel<Signer> {
                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_NO_WUMBO {
-                       return Err(APIError::APIMisuseError{err: format!("funding_value must be smaller than {}, it was {}", MAX_FUNDING_SATOSHIS_NO_WUMBO, 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 {
@@ -1080,8 +1083,11 @@ impl<Signer: Sign> Channel<Signer> {
                }
 
                // Check sanity of message fields:
-               if msg.funding_satoshis >= MAX_FUNDING_SATOSHIS_NO_WUMBO {
-                       return Err(ChannelError::Close(format!("Funding must be smaller than {}. It was {}", MAX_FUNDING_SATOSHIS_NO_WUMBO, 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)));
index 57d1d427f1793450781f874f2c8786b12e8760c6..2580874640e438e611bea9b88ddda0384dc9db5d 100644 (file)
@@ -743,6 +743,15 @@ impl<T: sealed::ShutdownAnySegwit> Features<T> {
                self
        }
 }
+
+impl<T: sealed::Wumbo> Features<T> {
+       #[cfg(test)]
+       pub(crate) fn clear_wumbo(mut self) -> Self {
+               <T as sealed::Wumbo>::clear_bits(&mut self.flags);
+               self
+       }
+}
+
 macro_rules! impl_feature_len_prefixed_write {
        ($features: ident) => {
                impl Writeable for $features {
index ef30c57e0093ef20981c7fa9e36dfed6b2328b21..3449bd941d9385d0ad8bb28a8c32239c1e489e1c 100644 (file)
@@ -58,9 +58,12 @@ use ln::chan_utils::CommitmentTransaction;
 #[test]
 fn test_insane_channel_opens() {
        // Stand up a network of 2 nodes
+       use ln::channel::TOTAL_BITCOIN_SUPPLY_SATOSHIS;
+       let mut cfg = UserConfig::default();
+       cfg.peer_channel_config_limits.max_funding_satoshis = TOTAL_BITCOIN_SUPPLY_SATOSHIS + 1;
        let chanmon_cfgs = create_chanmon_cfgs(2);
        let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
-       let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
+       let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, Some(cfg)]);
        let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
 
        // Instantiate channel parameters where we push the maximum msats given our
@@ -92,11 +95,11 @@ fn test_insane_channel_opens() {
                } else { assert!(false); }
        };
 
-       use ln::channel::MAX_FUNDING_SATOSHIS_NO_WUMBO;
        use ln::channelmanager::MAX_LOCAL_BREAKDOWN_TIMEOUT;
 
        // Test all mutations that would make the channel open message insane
-       insane_open_helper(format!("Funding must be smaller than {}. It was {}", MAX_FUNDING_SATOSHIS_NO_WUMBO, MAX_FUNDING_SATOSHIS_NO_WUMBO).as_str(), |mut msg| { msg.funding_satoshis = MAX_FUNDING_SATOSHIS_NO_WUMBO; msg });
+       insane_open_helper(format!("Per our config, funding must be at most {}. It was {}", TOTAL_BITCOIN_SUPPLY_SATOSHIS + 1, TOTAL_BITCOIN_SUPPLY_SATOSHIS + 2).as_str(), |mut msg| { msg.funding_satoshis = TOTAL_BITCOIN_SUPPLY_SATOSHIS + 2; msg });
+       insane_open_helper(format!("Funding must be smaller than the total bitcoin supply. It was {}", TOTAL_BITCOIN_SUPPLY_SATOSHIS).as_str(), |mut msg| { msg.funding_satoshis = TOTAL_BITCOIN_SUPPLY_SATOSHIS; msg });
 
        insane_open_helper("Bogus channel_reserve_satoshis", |mut msg| { msg.channel_reserve_satoshis = msg.funding_satoshis + 1; msg });
 
@@ -113,6 +116,25 @@ fn test_insane_channel_opens() {
        insane_open_helper("max_accepted_htlcs was 484. It must not be larger than 483", |mut msg| { msg.max_accepted_htlcs = 484; msg });
 }
 
+#[test]
+fn test_funding_exceeds_no_wumbo_limit() {
+       // Test that if a peer does not support wumbo channels, we'll refuse to open a wumbo channel to
+       // them.
+       use ln::channel::MAX_FUNDING_SATOSHIS_NO_WUMBO;
+       let chanmon_cfgs = create_chanmon_cfgs(2);
+       let mut node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+       node_cfgs[1].features = InitFeatures::known().clear_wumbo();
+       let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
+       let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+
+       match nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), MAX_FUNDING_SATOSHIS_NO_WUMBO + 1, 0, 42, None) {
+               Err(APIError::APIMisuseError { err }) => {
+                       assert_eq!(format!("funding_value must not exceed {}, it was {}", MAX_FUNDING_SATOSHIS_NO_WUMBO, MAX_FUNDING_SATOSHIS_NO_WUMBO + 1), err);
+               },
+               _ => panic!()
+       }
+}
+
 fn do_test_counterparty_no_reserve(send_from_initiator: bool) {
        // A peer providing a channel_reserve_satoshis of 0 (or less than our dust limit) is insecure,
        // but only for them. Because some LSPs do it with some level of trust of the clients (for a