Merge pull request #1531 from ariard/2022-06-fee-sniping
authorMatt Corallo <649246+TheBlueMatt@users.noreply.github.com>
Thu, 16 Jun 2022 13:12:29 +0000 (06:12 -0700)
committerGitHub <noreply@github.com>
Thu, 16 Jun 2022 13:12:29 +0000 (06:12 -0700)
Funding_tx: add anti-fee sniping recommendation and check if final

1  2 
lightning/src/ln/channelmanager.rs
lightning/src/ln/functional_tests.rs

index cf593068947478d7060b7603b93f13400e31bda2,9fd4a24f444bac25c23eac3312cda797faa4590e..a3c8b9df6f897441d3f762d765124ccdef291113
@@@ -2529,6 -2529,12 +2529,6 @@@ impl<Signer: Sign, M: Deref, T: Deref, 
                if route.paths.len() < 1 {
                        return Err(PaymentSendFailure::ParameterError(APIError::RouteError{err: "There must be at least one path to send over"}));
                }
 -              if route.paths.len() > 10 {
 -                      // This limit is completely arbitrary - there aren't any real fundamental path-count
 -                      // limits. After we support retrying individual paths we should likely bump this, but
 -                      // for now more than 10 paths likely carries too much one-path failure.
 -                      return Err(PaymentSendFailure::ParameterError(APIError::RouteError{err: "Sending over more than 10 paths is not currently supported"}));
 -              }
                if payment_secret.is_none() && route.paths.len() > 1 {
                        return Err(PaymentSendFailure::ParameterError(APIError::APIMisuseError{err: "Payment secret is required for multi-path payments".to_string()}));
                }
        /// Returns an [`APIError::APIMisuseError`] if the funding_transaction spent non-SegWit outputs
        /// or if no output was found which matches the parameters in [`Event::FundingGenerationReady`].
        ///
+       /// Returns [`APIError::APIMisuseError`] if the funding transaction is not final for propagation
+       /// across the p2p network.
+       ///
        /// Returns [`APIError::ChannelUnavailable`] if a funding transaction has already been provided
        /// for the channel or if the channel has been closed as indicated by [`Event::ChannelClosed`].
        ///
        /// not currently support replacing a funding transaction on an existing channel. Instead,
        /// create a new channel with a conflicting funding transaction.
        ///
+       /// Note to keep the miner incentives aligned in moving the blockchain forward, we recommend
+       /// the wallet software generating the funding transaction to apply anti-fee sniping as
+       /// implemented by Bitcoin Core wallet. See <https://bitcoinops.org/en/topics/fee-sniping/>
+       /// for more details.
+       ///
        /// [`Event::FundingGenerationReady`]: crate::util::events::Event::FundingGenerationReady
        /// [`Event::ChannelClosed`]: crate::util::events::Event::ChannelClosed
        pub fn funding_transaction_generated(&self, temporary_channel_id: &[u8; 32], counterparty_node_id: &PublicKey, funding_transaction: Transaction) -> Result<(), APIError> {
                                });
                        }
                }
