Merge pull request #1711 from TheBlueMatt/2022-08-0conf-panic
authorMatt Corallo <649246+TheBlueMatt@users.noreply.github.com>
Fri, 9 Sep 2022 20:24:07 +0000 (20:24 +0000)
committerGitHub <noreply@github.com>
Fri, 9 Sep 2022 20:24:07 +0000 (20:24 +0000)
Fix spurious panic on receipt of a block while awaiting funding

1  2 
lightning/src/ln/channel.rs
lightning/src/ln/priv_short_conf_tests.rs

index 624f4d6b688b0e2d81964d39e2deb77e1233af8a,d4da0ccb0aed2b5c4107effff419185d61071bdf..b37550b0d2a91eb909bb997dc3dda39a91de887b
@@@ -795,9 -795,6 +795,9 @@@ pub const MAX_CHAN_DUST_LIMIT_SATOSHIS
  /// See https://github.com/lightning/bolts/issues/905 for more details.
  pub const MIN_CHAN_DUST_LIMIT_SATOSHIS: u64 = 354;
  
 +// Just a reasonable implementation-specific safe lower bound, higher than the dust limit.
 +pub const MIN_THEIR_CHAN_RESERVE_SATOSHIS: u64 = 1000;
 +
  /// Used to return a simple Error back to ChannelManager. Will get converted to a
  /// msgs::ErrorAction::SendErrorMessage or msgs::ErrorAction::IgnoreError as appropriate with our
  /// channel_id in ChannelManager.
