From: Matt Corallo <649246+TheBlueMatt@users.noreply.github.com> Date: Fri, 20 Jul 2018 03:05:22 +0000 (-0400) Subject: Merge pull request #47 from ariard/block_disconnected_close_chan X-Git-Tag: v0.0.12~386 X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=commitdiff_plain;h=de523c4ca38c4f671df11fa4c7f5781ba0ab00fc;hp=-c;p=rust-lightning Merge pull request #47 from ariard/block_disconnected_close_chan Implement channel closing for block_disconnected on ChainListener + test --- de523c4ca38c4f671df11fa4c7f5781ba0ab00fc diff --combined src/ln/channel.rs index ded81f60,6c04ff1d..9e9ff8c8 --- a/src/ln/channel.rs +++ b/src/ln/channel.rs @@@ -231,7 -231,7 +231,7 @@@ const BOTH_SIDES_SHUTDOWN_MASK: u32 = ( // TODO: We should refactor this to be an Inbound/OutboundChannel until initial setup handshaking // has been completed, and then turn into a Channel to get compiler-time enforcement of things like - // calling get_channel_id() before we're set up or things like get_outbound_funding_signed on an + // calling channel_id() before we're set up or things like get_outbound_funding_signed on an // inbound channel. pub struct Channel { user_id: u64, @@@ -245,10 -245,6 +245,10 @@@ local_keys: ChannelKeys, + // Our commitment numbers start at 2^48-1 and count down, whereas the ones used in transaction + // generation start at 0 and count up...this simplifies some parts of implementation at the + // cost of others, but should really just be changed. + cur_local_commitment_transaction_number: u64, cur_remote_commitment_transaction_number: u64, value_to_self_msat: u64, // Excluding all pending_htlcs, excluding fees @@@ -347,7 -343,7 +347,7 @@@ impl Channel /// Guaranteed to return a value no larger than channel_value_satoshis fn get_our_channel_reserve_satoshis(channel_value_satoshis: u64) -> u64 { - cmp::min(channel_value_satoshis, 10) //TODO + cmp::min(channel_value_satoshis, 1000) //TODO } fn derive_our_dust_limit_satoshis(at_open_background_feerate: u64) -> u64 { @@@ -460,6 -456,9 +460,6 @@@ if msg.dust_limit_satoshis > msg.funding_satoshis { return Err(HandleError{err: "Peer never wants payout outputs?", msg: Some(msgs::ErrorAction::DisconnectPeer{})}); } - if msg.max_htlc_value_in_flight_msat > msg.funding_satoshis * 1000 { - return Err(HandleError{err: "Bogus max_htlc_value_in_flight_satoshis", msg: Some(msgs::ErrorAction::DisconnectPeer{})}); - } if msg.htlc_minimum_msat >= (msg.funding_satoshis - msg.channel_reserve_satoshis) * 1000 { return Err(HandleError{err: "Minimum htlc value is full channel value", msg: Some(msgs::ErrorAction::DisconnectPeer{})}); } @@@ -522,7 -521,7 +522,7 @@@ channel_value_satoshis: msg.funding_satoshis, their_dust_limit_satoshis: msg.dust_limit_satoshis, our_dust_limit_satoshis: Channel::derive_our_dust_limit_satoshis(background_feerate), - their_max_htlc_value_in_flight_msat: msg.max_htlc_value_in_flight_msat, + their_max_htlc_value_in_flight_msat: cmp::min(msg.max_htlc_value_in_flight_msat, msg.funding_satoshis * 1000), their_channel_reserve_satoshis: msg.channel_reserve_satoshis, their_htlc_minimum_msat: msg.htlc_minimum_msat, our_htlc_minimum_msat: Channel::derive_our_htlc_minimum_msat(msg.feerate_per_kw as u64), @@@ -596,7 -595,7 +596,7 @@@ /// which peer generated this transaction and "to whom" this transaction flows. #[inline] fn build_commitment_transaction(&self, commitment_number: u64, keys: &TxCreationKeys, local: bool, generated_by_local: bool) -> (Transaction, Vec) { - let obscured_commitment_transaction_number = self.get_commitment_transaction_number_obscure_factor() ^ commitment_number; + let obscured_commitment_transaction_number = self.get_commitment_transaction_number_obscure_factor() ^ (0xffffffffffff - commitment_number); let txins = { let mut ins: Vec = Vec::new(); @@@ -1082,6 -1081,9 +1082,6 @@@ if msg.dust_limit_satoshis > 21000000 * 100000000 { return Err(HandleError{err: "Peer never wants payout outputs?", msg: None}); } - if msg.max_htlc_value_in_flight_msat > self.channel_value_satoshis * 1000 { - return Err(HandleError{err: "Bogus max_htlc_value_in_flight_satoshis", msg: None}); - } if msg.channel_reserve_satoshis > self.channel_value_satoshis { return Err(HandleError{err: "Bogus channel_reserve_satoshis", msg: None}); } @@@ -1099,7 -1101,7 +1099,7 @@@ self.channel_monitor.set_their_htlc_base_key(&msg.htlc_basepoint); self.their_dust_limit_satoshis = msg.dust_limit_satoshis; - self.their_max_htlc_value_in_flight_msat = msg.max_htlc_value_in_flight_msat; + self.their_max_htlc_value_in_flight_msat = cmp::min(msg.max_htlc_value_in_flight_msat, self.channel_value_satoshis * 1000); self.their_channel_reserve_satoshis = msg.channel_reserve_satoshis; self.their_htlc_minimum_msat = msg.htlc_minimum_msat; self.their_to_self_delay = msg.to_self_delay; @@@ -1150,8 -1152,7 +1150,8 @@@ } let funding_txo = OutPoint::new(msg.funding_txid, msg.funding_output_index); - self.channel_monitor.set_funding_info(funding_txo); + let funding_txo_script = self.get_funding_redeemscript().to_v0_p2wsh(); + self.channel_monitor.set_funding_info((funding_txo, funding_txo_script)); let (remote_initial_commitment_tx, our_signature) = match self.funding_created_signature(&msg.signature) { Ok(res) => res, @@@ -2054,8 -2055,7 +2054,8 @@@ panic!("Should not have advanced channel commitment tx numbers prior to funding_created"); } - self.channel_monitor.set_funding_info(funding_txo); + let funding_txo_script = self.get_funding_redeemscript().to_v0_p2wsh(); + self.channel_monitor.set_funding_info((funding_txo, funding_txo_script)); let (our_signature, commitment_tx) = match self.get_outbound_funding_created_signature() { Ok(res) => res, @@@ -2326,7 -2326,6 +2326,7 @@@ mod tests use bitcoin::util::hash::Sha256dHash; use bitcoin::util::bip143; use bitcoin::network::serialize::serialize; + use bitcoin::blockdata::script::Script; use bitcoin::blockdata::transaction::Transaction; use ln::channel::{Channel,ChannelKeys,HTLCOutput,HTLCState,HTLCOutputInCommitment,TxCreationKeys}; use ln::channel::MAX_FUNDING_SATOSHIS; @@@ -2379,7 -2378,7 +2379,7 @@@ chan.our_dust_limit_satoshis = 546; let funding_info = OutPoint::new(Sha256dHash::from_hex("8984484a580b825b9972d7adb15050b3ab624ccd731946b3eeddb92f4e7ef6be").unwrap(), 0); - chan.channel_monitor.set_funding_info(funding_info); + chan.channel_monitor.set_funding_info((funding_info, Script::new())); chan.their_payment_basepoint = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&secp_ctx, &hex_bytes("4444444444444444444444444444444444444444444444444444444444444444").unwrap()[..]).unwrap()).unwrap(); assert_eq!(chan.their_payment_basepoint.serialize()[..], @@@ -2408,7 -2407,7 +2408,7 @@@ macro_rules! test_commitment { ( $their_sig_hex: expr, $our_sig_hex: expr, $tx_hex: expr) => { - unsigned_tx = chan.build_commitment_transaction(42, &keys, true, false); + unsigned_tx = chan.build_commitment_transaction(0xffffffffffff - 42, &keys, true, false); let their_signature = Signature::from_der(&secp_ctx, &hex_bytes($their_sig_hex).unwrap()[..]).unwrap(); let sighash = Message::from_slice(&bip143::SighashComponents::new(&unsigned_tx.0).sighash_all(&unsigned_tx.0.input[0], &chan.get_funding_redeemscript(), chan.channel_value_satoshis)[..]).unwrap(); secp_ctx.verify(&sighash, &their_signature, &chan.their_funding_pubkey).unwrap(); diff --combined src/ln/channelmanager.rs index f5af09a7,725f598c..4ba6e3d6 --- a/src/ln/channelmanager.rs +++ b/src/ln/channelmanager.rs @@@ -754,7 -754,7 +754,7 @@@ impl ChannelManager match forward_chan.send_htlc(forward_info.amt_to_forward, forward_info.payment_hash, forward_info.outgoing_cltv_value, forward_info.onion_packet.unwrap()) { Err(_e) => { let chan_update = self.get_channel_update(forward_chan).unwrap(); - failed_forwards.push((forward_info.payment_hash, 0x4000 | 7, Some(chan_update))); + failed_forwards.push((forward_info.payment_hash, 0x1000 | 7, Some(chan_update))); continue; }, Ok(update_add) => { @@@ -1085,13 -1085,25 +1085,25 @@@ impl ChainListener for ChannelManager } } + /// We force-close the channel without letting our counterparty participate in the shutdown fn block_disconnected(&self, header: &BlockHeader) { - let mut channel_state = self.channel_state.lock().unwrap(); - for channel in channel_state.by_id.values_mut() { - if channel.block_disconnected(header) { - //TODO Close channel here + let mut channel_lock = self.channel_state.lock().unwrap(); + let channel_state = channel_lock.borrow_parts(); + let short_to_id = channel_state.short_to_id; + channel_state.by_id.retain(|_, v| { + if v.block_disconnected(header) { + let tx = v.force_shutdown(); + for broadcast_tx in tx { + self.tx_broadcaster.broadcast_transaction(&broadcast_tx); + } + if let Some(short_id) = v.get_short_channel_id() { + short_to_id.remove(&short_id); + } + false + } else { + true } - } + }); } } @@@ -1465,7 -1477,7 +1477,7 @@@ impl ChannelMessageHandler for ChannelM let chan = channel_state.by_id.get_mut(&forwarding_id).unwrap(); if !chan.is_live() { let chan_update = self.get_channel_update(chan).unwrap(); - return_err!("Forwarding channel is not in a ready state.", 0x4000 | 7, &chan_update.encode_with_len()[..]); + return_err!("Forwarding channel is not in a ready state.", 0x1000 | 7, &chan_update.encode_with_len()[..]); } } @@@ -1816,6 -1828,7 +1828,7 @@@ mod tests { use chain::chaininterface; use chain::transaction::OutPoint; + use chain::chaininterface::ChainListener; use ln::channelmanager::{ChannelManager,OnionKeys}; use ln::router::{Route, RouteHop, Router}; use ln::msgs; @@@ -2919,4 -2932,30 +2932,30 @@@ assert_eq!(node.chan_monitor.added_monitors.lock().unwrap().len(), 0); } } + + #[test] + fn test_unconf_chan() { + // After creating a chan between nodes, we disconnect all blocks previously seen to force a channel close on nodes[0] side + let nodes = create_network(2); + create_announced_chan_between_nodes(&nodes, 0, 1); + + let channel_state = nodes[0].node.channel_state.lock().unwrap(); + assert_eq!(channel_state.by_id.len(), 1); + assert_eq!(channel_state.short_to_id.len(), 1); + mem::drop(channel_state); + + let mut headers = Vec::new(); + let mut header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; + headers.push(header.clone()); + for _i in 2..100 { + header = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; + headers.push(header.clone()); + } + while !headers.is_empty() { + nodes[0].node.block_disconnected(&headers.pop().unwrap()); + } + let channel_state = nodes[0].node.channel_state.lock().unwrap(); + assert_eq!(channel_state.by_id.len(), 0); + assert_eq!(channel_state.short_to_id.len(), 0); + } }