+               {
+                       let height = self.best_block.read().unwrap().height();
+                       // Transactions are evaluated as final by network mempools at the next block. However, the modules
+                       // constituting our Lightning node might not have perfect sync about their blockchain views. Thus, if
+                       // the wallet module is in advance on the LDK view, allow one more block of headroom.
+                       // TODO: updated if/when https://github.com/rust-bitcoin/rust-bitcoin/pull/994 landed and rust-bitcoin bumped.
+                       if !funding_transaction.input.iter().all(|input| input.sequence == 0xffffffff) && funding_transaction.lock_time < 500_000_000 && funding_transaction.lock_time > height + 2 {
+                               return Err(APIError::APIMisuseError {
+                                       err: "Funding transaction absolute timelock is non-final".to_owned()
+                               });
+                       }
+               }
                self.funding_transaction_generated_intern(temporary_channel_id, counterparty_node_id, funding_transaction, |chan, tx| {
                        let mut output_index = None;
                        let expected_spk = chan.get_funding_redeemscript().to_v0_p2wsh();
                                        if chan.get().get_counterparty_node_id() != *counterparty_node_id {
                                                return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!".to_owned(), msg.temporary_channel_id));
                                        }
 -                                      try_chan_entry!(self, chan.get_mut().accept_channel(&msg, &self.default_configuration.peer_channel_config_limits, &their_features), channel_state, chan);
 +                                      try_chan_entry!(self, chan.get_mut().accept_channel(&msg, &self.default_configuration.channel_handshake_limits, &their_features), channel_state, chan);
                                        (chan.get().get_value_satoshis(), chan.get().get_funding_redeemscript().to_v0_p2wsh(), chan.get().get_user_id())
                                },
                                hash_map::Entry::Vacant(_) => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel".to_owned(), msg.temporary_channel_id))
@@@ -7630,7 -7656,7 +7650,7 @@@ pub mod bench 
                let fee_estimator = test_utils::TestFeeEstimator { sat_per_kw: Mutex::new(253) };
  
                let mut config: UserConfig = Default::default();
 -              config.own_channel_config.minimum_depth = 1;
 +              config.channel_handshake_config.minimum_depth = 1;
  
                let logger_a = test_utils::TestLogger::with_id("node a".to_owned());
                let chain_monitor_a = ChainMonitor::new(None, &tx_broadcaster, &logger_a, &fee_estimator, &persister_a);
index 76487ddcfd16967369bfde624ac1df4a4b77bb83,f82b8d08811ae86d502025ac45fb225fea262f4b..f99a5af6e22bcbd9d56dcad26611efc43104b83e
@@@ -41,6 -41,8 +41,8 @@@ use bitcoin::blockdata::script::Builder
  use bitcoin::blockdata::opcodes;
  use bitcoin::blockdata::constants::genesis_block;
  use bitcoin::network::constants::Network;
+ use bitcoin::{Transaction, TxIn, TxOut, Witness};
+ use bitcoin::OutPoint as BitcoinOutPoint;
  
  use bitcoin::secp256k1::Secp256k1;
  use bitcoin::secp256k1::{PublicKey,SecretKey};
@@@ -61,7 -63,7 +63,7 @@@ 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;
 +      cfg.channel_handshake_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, Some(cfg)]);
@@@ -1790,7 -1792,7 +1792,7 @@@ fn test_channel_reserve_holding_cell_ht
        // When this test was written, the default base fee floated based on the HTLC count.
        // It is now fixed, so we simply set the fee to the expected value here.
        let mut config = test_default_channel_config();
 -      config.channel_options.forwarding_fee_base_msat = 239;
 +      config.channel_config.forwarding_fee_base_msat = 239;
        let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[Some(config.clone()), Some(config.clone()), Some(config.clone())]);
        let mut nodes = create_network(3, &node_cfgs, &node_chanmgrs);
        let chan_1 = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 190000, 1001, InitFeatures::known(), InitFeatures::known());
@@@ -2362,13 -2364,13 +2364,13 @@@ fn channel_monitor_network_test() 
  fn test_justice_tx() {
        // Test justice txn built on revoked HTLC-Success tx, against both sides
        let mut alice_config = UserConfig::default();
 -      alice_config.channel_options.announced_channel = true;
 -      alice_config.peer_channel_config_limits.force_announced_channel_preference = false;
 -      alice_config.own_channel_config.our_to_self_delay = 6 * 24 * 5;
 +      alice_config.channel_handshake_config.announced_channel = true;
 +      alice_config.channel_handshake_limits.force_announced_channel_preference = false;
 +      alice_config.channel_handshake_config.our_to_self_delay = 6 * 24 * 5;
        let mut bob_config = UserConfig::default();
 -      bob_config.channel_options.announced_channel = true;
 -      bob_config.peer_channel_config_limits.force_announced_channel_preference = false;
 -      bob_config.own_channel_config.our_to_self_delay = 6 * 24 * 3;
 +      bob_config.channel_handshake_config.announced_channel = true;
 +      bob_config.channel_handshake_limits.force_announced_channel_preference = false;
 +      bob_config.channel_handshake_config.our_to_self_delay = 6 * 24 * 3;
        let user_cfgs = [Some(alice_config), Some(bob_config)];
        let mut chanmon_cfgs = create_chanmon_cfgs(2);
        chanmon_cfgs[0].keys_manager.disable_revocation_policy_check = true;
@@@ -5275,7 -5277,7 +5277,7 @@@ fn test_duplicate_payment_hash_one_fail
        // When this test was written, the default base fee floated based on the HTLC count.
        // It is now fixed, so we simply set the fee to the expected value here.
        let mut config = test_default_channel_config();
 -      config.channel_options.forwarding_fee_base_msat = 196;
 +      config.channel_config.forwarding_fee_base_msat = 196;
        let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs,
                &[Some(config.clone()), Some(config.clone()), Some(config.clone()), Some(config.clone())]);
        let mut nodes = create_network(4, &node_cfgs, &node_chanmgrs);