@@@ -846,25 -843,16 +846,25 @@@ impl<Signer: Sign> Channel<Signer> 
        }
  
        /// Returns a minimum channel reserve value the remote needs to maintain,
 -      /// required by us.
 +      /// required by us according to the configured or default
 +      /// [`ChannelHandshakeConfig::their_channel_reserve_proportional_millionths`]
        ///
        /// Guaranteed to return a value no larger than channel_value_satoshis
        ///
 -      /// This is used both for new channels and to figure out what reserve value we sent to peers
 -      /// for channels serialized before we included our selected reserve value in the serialized
 -      /// data explicitly.
 -      pub(crate) fn get_holder_selected_channel_reserve_satoshis(channel_value_satoshis: u64) -> u64 {
 +      /// This is used both for outbound and inbound channels and has lower bound
 +      /// of `MIN_THEIR_CHAN_RESERVE_SATOSHIS`.
 +      pub(crate) fn get_holder_selected_channel_reserve_satoshis(channel_value_satoshis: u64, config: &UserConfig) -> u64 {
 +              let calculated_reserve = channel_value_satoshis.saturating_mul(config.channel_handshake_config.their_channel_reserve_proportional_millionths as u64) / 1_000_000;
 +              cmp::min(channel_value_satoshis, cmp::max(calculated_reserve, MIN_THEIR_CHAN_RESERVE_SATOSHIS))
 +      }
 +
 +      /// This is for legacy reasons, present for forward-compatibility.
 +      /// LDK versions older than 0.0.104 don't know how read/handle values other than default
 +      /// from storage. Hence, we use this function to not persist default values of
 +      /// `holder_selected_channel_reserve_satoshis` for channels into storage.
 +      pub(crate) fn get_legacy_default_holder_selected_channel_reserve_satoshis(channel_value_satoshis: u64) -> u64 {
                let (q, _) = channel_value_satoshis.overflowing_div(100);
 -              cmp::min(channel_value_satoshis, cmp::max(q, 1000)) //TODO
 +              cmp::min(channel_value_satoshis, cmp::max(q, 1000))
        }
  
        pub(crate) fn opt_anchors(&self) -> bool {
                if holder_selected_contest_delay < BREAKDOWN_TIMEOUT {
                        return Err(APIError::APIMisuseError {err: format!("Configured with an unreasonable our_to_self_delay ({}) putting user funds at risks", holder_selected_contest_delay)});
                }
 -              let holder_selected_channel_reserve_satoshis = Channel::<Signer>::get_holder_selected_channel_reserve_satoshis(channel_value_satoshis);
 +              let holder_selected_channel_reserve_satoshis = Channel::<Signer>::get_holder_selected_channel_reserve_satoshis(channel_value_satoshis, config);
                if holder_selected_channel_reserve_satoshis < MIN_CHAN_DUST_LIMIT_SATOSHIS {
 +                      // Protocol level safety check in place, although it should never happen because
 +                      // of `MIN_THEIR_CHAN_RESERVE_SATOSHIS`
                        return Err(APIError::APIMisuseError { err: format!("Holder selected channel  reserve below implemention limit dust_limit_satoshis {}", holder_selected_channel_reserve_satoshis) });
                }
  
                        }
                }
  
 -              let holder_selected_channel_reserve_satoshis = Channel::<Signer>::get_holder_selected_channel_reserve_satoshis(msg.funding_satoshis);
 +              let holder_selected_channel_reserve_satoshis = Channel::<Signer>::get_holder_selected_channel_reserve_satoshis(msg.funding_satoshis, config);
                if holder_selected_channel_reserve_satoshis < MIN_CHAN_DUST_LIMIT_SATOSHIS {
 +                      // Protocol level safety check in place, although it should never happen because
 +                      // of `MIN_THEIR_CHAN_RESERVE_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)));
 +                      return Err(ChannelError::Close(format!("Suitable channel reserve not found. remote_channel_reserve was ({})msats. Channel value is ({} - {})msats.", holder_selected_channel_reserve_satoshis * 1000, 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.",
        }
  
        fn check_get_channel_ready(&mut self, height: u32) -> Option<msgs::ChannelReady> {
+               // Called:
+               //  * always when a new block/transactions are confirmed with the new height
+               //  * when funding is signed with a height of 0
                if self.funding_tx_confirmation_height == 0 && self.minimum_depth != Some(0) {
                        return None;
                }
                        // We got a reorg but not enough to trigger a force close, just ignore.
                        false
                } else {
-                       if self.channel_state < ChannelState::ChannelFunded as u32 {
+                       if self.funding_tx_confirmation_height != 0 && self.channel_state < ChannelState::ChannelFunded as u32 {
                                // We should never see a funding transaction on-chain until we've received
                                // funding_signed (if we're an outbound channel), or seen funding_generated (if we're
                                // an inbound channel - before that we have no known funding TXID). The fuzzer,
@@@ -6123,7 -6110,7 +6126,7 @@@ impl<Signer: Sign> Writeable for Channe
                // a different percentage of the channel value then 10%, which older versions of LDK used
                // to set it to before the percentage was made configurable.
                let serialized_holder_selected_reserve =
 -                      if self.holder_selected_channel_reserve_satoshis != Self::get_holder_selected_channel_reserve_satoshis(self.channel_value_satoshis)
 +                      if self.holder_selected_channel_reserve_satoshis != Self::get_legacy_default_holder_selected_channel_reserve_satoshis(self.channel_value_satoshis)
                        { Some(self.holder_selected_channel_reserve_satoshis) } else { None };
  
                let mut old_max_in_flight_percent_config = UserConfig::default().channel_handshake_config;
@@@ -6398,7 -6385,7 +6401,7 @@@ impl<'a, Signer: Sign, K: Deref> Readab
                let mut announcement_sigs = None;
                let mut target_closing_feerate_sats_per_kw = None;
                let mut monitor_pending_finalized_fulfills = Some(Vec::new());
 -              let mut holder_selected_channel_reserve_satoshis = Some(Self::get_holder_selected_channel_reserve_satoshis(channel_value_satoshis));
 +              let mut holder_selected_channel_reserve_satoshis = Some(Self::get_legacy_default_holder_selected_channel_reserve_satoshis(channel_value_satoshis));
                let mut holder_max_htlc_value_in_flight_msat = Some(Self::get_holder_max_htlc_value_in_flight_msat(channel_value_satoshis, &UserConfig::default().channel_handshake_config));
                // Prior to supporting channel type negotiation, all of our channels were static_remotekey
                // only, so we default to that if none was written.
  
  #[cfg(test)]
  mod tests {
 +      use std::cmp;
        use bitcoin::blockdata::script::{Script, Builder};
        use bitcoin::blockdata::transaction::{Transaction, TxOut};
        use bitcoin::blockdata::constants::genesis_block;
        use ln::PaymentHash;
        use ln::channelmanager::{HTLCSource, PaymentId};
        use ln::channel::{Channel, InboundHTLCOutput, OutboundHTLCOutput, InboundHTLCState, OutboundHTLCState, HTLCCandidate, HTLCInitiator};
 -      use ln::channel::{MAX_FUNDING_SATOSHIS_NO_WUMBO, TOTAL_BITCOIN_SUPPLY_SATOSHIS};
 +      use ln::channel::{MAX_FUNDING_SATOSHIS_NO_WUMBO, TOTAL_BITCOIN_SUPPLY_SATOSHIS, MIN_THEIR_CHAN_RESERVE_SATOSHIS};
        use ln::features::{InitFeatures, ChannelTypeFeatures};
        use ln::msgs::{ChannelUpdate, DataLossProtect, DecodeError, OptionalField, UnsignedChannelUpdate, MAX_VALUE_MSAT};
        use ln::script::ShutdownScript;
        use util::errors::APIError;
        use util::test_utils;
        use util::test_utils::OnGetShutdownScriptpubkey;
 -      use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature};
 +      use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature, Scalar};
        use bitcoin::secp256k1::ffi::Signature as FFISignature;
        use bitcoin::secp256k1::{SecretKey,PublicKey};
 +      use bitcoin::secp256k1::ecdh::SharedSecret;
        use bitcoin::secp256k1::ecdsa::RecoverableSignature;
        use bitcoin::hashes::sha256::Hash as Sha256;
        use bitcoin::hashes::Hash;
        use bitcoin::hash_types::WPubkeyHash;
        use bitcoin::bech32::u5;
 +      use bitcoin::PackedLockTime;
        use bitcoin::util::address::WitnessVersion;
        use prelude::*;
  
                type Signer = InMemorySigner;
  
                fn get_node_secret(&self, _recipient: Recipient) -> Result<SecretKey, ()> { panic!(); }
 +              fn ecdh(&self, _recipient: Recipient, _other_key: &PublicKey, _tweak: Option<&Scalar>) -> Result<SharedSecret, ()> { panic!(); }
                fn get_inbound_payment_key_material(&self) -> KeyMaterial { panic!(); }
                fn get_destination_script(&self) -> Script {
                        let secp_ctx = Secp256k1::signing_only();
  
                // Node A --> Node B: funding created
                let output_script = node_a_chan.get_funding_redeemscript();
 -              let tx = Transaction { version: 1, lock_time: 0, input: Vec::new(), output: vec![TxOut {
 +              let tx = Transaction { version: 1, lock_time: PackedLockTime::ZERO, input: Vec::new(), output: vec![TxOut {
                        value: 10000000, script_pubkey: output_script.clone(),
                }]};
                let funding_outpoint = OutPoint{ txid: tx.txid(), index: 0 };
                assert_eq!(chan_8.holder_max_htlc_value_in_flight_msat, chan_8_value_msat);
        }
  
 +      #[test]
 +      fn test_configured_holder_selected_channel_reserve_satoshis() {
 +
 +              // Test that `new_outbound` and `new_from_req` create a channel with the correct
 +              // channel reserves, when `their_channel_reserve_proportional_millionths` is configured.
 +              test_self_and_counterparty_channel_reserve(10_000_000, 0.02, 0.02);
 +
 +              // Test with valid but unreasonably high channel reserves
 +              // Requesting and accepting parties have requested for 49%-49% and 60%-30% channel reserve
 +              test_self_and_counterparty_channel_reserve(10_000_000, 0.49, 0.49);
 +              test_self_and_counterparty_channel_reserve(10_000_000, 0.60, 0.30);
 +
 +              // Test with calculated channel reserve less than lower bound
 +              // i.e `MIN_THEIR_CHAN_RESERVE_SATOSHIS`
 +              test_self_and_counterparty_channel_reserve(100_000, 0.00002, 0.30);
 +
 +              // Test with invalid channel reserves since sum of both is greater than or equal
 +              // to channel value
 +              test_self_and_counterparty_channel_reserve(10_000_000, 0.50, 0.50);
 +              test_self_and_counterparty_channel_reserve(10_000_000, 0.60, 0.50);
 +      }
 +
 +      fn test_self_and_counterparty_channel_reserve(channel_value_satoshis: u64, outbound_selected_channel_reserve_perc: f64, inbound_selected_channel_reserve_perc: f64) {
 +              let fee_est = LowerBoundedFeeEstimator::new(&TestFeeEstimator { fee_est: 15_000 });
 +              let logger = test_utils::TestLogger::new();
 +              let secp_ctx = Secp256k1::new();
 +              let seed = [42; 32];
 +              let network = Network::Testnet;
 +              let keys_provider = test_utils::TestKeysInterface::new(&seed, network);
 +              let outbound_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
 +              let inbound_node_id = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[7; 32]).unwrap());
 +
 +
 +              let mut outbound_node_config = UserConfig::default();
 +              outbound_node_config.channel_handshake_config.their_channel_reserve_proportional_millionths = (outbound_selected_channel_reserve_perc * 1_000_000.0) as u32;
 +              let chan = Channel::<EnforcingSigner>::new_outbound(&&fee_est, &&keys_provider, outbound_node_id, &InitFeatures::known(), channel_value_satoshis, 100_000, 42, &outbound_node_config, 0, 42).unwrap();
 +
 +              let expected_outbound_selected_chan_reserve = cmp::max(MIN_THEIR_CHAN_RESERVE_SATOSHIS, (chan.channel_value_satoshis as f64 * outbound_selected_channel_reserve_perc) as u64);
 +              assert_eq!(chan.holder_selected_channel_reserve_satoshis, expected_outbound_selected_chan_reserve);
 +
 +              let chan_open_channel_msg = chan.get_open_channel(genesis_block(network).header.block_hash());
 +              let mut inbound_node_config = UserConfig::default();
 +              inbound_node_config.channel_handshake_config.their_channel_reserve_proportional_millionths = (inbound_selected_channel_reserve_perc * 1_000_000.0) as u32;
 +
 +              if outbound_selected_channel_reserve_perc + inbound_selected_channel_reserve_perc < 1.0 {
 +                      let chan_inbound_node = Channel::<EnforcingSigner>::new_from_req(&&fee_est, &&keys_provider, inbound_node_id, &InitFeatures::known(), &chan_open_channel_msg, 7, &inbound_node_config, 0, &&logger, 42).unwrap();
 +
 +                      let expected_inbound_selected_chan_reserve = cmp::max(MIN_THEIR_CHAN_RESERVE_SATOSHIS, (chan.channel_value_satoshis as f64 * inbound_selected_channel_reserve_perc) as u64);
 +
 +                      assert_eq!(chan_inbound_node.holder_selected_channel_reserve_satoshis, expected_inbound_selected_chan_reserve);
 +                      assert_eq!(chan_inbound_node.counterparty_selected_channel_reserve_satoshis.unwrap(), expected_outbound_selected_chan_reserve);
 +              } else {
 +                      // Channel Negotiations failed
 +                      let result = Channel::<EnforcingSigner>::new_from_req(&&fee_est, &&keys_provider, inbound_node_id, &InitFeatures::known(), &chan_open_channel_msg, 7, &inbound_node_config, 0, &&logger, 42);
 +                      assert!(result.is_err());
 +              }
 +      }
 +
        #[test]
        fn channel_update() {
                let feeest = LowerBoundedFeeEstimator::new(&TestFeeEstimator{fee_est: 15000});
index acceb43953e896e76364da8cf20c49eac6448a8b,35b5c20c6a39b46e730ff4e62c941dd99b95820c..0977d54c6a828997333f54ef16f95828806ca7a3
@@@ -144,8 -144,8 +144,8 @@@ fn test_priv_forwarding_rejection() 
  
        nodes[0].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::known(), remote_network_address: None });
        nodes[1].node.peer_connected(&nodes[0].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
 -      let as_reestablish = get_event_msg!(nodes[0], MessageSendEvent::SendChannelReestablish, nodes[1].node.get_our_node_id());
 -      let bs_reestablish = get_event_msg!(nodes[1], MessageSendEvent::SendChannelReestablish, nodes[0].node.get_our_node_id());
 +      let as_reestablish = get_chan_reestablish_msgs!(nodes[0], nodes[1]).pop().unwrap();
 +      let bs_reestablish = get_chan_reestablish_msgs!(nodes[1], nodes[0]).pop().unwrap();
        nodes[1].node.handle_channel_reestablish(&nodes[0].node.get_our_node_id(), &as_reestablish);
        nodes[0].node.handle_channel_reestablish(&nodes[1].node.get_our_node_id(), &bs_reestablish);
        get_event_msg!(nodes[0], MessageSendEvent::SendChannelUpdate, nodes[1].node.get_our_node_id());
  
        nodes[1].node.peer_connected(&nodes[2].node.get_our_node_id(), &msgs::Init { features: InitFeatures::known(), remote_network_address: None });
        nodes[2].node.peer_connected(&nodes[1].node.get_our_node_id(), &msgs::Init { features: InitFeatures::empty(), remote_network_address: None });
 -      let bs_reestablish = get_event_msg!(nodes[1], MessageSendEvent::SendChannelReestablish, nodes[2].node.get_our_node_id());
 -      let cs_reestablish = get_event_msg!(nodes[2], MessageSendEvent::SendChannelReestablish, nodes[1].node.get_our_node_id());
 +      let bs_reestablish = get_chan_reestablish_msgs!(nodes[1], nodes[2]).pop().unwrap();
 +      let cs_reestablish = get_chan_reestablish_msgs!(nodes[2], nodes[1]).pop().unwrap();
        nodes[2].node.handle_channel_reestablish(&nodes[1].node.get_our_node_id(), &bs_reestablish);
        nodes[1].node.handle_channel_reestablish(&nodes[2].node.get_our_node_id(), &cs_reestablish);
        get_event_msg!(nodes[1], MessageSendEvent::SendChannelUpdate, nodes[2].node.get_our_node_id());
@@@ -565,6 -565,69 +565,6 @@@ fn test_scid_alias_returned() 
                        .blamed_chan_closed(false).expected_htlc_error_data(0x1000|12, &err_data));
  }
  
 -// Receiver must have been initialized with manually_accept_inbound_channels set to true.
 -fn open_zero_conf_channel<'a, 'b, 'c, 'd>(initiator: &'a Node<'b, 'c, 'd>, receiver: &'a Node<'b, 'c, 'd>, initiator_config: Option<UserConfig>) -> bitcoin::Transaction {
 -      initiator.node.create_channel(receiver.node.get_our_node_id(), 100_000, 10_001, 42, initiator_config).unwrap();
 -      let open_channel = get_event_msg!(initiator, MessageSendEvent::SendOpenChannel, receiver.node.get_our_node_id());
 -
 -      receiver.node.handle_open_channel(&initiator.node.get_our_node_id(), InitFeatures::known(), &open_channel);
 -      let events = receiver.node.get_and_clear_pending_events();
 -      assert_eq!(events.len(), 1);
 -      match events[0] {
 -              Event::OpenChannelRequest { temporary_channel_id, .. } => {
 -                      receiver.node.accept_inbound_channel_from_trusted_peer_0conf(&temporary_channel_id, &initiator.node.get_our_node_id(), 0).unwrap();
 -              },
 -              _ => panic!("Unexpected event"),
 -      };
 -
 -      let mut accept_channel = get_event_msg!(receiver, MessageSendEvent::SendAcceptChannel, initiator.node.get_our_node_id());
 -      assert_eq!(accept_channel.minimum_depth, 0);
 -      initiator.node.handle_accept_channel(&receiver.node.get_our_node_id(), InitFeatures::known(), &accept_channel);
 -
 -      let (temporary_channel_id, tx, _) = create_funding_transaction(&initiator, &receiver.node.get_our_node_id(), 100_000, 42);
 -      initiator.node.funding_transaction_generated(&temporary_channel_id, &receiver.node.get_our_node_id(), tx.clone()).unwrap();
 -      let funding_created = get_event_msg!(initiator, MessageSendEvent::SendFundingCreated, receiver.node.get_our_node_id());
 -
 -      receiver.node.handle_funding_created(&initiator.node.get_our_node_id(), &funding_created);
 -      check_added_monitors!(receiver, 1);
 -      let bs_signed_locked = receiver.node.get_and_clear_pending_msg_events();
 -      assert_eq!(bs_signed_locked.len(), 2);
 -      let as_channel_ready;
 -      match &bs_signed_locked[0] {
 -              MessageSendEvent::SendFundingSigned { node_id, msg } => {
 -                      assert_eq!(*node_id, initiator.node.get_our_node_id());
 -                      initiator.node.handle_funding_signed(&receiver.node.get_our_node_id(), &msg);
 -                      check_added_monitors!(initiator, 1);
 -
 -                      assert_eq!(initiator.tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 1);
 -                      assert_eq!(initiator.tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0)[0], tx);
 -
 -                      as_channel_ready = get_event_msg!(initiator, MessageSendEvent::SendChannelReady, receiver.node.get_our_node_id());
 -              }
 -              _ => panic!("Unexpected event"),
 -      }
 -      match &bs_signed_locked[1] {
 -              MessageSendEvent::SendChannelReady { node_id, msg } => {
 -                      assert_eq!(*node_id, initiator.node.get_our_node_id());
 -                      initiator.node.handle_channel_ready(&receiver.node.get_our_node_id(), &msg);
 -              }
 -              _ => panic!("Unexpected event"),
 -      }
 -
 -      receiver.node.handle_channel_ready(&initiator.node.get_our_node_id(), &as_channel_ready);
 -
 -      let as_channel_update = get_event_msg!(initiator, MessageSendEvent::SendChannelUpdate, receiver.node.get_our_node_id());
 -      let bs_channel_update = get_event_msg!(receiver, MessageSendEvent::SendChannelUpdate, initiator.node.get_our_node_id());
 -
 -      initiator.node.handle_channel_update(&receiver.node.get_our_node_id(), &bs_channel_update);
 -      receiver.node.handle_channel_update(&initiator.node.get_our_node_id(), &as_channel_update);
 -
 -      assert_eq!(initiator.node.list_usable_channels().len(), 1);
 -      assert_eq!(receiver.node.list_usable_channels().len(), 1);
 -
 -      tx
 -}
 -
  #[test]
  fn test_simple_0conf_channel() {
        // If our peer tells us they will accept our channel with 0 confs, and we funded the channel,
@@@ -773,7 -836,7 +773,7 @@@ fn test_public_0conf_channel() 
  
        // This is the default but we force it on anyway
        chan_config.channel_handshake_config.announced_channel = true;
 -      let tx = open_zero_conf_channel(&nodes[0], &nodes[1], Some(chan_config));
 +      let (tx, ..) = open_zero_conf_channel(&nodes[0], &nodes[1], Some(chan_config));
  
        // We can use the channel immediately, but we can't announce it until we get 6+ confirmations
        send_payment(&nodes[0], &[&nodes[1]], 100_000);
@@@ -826,7 -889,7 +826,7 @@@ fn test_0conf_channel_reorg() 
  
        // This is the default but we force it on anyway
        chan_config.channel_handshake_config.announced_channel = true;
 -      let tx = open_zero_conf_channel(&nodes[0], &nodes[1], Some(chan_config));
 +      let (tx, ..) = open_zero_conf_channel(&nodes[0], &nodes[1], Some(chan_config));
  
        // We can use the channel immediately, but we can't announce it until we get 6+ confirmations
        send_payment(&nodes[0], &[&nodes[1]], 100_000);
@@@ -958,3 -1021,45 +958,45 @@@ fn test_zero_conf_accept_reject() 
                _ => panic!(),
        }
  }
+ #[test]
+ fn test_connect_before_funding() {
+       // Tests for a particularly dumb explicit panic that existed prior to 0.0.111 for 0conf
+       // channels. If we received a block while awaiting funding for 0-conf channels we'd hit an
+       // explicit panic when deciding if we should broadcast our channel_ready message.
+       let chanmon_cfgs = create_chanmon_cfgs(2);
+       let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
+       let mut manually_accept_conf = test_default_channel_config();
+       manually_accept_conf.manually_accept_inbound_channels = true;
+       let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, Some(manually_accept_conf)]);
+       let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+       nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100_000, 10_001, 42, None).unwrap();
+       let open_channel = 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);
+       let events = nodes[1].node.get_and_clear_pending_events();
+       assert_eq!(events.len(), 1);
+       match events[0] {
+               Event::OpenChannelRequest { temporary_channel_id, .. } => {
+                       nodes[1].node.accept_inbound_channel_from_trusted_peer_0conf(&temporary_channel_id, &nodes[0].node.get_our_node_id(), 0).unwrap();
+               },
+               _ => panic!("Unexpected event"),
+       };
+       let mut accept_channel = get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id());
+       assert_eq!(accept_channel.minimum_depth, 0);
+       nodes[0].node.handle_accept_channel(&nodes[1].node.get_our_node_id(), InitFeatures::known(), &accept_channel);
+       let events = nodes[0].node.get_and_clear_pending_events();
+       assert_eq!(events.len(), 1);
+       match events[0] {
+               Event::FundingGenerationReady { .. } => {},
+               _ => panic!("Unexpected event"),
+       }
+       connect_blocks(&nodes[0], 1);
+       connect_blocks(&nodes[1], 1);
+ }