X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=src%2Fln%2Fchannelmanager.rs;h=bebb4e776853f630224ad053e9beac1a1928a537;hb=aa2021dff5f7a444b26a1db0bb4d895eaeabf71e;hp=8875f9c9eea7cb3e8d61fc140b4cc08c242c2506;hpb=8bfe18cf86b343acdc3cdecbd7e472b390605fbe;p=rust-lightning diff --git a/src/ln/channelmanager.rs b/src/ln/channelmanager.rs index 8875f9c9..bebb4e77 100644 --- a/src/ln/channelmanager.rs +++ b/src/ln/channelmanager.rs @@ -22,7 +22,7 @@ use secp256k1; use chain::chaininterface::{BroadcasterInterface,ChainListener,ChainWatchInterface,FeeEstimator}; use chain::transaction::OutPoint; -use ln::channel::{Channel, ChannelKeys}; +use ln::channel::{Channel, ChannelError, ChannelKeys}; use ln::channelmonitor::ManyChannelMonitor; use ln::router::{Route,RouteHop}; use ln::msgs; @@ -173,6 +173,48 @@ impl MsgHandleErrInternal { fn from_no_close(err: msgs::HandleError) -> Self { Self { err, needs_channel_force_close: false } } + #[inline] + fn from_chan_no_close(err: ChannelError, channel_id: [u8; 32]) -> Self { + Self { + err: match err { + ChannelError::Ignore(msg) => HandleError { + err: msg, + action: Some(msgs::ErrorAction::IgnoreError), + }, + ChannelError::Close(msg) => HandleError { + err: msg, + action: Some(msgs::ErrorAction::SendErrorMessage { + msg: msgs::ErrorMessage { + channel_id, + data: msg.to_string() + }, + }), + }, + }, + needs_channel_force_close: false, + } + } + #[inline] + fn from_chan_maybe_close(err: ChannelError, channel_id: [u8; 32]) -> Self { + Self { + err: match err { + ChannelError::Ignore(msg) => HandleError { + err: msg, + action: Some(msgs::ErrorAction::IgnoreError), + }, + ChannelError::Close(msg) => HandleError { + err: msg, + action: Some(msgs::ErrorAction::SendErrorMessage { + msg: msgs::ErrorMessage { + channel_id, + data: msg.to_string() + }, + }), + }, + }, + needs_channel_force_close: true, + } + } } /// We hold back HTLCs we intend to relay for a random interval in the range (this, 5*this). This @@ -1467,7 +1509,8 @@ impl ChannelManager { } }; - let channel = Channel::new_from_req(&*self.fee_estimator, chan_keys, their_node_id.clone(), msg, 0, false, self.announce_channels_publicly, Arc::clone(&self.logger)).map_err(|e| MsgHandleErrInternal::from_no_close(e))?; + let channel = Channel::new_from_req(&*self.fee_estimator, chan_keys, their_node_id.clone(), msg, 0, false, self.announce_channels_publicly, Arc::clone(&self.logger)) + .map_err(|e| MsgHandleErrInternal::from_chan_no_close(e, msg.temporary_channel_id))?; let accept_msg = channel.get_accept_channel(); channel_state.by_id.insert(channel.channel_id(), channel); Ok(accept_msg) @@ -1868,7 +1911,7 @@ impl ChannelManager { //TODO: here and below MsgHandleErrInternal, #153 case return Err(MsgHandleErrInternal::send_err_msg_no_close("Got a message for a channel from the wrong node!", msg.channel_id)); } - chan.update_fee(&*self.fee_estimator, &msg).map_err(|e| MsgHandleErrInternal::from_maybe_close(e)) + chan.update_fee(&*self.fee_estimator, &msg).map_err(|e| MsgHandleErrInternal::from_chan_maybe_close(e, msg.channel_id)) }, None => return Err(MsgHandleErrInternal::send_err_msg_no_close("Failed to find corresponding channel", msg.channel_id)) } @@ -2326,7 +2369,7 @@ mod tests { use rand::{thread_rng,Rng}; use std::cell::RefCell; - use std::collections::HashMap; + use std::collections::{BTreeSet, HashMap}; use std::default::Default; use std::rc::Rc; use std::sync::{Arc, Mutex}; @@ -2648,6 +2691,17 @@ mod tests { (chan_announcement.1, chan_announcement.2, chan_announcement.3, chan_announcement.4) } + macro_rules! check_spends { + ($tx: expr, $spends_tx: expr) => { + { + let mut funding_tx_map = HashMap::new(); + let spends_tx = $spends_tx; + funding_tx_map.insert(spends_tx.txid(), spends_tx); + $tx.verify(&funding_tx_map).unwrap(); + } + } + } + fn close_channel(outbound_node: &Node, inbound_node: &Node, channel_id: &[u8; 32], funding_tx: Transaction, close_inbound_first: bool) -> (msgs::ChannelUpdate, msgs::ChannelUpdate) { let (node_a, broadcaster_a) = if close_inbound_first { (&inbound_node.node, &inbound_node.tx_broadcaster) } else { (&outbound_node.node, &outbound_node.tx_broadcaster) }; let (node_b, broadcaster_b) = if close_inbound_first { (&outbound_node.node, &outbound_node.tx_broadcaster) } else { (&inbound_node.node, &inbound_node.tx_broadcaster) }; @@ -2691,9 +2745,7 @@ mod tests { tx_a = broadcaster_a.txn_broadcasted.lock().unwrap().remove(0); } assert_eq!(tx_a, tx_b); - let mut funding_tx_map = HashMap::new(); - funding_tx_map.insert(funding_tx.txid(), funding_tx); - tx_a.verify(&funding_tx_map).unwrap(); + check_spends!(tx_a, funding_tx); let events_2 = node_a.get_and_clear_pending_events(); assert_eq!(events_2.len(), 1); @@ -3722,9 +3774,7 @@ mod tests { let mut res = Vec::with_capacity(2); node_txn.retain(|tx| { if tx.input.len() == 1 && tx.input[0].previous_output.txid == chan.3.txid() { - let mut funding_tx_map = HashMap::new(); - funding_tx_map.insert(chan.3.txid(), chan.3.clone()); - tx.verify(&funding_tx_map).unwrap(); + check_spends!(tx, chan.3.clone()); if commitment_tx.is_none() { res.push(tx.clone()); } @@ -3740,9 +3790,7 @@ mod tests { if has_htlc_tx != HTLCType::NONE { node_txn.retain(|tx| { if tx.input.len() == 1 && tx.input[0].previous_output.txid == res[0].txid() { - let mut funding_tx_map = HashMap::new(); - funding_tx_map.insert(res[0].txid(), res[0].clone()); - tx.verify(&funding_tx_map).unwrap(); + check_spends!(tx, res[0].clone()); if has_htlc_tx == HTLCType::TIMEOUT { assert!(tx.lock_time != 0); } else { @@ -3766,9 +3814,7 @@ mod tests { assert_eq!(node_txn.len(), 1); node_txn.retain(|tx| { if tx.input.len() == 1 && tx.input[0].previous_output.txid == revoked_tx.txid() { - let mut funding_tx_map = HashMap::new(); - funding_tx_map.insert(revoked_tx.txid(), revoked_tx.clone()); - tx.verify(&funding_tx_map).unwrap(); + check_spends!(tx, revoked_tx.clone()); false } else { true } }); @@ -3784,10 +3830,7 @@ mod tests { for tx in prev_txn { if node_txn[0].input[0].previous_output.txid == tx.txid() { - let mut funding_tx_map = HashMap::new(); - funding_tx_map.insert(tx.txid(), tx.clone()); - node_txn[0].verify(&funding_tx_map).unwrap(); - + check_spends!(node_txn[0], tx.clone()); assert!(node_txn[0].input[0].witness[2].len() > 106); // must spend an htlc output assert_eq!(tx.input.len(), 1); // must spend a commitment tx @@ -3951,6 +3994,13 @@ mod tests { let payment_preimage_3 = route_payment(&nodes[0], &vec!(&nodes[1])[..], 3000000).0; // Get the will-be-revoked local txn from nodes[0] let revoked_local_txn = nodes[0].node.channel_state.lock().unwrap().by_id.iter().next().unwrap().1.last_local_commitment_txn.clone(); + assert_eq!(revoked_local_txn.len(), 2); // First commitment tx, then HTLC tx + assert_eq!(revoked_local_txn[0].input.len(), 1); + assert_eq!(revoked_local_txn[0].input[0].previous_output.txid, chan_5.3.txid()); + assert_eq!(revoked_local_txn[0].output.len(), 2); // Only HTLC and output back to 0 are present + assert_eq!(revoked_local_txn[1].input.len(), 1); + assert_eq!(revoked_local_txn[1].input[0].previous_output.txid, revoked_local_txn[0].txid()); + assert_eq!(revoked_local_txn[1].input[0].witness.last().unwrap().len(), 133); // HTLC-Timeout // Revoke the old state claim_payment(&nodes[0], &vec!(&nodes[1])[..], payment_preimage_3); @@ -3961,11 +4011,9 @@ mod tests { let mut node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap(); assert_eq!(node_txn.len(), 3); assert_eq!(node_txn.pop().unwrap(), node_txn[0]); // An outpoint registration will result in a 2nd block_connected - assert_eq!(node_txn[0].input.len(), 1); + assert_eq!(node_txn[0].input.len(), 2); // We should claim the revoked output and the HTLC output - let mut funding_tx_map = HashMap::new(); - funding_tx_map.insert(revoked_local_txn[0].txid(), revoked_local_txn[0].clone()); - node_txn[0].verify(&funding_tx_map).unwrap(); + check_spends!(node_txn[0], revoked_local_txn[0].clone()); node_txn.swap_remove(0); } test_txn_broadcast(&nodes[1], &chan_5, None, HTLCType::NONE); @@ -3981,6 +4029,173 @@ mod tests { assert_eq!(nodes[1].node.list_channels().len(), 0); } + #[test] + fn revoked_output_claim() { + // Simple test to ensure a node will claim a revoked output when a stale remote commitment + // transaction is broadcast by its counterparty + let nodes = create_network(2); + let chan_1 = create_announced_chan_between_nodes(&nodes, 0, 1); + // node[0] is gonna to revoke an old state thus node[1] should be able to claim the revoked output + let revoked_local_txn = nodes[0].node.channel_state.lock().unwrap().by_id.get(&chan_1.2).unwrap().last_local_commitment_txn.clone(); + assert_eq!(revoked_local_txn.len(), 1); + // Only output is the full channel value back to nodes[0]: + assert_eq!(revoked_local_txn[0].output.len(), 1); + // Send a payment through, updating everyone's latest commitment txn + send_payment(&nodes[0], &vec!(&nodes[1])[..], 5000000); + + // Inform nodes[1] that nodes[0] broadcast a stale tx + let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; + nodes[1].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 1); + let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap(); + assert_eq!(node_txn.len(), 3); // nodes[1] will broadcast justice tx twice, and its own local state once + + assert_eq!(node_txn[0], node_txn[2]); + + check_spends!(node_txn[0], revoked_local_txn[0].clone()); + check_spends!(node_txn[1], chan_1.3.clone()); + + // Inform nodes[0] that a watchtower cheated on its behalf, so it will force-close the chan + nodes[0].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 1); + get_announce_close_broadcast_events(&nodes, 0, 1); + } + + #[test] + fn claim_htlc_outputs_shared_tx() { + // Node revoked old state, htlcs haven't time out yet, claim them in shared justice tx + let nodes = create_network(2); + + // Create some new channel: + let chan_1 = create_announced_chan_between_nodes(&nodes, 0, 1); + + // Rebalance the network to generate htlc in the two directions + send_payment(&nodes[0], &vec!(&nodes[1])[..], 8000000); + // node[0] is gonna to revoke an old state thus node[1] should be able to claim both offered/received HTLC outputs on top of commitment tx + let payment_preimage_1 = route_payment(&nodes[0], &vec!(&nodes[1])[..], 3000000).0; + let _payment_preimage_2 = route_payment(&nodes[1], &vec!(&nodes[0])[..], 3000000).0; + + // Get the will-be-revoked local txn from node[0] + let revoked_local_txn = nodes[0].node.channel_state.lock().unwrap().by_id.get(&chan_1.2).unwrap().last_local_commitment_txn.clone(); + assert_eq!(revoked_local_txn.len(), 2); // commitment tx + 1 HTLC-Timeout tx + assert_eq!(revoked_local_txn[0].input.len(), 1); + assert_eq!(revoked_local_txn[0].input[0].previous_output.txid, chan_1.3.txid()); + assert_eq!(revoked_local_txn[1].input.len(), 1); + assert_eq!(revoked_local_txn[1].input[0].previous_output.txid, revoked_local_txn[0].txid()); + assert_eq!(revoked_local_txn[1].input[0].witness.last().unwrap().len(), 133); // HTLC-Timeout + check_spends!(revoked_local_txn[1], revoked_local_txn[0].clone()); + + //Revoke the old state + claim_payment(&nodes[0], &vec!(&nodes[1])[..], payment_preimage_1); + + { + let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; + + nodes[0].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 1); + + nodes[1].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 1); + let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap(); + assert_eq!(node_txn.len(), 4); + + assert_eq!(node_txn[0].input.len(), 3); // Claim the revoked output + both revoked HTLC outputs + check_spends!(node_txn[0], revoked_local_txn[0].clone()); + + assert_eq!(node_txn[0], node_txn[3]); // justice tx is duplicated due to block re-scanning + + let mut witness_lens = BTreeSet::new(); + witness_lens.insert(node_txn[0].input[0].witness.last().unwrap().len()); + witness_lens.insert(node_txn[0].input[1].witness.last().unwrap().len()); + witness_lens.insert(node_txn[0].input[2].witness.last().unwrap().len()); + assert_eq!(witness_lens.len(), 3); + assert_eq!(*witness_lens.iter().skip(0).next().unwrap(), 77); // revoked to_local + assert_eq!(*witness_lens.iter().skip(1).next().unwrap(), 133); // revoked offered HTLC + assert_eq!(*witness_lens.iter().skip(2).next().unwrap(), 138); // revoked received HTLC + + // Next nodes[1] broadcasts its current local tx state: + assert_eq!(node_txn[1].input.len(), 1); + assert_eq!(node_txn[1].input[0].previous_output.txid, chan_1.3.txid()); //Spending funding tx unique txouput, tx broadcasted by ChannelManager + + assert_eq!(node_txn[2].input.len(), 1); + let witness_script = node_txn[2].clone().input[0].witness.pop().unwrap(); + assert_eq!(witness_script.len(), 133); //Spending an offered htlc output + assert_eq!(node_txn[2].input[0].previous_output.txid, node_txn[1].txid()); + assert_ne!(node_txn[2].input[0].previous_output.txid, node_txn[0].input[0].previous_output.txid); + assert_ne!(node_txn[2].input[0].previous_output.txid, node_txn[0].input[1].previous_output.txid); + } + get_announce_close_broadcast_events(&nodes, 0, 1); + assert_eq!(nodes[0].node.list_channels().len(), 0); + assert_eq!(nodes[1].node.list_channels().len(), 0); + } + + #[test] + fn claim_htlc_outputs_single_tx() { + // Node revoked old state, htlcs have timed out, claim each of them in separated justice tx + let nodes = create_network(2); + + let chan_1 = create_announced_chan_between_nodes(&nodes, 0, 1); + + // Rebalance the network to generate htlc in the two directions + send_payment(&nodes[0], &vec!(&nodes[1])[..], 8000000); + // node[0] is gonna to revoke an old state thus node[1] should be able to claim both offered/received HTLC outputs on top of commitment tx, but this + // time as two different claim transactions as we're gonna to timeout htlc with given a high current height + let payment_preimage_1 = route_payment(&nodes[0], &vec!(&nodes[1])[..], 3000000).0; + let _payment_preimage_2 = route_payment(&nodes[1], &vec!(&nodes[0])[..], 3000000).0; + + // Get the will-be-revoked local txn from node[0] + let revoked_local_txn = nodes[0].node.channel_state.lock().unwrap().by_id.get(&chan_1.2).unwrap().last_local_commitment_txn.clone(); + + //Revoke the old state + claim_payment(&nodes[0], &vec!(&nodes[1])[..], payment_preimage_1); + + { + let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 }; + + nodes[0].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 200); + + nodes[1].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 200); + let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap(); + assert_eq!(node_txn.len(), 12); // ChannelManager : 2, ChannelMontitor: 8 (1 standard revoked output, 2 revocation htlc tx, 1 local commitment tx + 1 htlc timeout tx) * 2 (block-rescan) + + assert_eq!(node_txn[0], node_txn[7]); + assert_eq!(node_txn[1], node_txn[8]); + assert_eq!(node_txn[2], node_txn[9]); + assert_eq!(node_txn[3], node_txn[10]); + assert_eq!(node_txn[4], node_txn[11]); + assert_eq!(node_txn[3], node_txn[5]); //local commitment tx + htlc timeout tx broadcated by ChannelManger + assert_eq!(node_txn[4], node_txn[6]); + + assert_eq!(node_txn[0].input.len(), 1); + assert_eq!(node_txn[1].input.len(), 1); + assert_eq!(node_txn[2].input.len(), 1); + + let mut revoked_tx_map = HashMap::new(); + revoked_tx_map.insert(revoked_local_txn[0].txid(), revoked_local_txn[0].clone()); + node_txn[0].verify(&revoked_tx_map).unwrap(); + node_txn[1].verify(&revoked_tx_map).unwrap(); + node_txn[2].verify(&revoked_tx_map).unwrap(); + + let mut witness_lens = BTreeSet::new(); + witness_lens.insert(node_txn[0].input[0].witness.last().unwrap().len()); + witness_lens.insert(node_txn[1].input[0].witness.last().unwrap().len()); + witness_lens.insert(node_txn[2].input[0].witness.last().unwrap().len()); + assert_eq!(witness_lens.len(), 3); + assert_eq!(*witness_lens.iter().skip(0).next().unwrap(), 77); // revoked to_local + assert_eq!(*witness_lens.iter().skip(1).next().unwrap(), 133); // revoked offered HTLC + assert_eq!(*witness_lens.iter().skip(2).next().unwrap(), 138); // revoked received HTLC + + assert_eq!(node_txn[3].input.len(), 1); + check_spends!(node_txn[3], chan_1.3.clone()); + + assert_eq!(node_txn[4].input.len(), 1); + let witness_script = node_txn[4].input[0].witness.last().unwrap(); + assert_eq!(witness_script.len(), 133); //Spending an offered htlc output + assert_eq!(node_txn[4].input[0].previous_output.txid, node_txn[3].txid()); + assert_ne!(node_txn[4].input[0].previous_output.txid, node_txn[0].input[0].previous_output.txid); + assert_ne!(node_txn[4].input[0].previous_output.txid, node_txn[1].input[0].previous_output.txid); + } + get_announce_close_broadcast_events(&nodes, 0, 1); + assert_eq!(nodes[0].node.list_channels().len(), 0); + assert_eq!(nodes[1].node.list_channels().len(), 0); + } + #[test] fn test_htlc_ignore_latest_remote_commitment() { // Test that HTLC transactions spending the latest remote commitment transaction are simply @@ -4115,9 +4330,8 @@ mod tests { assert_eq!(node_txn[0].input[0].previous_output.txid, tx.txid()); assert_eq!(node_txn[0].lock_time, 0); // Must be an HTLC-Success assert_eq!(node_txn[0].input[0].witness.len(), 5); // Must be an HTLC-Success - let mut funding_tx_map = HashMap::new(); - funding_tx_map.insert(tx.txid(), tx); - node_txn[0].verify(&funding_tx_map).unwrap(); + + check_spends!(node_txn[0], tx); } #[test]