@@@ -5487,7 -5489,7 +5489,7 @@@ fn do_test_fail_backwards_unrevoked_rem
        // When this test was written, the default base fee floated based on the HTLC count.
        // It is now fixed, so we simply set the fee to the expected value here.
        let mut config = test_default_channel_config();
 -      config.channel_options.forwarding_fee_base_msat = 196;
 +      config.channel_config.forwarding_fee_base_msat = 196;
        let node_chanmgrs = create_node_chanmgrs(6, &node_cfgs,
                &[Some(config.clone()), Some(config.clone()), Some(config.clone()), Some(config.clone()), Some(config.clone()), Some(config.clone())]);
        let nodes = create_network(6, &node_cfgs, &node_chanmgrs);
@@@ -6369,7 -6371,7 +6371,7 @@@ fn test_fail_holding_cell_htlc_upon_fre
        // When this test was written, the default base fee floated based on the HTLC count.
        // It is now fixed, so we simply set the fee to the expected value here.
        let mut config = test_default_channel_config();
 -      config.channel_options.forwarding_fee_base_msat = 196;
 +      config.channel_config.forwarding_fee_base_msat = 196;
        let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[Some(config.clone()), Some(config.clone()), Some(config.clone())]);
        let mut nodes = create_network(3, &node_cfgs, &node_chanmgrs);
        let chan_0_1 = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 95000000, InitFeatures::known(), InitFeatures::known());
@@@ -7328,9 -7330,9 +7330,9 @@@ fn test_user_configurable_csv_delay() 
        // We test our channel constructors yield errors when we pass them absurd csv delay
  
        let mut low_our_to_self_config = UserConfig::default();
 -      low_our_to_self_config.own_channel_config.our_to_self_delay = 6;
 +      low_our_to_self_config.channel_handshake_config.our_to_self_delay = 6;
        let mut high_their_to_self_config = UserConfig::default();
 -      high_their_to_self_config.peer_channel_config_limits.their_to_self_delay = 100;
 +      high_their_to_self_config.channel_handshake_limits.their_to_self_delay = 100;
        let user_cfgs = [Some(high_their_to_self_config.clone()), None];
        let chanmon_cfgs = create_chanmon_cfgs(2);
        let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
@@@ -8245,7 -8247,7 +8247,7 @@@ fn test_override_channel_config() 
  
        // Node0 initiates a channel to node1 using the override config.
        let mut override_config = UserConfig::default();
 -      override_config.own_channel_config.our_to_self_delay = 200;
 +      override_config.channel_handshake_config.our_to_self_delay = 200;
  
        nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 16_000_000, 12_000_000, 42, Some(override_config)).unwrap();
  
  #[test]
  fn test_override_0msat_htlc_minimum() {
        let mut zero_config = UserConfig::default();
 -      zero_config.own_channel_config.our_htlc_minimum_msat = 0;
 +      zero_config.channel_handshake_config.our_htlc_minimum_msat = 0;
        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, Some(zero_config.clone())]);
@@@ -8282,17 -8284,17 +8284,17 @@@ fn test_channel_update_has_correct_htlc
        // 2. MUST be set to less than or equal to the `max_htlc_value_in_flight_msat` received from the peer.
  
        let mut config_30_percent = UserConfig::default();
 -      config_30_percent.channel_options.announced_channel = true;
 -      config_30_percent.own_channel_config.max_inbound_htlc_value_in_flight_percent_of_channel = 30;
 +      config_30_percent.channel_handshake_config.announced_channel = true;
 +      config_30_percent.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 30;
        let mut config_50_percent = UserConfig::default();
 -      config_50_percent.channel_options.announced_channel = true;
 -      config_50_percent.own_channel_config.max_inbound_htlc_value_in_flight_percent_of_channel = 50;
 +      config_50_percent.channel_handshake_config.announced_channel = true;
 +      config_50_percent.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 50;
        let mut config_95_percent = UserConfig::default();
 -      config_95_percent.channel_options.announced_channel = true;
 -      config_95_percent.own_channel_config.max_inbound_htlc_value_in_flight_percent_of_channel = 95;
 +      config_95_percent.channel_handshake_config.announced_channel = true;
 +      config_95_percent.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 95;
        let mut config_100_percent = UserConfig::default();
 -      config_100_percent.channel_options.announced_channel = true;
 -      config_100_percent.own_channel_config.max_inbound_htlc_value_in_flight_percent_of_channel = 100;
 +      config_100_percent.channel_handshake_config.announced_channel = true;
 +      config_100_percent.channel_handshake_config.max_inbound_htlc_value_in_flight_percent_of_channel = 100;
  
        let chanmon_cfgs = create_chanmon_cfgs(4);
        let node_cfgs = create_node_cfgs(4, &chanmon_cfgs);
