From 9db22d059b065b6ccff074ae72a22d01b7111ffb Mon Sep 17 00:00:00 2001 From: Antoine Riard Date: Thu, 20 Sep 2018 00:52:02 +0000 Subject: [PATCH 1/2] Test htlc outputs shared tx claim case --- src/ln/channelmanager.rs | 65 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/ln/channelmanager.rs b/src/ln/channelmanager.rs index 01faec671c3..68453decc34 100644 --- a/src/ln/channelmanager.rs +++ b/src/ln/channelmanager.rs @@ -3426,6 +3426,71 @@ mod tests { assert_eq!(nodes[1].node.list_channels().len(), 0); } + #[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(); + + //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); + + let mut revoked_tx_map = HashMap::new(); + revoked_tx_map.insert(revoked_local_txn[0].txid(), revoked_local_txn[0].clone()); + + assert_eq!(node_txn[0].input.len(), 2); + node_txn[0].verify(&revoked_tx_map).unwrap(); + + assert_eq!(node_txn[3].input.len(), 2); + node_txn[3].verify(&revoked_tx_map).unwrap(); + + assert_eq!(node_txn[0].txid(), node_txn[3].txid()); // justice tx is duplicated due to block re-scanning + + let witness_script_1 = node_txn[0].clone().input[0].witness.pop().unwrap(); + let witness_script_2 = node_txn[0].clone().input[1].witness.pop().unwrap(); + if witness_script_1.len() > 133 { + assert_eq!(witness_script_1.len(), 138); + assert_eq!(witness_script_2.len(), 133); + } else { + assert_eq!(witness_script_1.len(), 133); + assert_eq!(witness_script_2.len(), 138); + } + + assert_eq!(node_txn[1].input.len(), 1); + let witness_script = node_txn[1].clone().input[0].witness.pop().unwrap(); + assert_eq!(witness_script.len(), 71); // Spending funding tx unique txoutput, 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 a offered htlc output + + } + 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 From f1f9ead1a0559dbea4bef4371321e1fd9eeb1f75 Mon Sep 17 00:00:00 2001 From: Antoine Riard Date: Thu, 20 Sep 2018 00:56:18 +0000 Subject: [PATCH 2/2] Test htlc outputs single tx claim due to timeout case --- src/ln/channelmanager.rs | 83 ++++++++++++++++++++++++++++++++++++++-- src/ln/channelmonitor.rs | 2 +- 2 files changed, 80 insertions(+), 5 deletions(-) diff --git a/src/ln/channelmanager.rs b/src/ln/channelmanager.rs index 68453decc34..249c979d2d1 100644 --- a/src/ln/channelmanager.rs +++ b/src/ln/channelmanager.rs @@ -3465,7 +3465,7 @@ mod tests { assert_eq!(node_txn[3].input.len(), 2); node_txn[3].verify(&revoked_tx_map).unwrap(); - assert_eq!(node_txn[0].txid(), node_txn[3].txid()); // justice tx is duplicated due to block re-scanning + assert_eq!(node_txn[0], node_txn[3]); // justice tx is duplicated due to block re-scanning let witness_script_1 = node_txn[0].clone().input[0].witness.pop().unwrap(); let witness_script_2 = node_txn[0].clone().input[1].witness.pop().unwrap(); @@ -3478,12 +3478,87 @@ mod tests { } assert_eq!(node_txn[1].input.len(), 1); - let witness_script = node_txn[1].clone().input[0].witness.pop().unwrap(); - assert_eq!(witness_script.len(), 71); // Spending funding tx unique txoutput, tx broadcasted by ChannelManager + 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 a offered htlc output + 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(), 10); // ChannelManager : 2, ChannelMontitor: 8 (2 revocation htlc tx, 1 local commitment tx + 1 htlc timeout tx) * 2 (block-rescan) + + assert_eq!(node_txn[0], node_txn[6]); + assert_eq!(node_txn[1], node_txn[7]); + assert_eq!(node_txn[2], node_txn[8]); + assert_eq!(node_txn[3], node_txn[9]); + assert_eq!(node_txn[2], node_txn[4]); //local commitment tx + htlc timeout tx broadcated by ChannelManger + assert_eq!(node_txn[3], node_txn[5]); + + assert_eq!(node_txn[0].input.len(), 1); + assert_eq!(node_txn[1].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(); + + let witness_script_1 = node_txn[0].clone().input[0].witness.pop().unwrap(); + let witness_script_2 = node_txn[1].clone().input[0].witness.pop().unwrap(); + if witness_script_1.len() > 133 { + assert_eq!(witness_script_1.len(), 138); + assert_eq!(witness_script_2.len(), 133); + } else { + assert_eq!(witness_script_1.len(), 133); + assert_eq!(witness_script_2.len(), 138); + } + + let mut funding_tx_map = HashMap::new(); + funding_tx_map.insert(chan_1.3.txid(), chan_1.3.clone()); + node_txn[2].verify(&funding_tx_map).unwrap(); + assert_eq!(node_txn[2].input.len(), 1); + + assert_eq!(node_txn[3].input.len(), 1); + let witness_script = node_txn[3].clone().input[0].witness.pop().unwrap(); + assert_eq!(witness_script.len(), 133); //Spending an offered htlc output + assert_eq!(node_txn[3].input[0].previous_output.txid, node_txn[2].txid()); + assert_ne!(node_txn[3].input[0].previous_output.txid, node_txn[0].input[0].previous_output.txid); + assert_ne!(node_txn[3].input[0].previous_output.txid, node_txn[1].input[0].previous_output.txid); } get_announce_close_broadcast_events(&nodes, 0, 1); diff --git a/src/ln/channelmonitor.rs b/src/ln/channelmonitor.rs index acd63da79e1..9fde2cbb83f 100644 --- a/src/ln/channelmonitor.rs +++ b/src/ln/channelmonitor.rs @@ -847,7 +847,7 @@ impl ChannelMonitor { }; let sighash_parts = bip143::SighashComponents::new(&single_htlc_tx); sign_input!(sighash_parts, single_htlc_tx.input[0], Some(idx), htlc.amount_msat / 1000); - txn_to_broadcast.push(single_htlc_tx); // TODO: This is not yet tested in ChannelManager! + txn_to_broadcast.push(single_htlc_tx); } } }