@@@ -10182,7 -10184,7 +10184,7 @@@ fn do_test_max_dust_htlc_exposure(dust_
  
        let chanmon_cfgs = create_chanmon_cfgs(2);
        let mut config = test_default_channel_config();
 -      config.channel_options.max_dust_htlc_exposure_msat = 5_000_000; // default setting value
 +      config.channel_config.max_dust_htlc_exposure_msat = 5_000_000; // default setting value
        let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
        let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(config), None]);
        let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
                chan.get_dust_buffer_feerate(None) as u64
        };
        let dust_outbound_htlc_on_holder_tx_msat: u64 = (dust_buffer_feerate * htlc_timeout_tx_weight(opt_anchors) / 1000 + open_channel.dust_limit_satoshis - 1) * 1000;
 -      let dust_outbound_htlc_on_holder_tx: u64 = config.channel_options.max_dust_htlc_exposure_msat / dust_outbound_htlc_on_holder_tx_msat;
 +      let dust_outbound_htlc_on_holder_tx: u64 = config.channel_config.max_dust_htlc_exposure_msat / dust_outbound_htlc_on_holder_tx_msat;
  
        let dust_inbound_htlc_on_holder_tx_msat: u64 = (dust_buffer_feerate * htlc_success_tx_weight(opt_anchors) / 1000 + open_channel.dust_limit_satoshis - 1) * 1000;
 -      let dust_inbound_htlc_on_holder_tx: u64 = config.channel_options.max_dust_htlc_exposure_msat / dust_inbound_htlc_on_holder_tx_msat;
 +      let dust_inbound_htlc_on_holder_tx: u64 = config.channel_config.max_dust_htlc_exposure_msat / dust_inbound_htlc_on_holder_tx_msat;
  
        let dust_htlc_on_counterparty_tx: u64 = 25;
 -      let dust_htlc_on_counterparty_tx_msat: u64 = config.channel_options.max_dust_htlc_exposure_msat / dust_htlc_on_counterparty_tx;
 +      let dust_htlc_on_counterparty_tx_msat: u64 = config.channel_config.max_dust_htlc_exposure_msat / dust_htlc_on_counterparty_tx;
  
        if on_holder_tx {
                if dust_outbound_balance {
                if on_holder_tx {
                        let dust_outbound_overflow = dust_outbound_htlc_on_holder_tx_msat * (dust_outbound_htlc_on_holder_tx + 1);
                        let dust_inbound_overflow = dust_inbound_htlc_on_holder_tx_msat * dust_inbound_htlc_on_holder_tx + dust_outbound_htlc_on_holder_tx_msat;
 -                      unwrap_send_err!(nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret)), true, APIError::ChannelUnavailable { ref err }, assert_eq!(err, &format!("Cannot send value that would put our exposure to dust HTLCs at {} over the limit {} on holder commitment tx", if dust_outbound_balance { dust_outbound_overflow } else { dust_inbound_overflow }, config.channel_options.max_dust_htlc_exposure_msat)));
 +                      unwrap_send_err!(nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret)), true, APIError::ChannelUnavailable { ref err }, assert_eq!(err, &format!("Cannot send value that would put our exposure to dust HTLCs at {} over the limit {} on holder commitment tx", if dust_outbound_balance { dust_outbound_overflow } else { dust_inbound_overflow }, config.channel_config.max_dust_htlc_exposure_msat)));
                } else {
 -                      unwrap_send_err!(nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret)), true, APIError::ChannelUnavailable { ref err }, assert_eq!(err, &format!("Cannot send value that would put our exposure to dust HTLCs at {} over the limit {} on counterparty commitment tx", dust_overflow, config.channel_options.max_dust_htlc_exposure_msat)));
 +                      unwrap_send_err!(nodes[0].node.send_payment(&route, payment_hash, &Some(payment_secret)), true, APIError::ChannelUnavailable { ref err }, assert_eq!(err, &format!("Cannot send value that would put our exposure to dust HTLCs at {} over the limit {} on counterparty commitment tx", dust_overflow, config.channel_config.max_dust_htlc_exposure_msat)));
                }
        } else if exposure_breach_event == ExposureEvent::AtHTLCReception {
                let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[1], nodes[0], if on_holder_tx { dust_inbound_htlc_on_holder_tx_msat } else { dust_htlc_on_counterparty_tx_msat });
                        // Outbound dust balance: 6399 sats
                        let dust_inbound_overflow = dust_inbound_htlc_on_holder_tx_msat * (dust_inbound_htlc_on_holder_tx + 1);
                        let dust_outbound_overflow = dust_outbound_htlc_on_holder_tx_msat * dust_outbound_htlc_on_holder_tx + dust_inbound_htlc_on_holder_tx_msat;
 -                      nodes[0].logger.assert_log("lightning::ln::channel".to_string(), format!("Cannot accept value that would put our exposure to dust HTLCs at {} over the limit {} on holder commitment tx", if dust_outbound_balance { dust_outbound_overflow } else { dust_inbound_overflow }, config.channel_options.max_dust_htlc_exposure_msat), 1);
 +                      nodes[0].logger.assert_log("lightning::ln::channel".to_string(), format!("Cannot accept value that would put our exposure to dust HTLCs at {} over the limit {} on holder commitment tx", if dust_outbound_balance { dust_outbound_overflow } else { dust_inbound_overflow }, config.channel_config.max_dust_htlc_exposure_msat), 1);
                } else {
                        // Outbound dust balance: 5200 sats
 -                      nodes[0].logger.assert_log("lightning::ln::channel".to_string(), format!("Cannot accept value that would put our exposure to dust HTLCs at {} over the limit {} on counterparty commitment tx", dust_overflow, config.channel_options.max_dust_htlc_exposure_msat), 1);
 +                      nodes[0].logger.assert_log("lightning::ln::channel".to_string(), format!("Cannot accept value that would put our exposure to dust HTLCs at {} over the limit {} on counterparty commitment tx", dust_overflow, config.channel_config.max_dust_htlc_exposure_msat), 1);
                }
        } else if exposure_breach_event == ExposureEvent::AtUpdateFeeOutbound {
                let (route, payment_hash, _, payment_secret) = get_route_and_payment_hash!(nodes[0], nodes[1], 2_500_000);
@@@ -10329,3 -10331,45 +10331,45 @@@ fn test_max_dust_htlc_exposure() 
        do_test_max_dust_htlc_exposure(false, ExposureEvent::AtUpdateFeeOutbound, false);
        do_test_max_dust_htlc_exposure(false, ExposureEvent::AtUpdateFeeOutbound, true);
  }
+ #[test]
+ fn test_non_final_funding_tx() {
+       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 nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+       let temp_channel_id = nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100_000, 0, 42, None).unwrap();
+       let open_channel_message = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
+       nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), InitFeatures::known(), &open_channel_message);
+       let accept_channel_message = get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id());
+       nodes[0].node.handle_accept_channel(&nodes[1].node.get_our_node_id(), InitFeatures::known(), &accept_channel_message);
+       let best_height = nodes[0].node.best_block.read().unwrap().height();
+       let chan_id = *nodes[0].network_chan_count.borrow();
+       let events = nodes[0].node.get_and_clear_pending_events();
+       let input = TxIn { previous_output: BitcoinOutPoint::null(), script_sig: bitcoin::Script::new(), sequence: 0x1, witness: Witness::from_vec(vec!(vec!(1))) };
+       assert_eq!(events.len(), 1);
+       let mut tx = match events[0] {
+               Event::FundingGenerationReady { ref channel_value_satoshis, ref output_script, .. } => {
+                       // Timelock the transaction _beyond_ the best client height + 2.
+                       Transaction { version: chan_id as i32, lock_time: best_height + 3, input: vec![input], output: vec![TxOut {
+                               value: *channel_value_satoshis, script_pubkey: output_script.clone(),
+                       }]}
+               },
+               _ => panic!("Unexpected event"),
+       };
+       // Transaction should fail as it's evaluated as non-final for propagation.
+       match nodes[0].node.funding_transaction_generated(&temp_channel_id, &nodes[1].node.get_our_node_id(), tx.clone()) {
+               Err(APIError::APIMisuseError { err }) => {
+                       assert_eq!(format!("Funding transaction absolute timelock is non-final"), err);
+               },
+               _ => panic!()
+       }
+       // However, transaction should be accepted if it's in a +2 headroom from best block.
+       tx.lock_time -= 1;
+       assert!(nodes[0].node.funding_transaction_generated(&temp_channel_id, &nodes[1].node.get_our_node_id(), tx.clone()).is_ok());
+       get_event_msg!(nodes[0], MessageSendEvent::SendFundingCreated, nodes[1].node.get_our_node_id());
+ }