Skip to content

Commit 6c1123c

Browse files
authored
Merge pull request #199 from TheBlueMatt/2018-09-184-fixed-monitor
Fix simple to_local revoked output claim and rebase #184
2 parents 8bfe18c + af29adc commit 6c1123c

File tree

2 files changed

+197
-26
lines changed

2 files changed

+197
-26
lines changed

src/ln/channelmanager.rs

Lines changed: 195 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2326,7 +2326,7 @@ mod tests {
23262326
use rand::{thread_rng,Rng};
23272327

23282328
use std::cell::RefCell;
2329-
use std::collections::HashMap;
2329+
use std::collections::{BTreeSet, HashMap};
23302330
use std::default::Default;
23312331
use std::rc::Rc;
23322332
use std::sync::{Arc, Mutex};
@@ -2648,6 +2648,17 @@ mod tests {
26482648
(chan_announcement.1, chan_announcement.2, chan_announcement.3, chan_announcement.4)
26492649
}
26502650

2651+
macro_rules! check_spends {
2652+
($tx: expr, $spends_tx: expr) => {
2653+
{
2654+
let mut funding_tx_map = HashMap::new();
2655+
let spends_tx = $spends_tx;
2656+
funding_tx_map.insert(spends_tx.txid(), spends_tx);
2657+
$tx.verify(&funding_tx_map).unwrap();
2658+
}
2659+
}
2660+
}
2661+
26512662
fn close_channel(outbound_node: &Node, inbound_node: &Node, channel_id: &[u8; 32], funding_tx: Transaction, close_inbound_first: bool) -> (msgs::ChannelUpdate, msgs::ChannelUpdate) {
26522663
let (node_a, broadcaster_a) = if close_inbound_first { (&inbound_node.node, &inbound_node.tx_broadcaster) } else { (&outbound_node.node, &outbound_node.tx_broadcaster) };
26532664
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 +2702,7 @@ mod tests {
26912702
tx_a = broadcaster_a.txn_broadcasted.lock().unwrap().remove(0);
26922703
}
26932704
assert_eq!(tx_a, tx_b);
2694-
let mut funding_tx_map = HashMap::new();
2695-
funding_tx_map.insert(funding_tx.txid(), funding_tx);
2696-
tx_a.verify(&funding_tx_map).unwrap();
2705+
check_spends!(tx_a, funding_tx);
26972706

26982707
let events_2 = node_a.get_and_clear_pending_events();
26992708
assert_eq!(events_2.len(), 1);
@@ -3722,9 +3731,7 @@ mod tests {
37223731
let mut res = Vec::with_capacity(2);
37233732
node_txn.retain(|tx| {
37243733
if tx.input.len() == 1 && tx.input[0].previous_output.txid == chan.3.txid() {
3725-
let mut funding_tx_map = HashMap::new();
3726-
funding_tx_map.insert(chan.3.txid(), chan.3.clone());
3727-
tx.verify(&funding_tx_map).unwrap();
3734+
check_spends!(tx, chan.3.clone());
37283735
if commitment_tx.is_none() {
37293736
res.push(tx.clone());
37303737
}
@@ -3740,9 +3747,7 @@ mod tests {
37403747
if has_htlc_tx != HTLCType::NONE {
37413748
node_txn.retain(|tx| {
37423749
if tx.input.len() == 1 && tx.input[0].previous_output.txid == res[0].txid() {
3743-
let mut funding_tx_map = HashMap::new();
3744-
funding_tx_map.insert(res[0].txid(), res[0].clone());
3745-
tx.verify(&funding_tx_map).unwrap();
3750+
check_spends!(tx, res[0].clone());
37463751
if has_htlc_tx == HTLCType::TIMEOUT {
37473752
assert!(tx.lock_time != 0);
37483753
} else {
@@ -3766,9 +3771,7 @@ mod tests {
37663771
assert_eq!(node_txn.len(), 1);
37673772
node_txn.retain(|tx| {
37683773
if tx.input.len() == 1 && tx.input[0].previous_output.txid == revoked_tx.txid() {
3769-
let mut funding_tx_map = HashMap::new();
3770-
funding_tx_map.insert(revoked_tx.txid(), revoked_tx.clone());
3771-
tx.verify(&funding_tx_map).unwrap();
3774+
check_spends!(tx, revoked_tx.clone());
37723775
false
37733776
} else { true }
37743777
});
@@ -3784,10 +3787,7 @@ mod tests {
37843787

37853788
for tx in prev_txn {
37863789
if node_txn[0].input[0].previous_output.txid == tx.txid() {
3787-
let mut funding_tx_map = HashMap::new();
3788-
funding_tx_map.insert(tx.txid(), tx.clone());
3789-
node_txn[0].verify(&funding_tx_map).unwrap();
3790-
3790+
check_spends!(node_txn[0], tx.clone());
37913791
assert!(node_txn[0].input[0].witness[2].len() > 106); // must spend an htlc output
37923792
assert_eq!(tx.input.len(), 1); // must spend a commitment tx
37933793

@@ -3951,6 +3951,13 @@ mod tests {
39513951
let payment_preimage_3 = route_payment(&nodes[0], &vec!(&nodes[1])[..], 3000000).0;
39523952
// Get the will-be-revoked local txn from nodes[0]
39533953
let revoked_local_txn = nodes[0].node.channel_state.lock().unwrap().by_id.iter().next().unwrap().1.last_local_commitment_txn.clone();
3954+
assert_eq!(revoked_local_txn.len(), 2); // First commitment tx, then HTLC tx
3955+
assert_eq!(revoked_local_txn[0].input.len(), 1);
3956+
assert_eq!(revoked_local_txn[0].input[0].previous_output.txid, chan_5.3.txid());
3957+
assert_eq!(revoked_local_txn[0].output.len(), 2); // Only HTLC and output back to 0 are present
3958+
assert_eq!(revoked_local_txn[1].input.len(), 1);
3959+
assert_eq!(revoked_local_txn[1].input[0].previous_output.txid, revoked_local_txn[0].txid());
3960+
assert_eq!(revoked_local_txn[1].input[0].witness.last().unwrap().len(), 133); // HTLC-Timeout
39543961
// Revoke the old state
39553962
claim_payment(&nodes[0], &vec!(&nodes[1])[..], payment_preimage_3);
39563963

@@ -3961,11 +3968,9 @@ mod tests {
39613968
let mut node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap();
39623969
assert_eq!(node_txn.len(), 3);
39633970
assert_eq!(node_txn.pop().unwrap(), node_txn[0]); // An outpoint registration will result in a 2nd block_connected
3964-
assert_eq!(node_txn[0].input.len(), 1);
3971+
assert_eq!(node_txn[0].input.len(), 2); // We should claim the revoked output and the HTLC output
39653972

3966-
let mut funding_tx_map = HashMap::new();
3967-
funding_tx_map.insert(revoked_local_txn[0].txid(), revoked_local_txn[0].clone());
3968-
node_txn[0].verify(&funding_tx_map).unwrap();
3973+
check_spends!(node_txn[0], revoked_local_txn[0].clone());
39693974
node_txn.swap_remove(0);
39703975
}
39713976
test_txn_broadcast(&nodes[1], &chan_5, None, HTLCType::NONE);
@@ -3981,6 +3986,173 @@ mod tests {
39813986
assert_eq!(nodes[1].node.list_channels().len(), 0);
39823987
}
39833988

3989+
#[test]
3990+
fn revoked_output_claim() {
3991+
// Simple test to ensure a node will claim a revoked output when a stale remote commitment
3992+
// transaction is broadcast by its counterparty
3993+
let nodes = create_network(2);
3994+
let chan_1 = create_announced_chan_between_nodes(&nodes, 0, 1);
3995+
// node[0] is gonna to revoke an old state thus node[1] should be able to claim the revoked output
3996+
let revoked_local_txn = nodes[0].node.channel_state.lock().unwrap().by_id.get(&chan_1.2).unwrap().last_local_commitment_txn.clone();
3997+
assert_eq!(revoked_local_txn.len(), 1);
3998+
// Only output is the full channel value back to nodes[0]:
3999+
assert_eq!(revoked_local_txn[0].output.len(), 1);
4000+
// Send a payment through, updating everyone's latest commitment txn
4001+
send_payment(&nodes[0], &vec!(&nodes[1])[..], 5000000);
4002+
4003+
// Inform nodes[1] that nodes[0] broadcast a stale tx
4004+
let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
4005+
nodes[1].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 1);
4006+
let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap();
4007+
assert_eq!(node_txn.len(), 3); // nodes[1] will broadcast justice tx twice, and its own local state once
4008+
4009+
assert_eq!(node_txn[0], node_txn[2]);
4010+
4011+
check_spends!(node_txn[0], revoked_local_txn[0].clone());
4012+
check_spends!(node_txn[1], chan_1.3.clone());
4013+
4014+
// Inform nodes[0] that a watchtower cheated on its behalf, so it will force-close the chan
4015+
nodes[0].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 1);
4016+
get_announce_close_broadcast_events(&nodes, 0, 1);
4017+
}
4018+
4019+
#[test]
4020+
fn claim_htlc_outputs_shared_tx() {
4021+
// Node revoked old state, htlcs haven't time out yet, claim them in shared justice tx
4022+
let nodes = create_network(2);
4023+
4024+
// Create some new channel:
4025+
let chan_1 = create_announced_chan_between_nodes(&nodes, 0, 1);
4026+
4027+
// Rebalance the network to generate htlc in the two directions
4028+
send_payment(&nodes[0], &vec!(&nodes[1])[..], 8000000);
4029+
// 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
4030+
let payment_preimage_1 = route_payment(&nodes[0], &vec!(&nodes[1])[..], 3000000).0;
4031+
let _payment_preimage_2 = route_payment(&nodes[1], &vec!(&nodes[0])[..], 3000000).0;
4032+
4033+
// Get the will-be-revoked local txn from node[0]
4034+
let revoked_local_txn = nodes[0].node.channel_state.lock().unwrap().by_id.get(&chan_1.2).unwrap().last_local_commitment_txn.clone();
4035+
assert_eq!(revoked_local_txn.len(), 2); // commitment tx + 1 HTLC-Timeout tx
4036+
assert_eq!(revoked_local_txn[0].input.len(), 1);
4037+
assert_eq!(revoked_local_txn[0].input[0].previous_output.txid, chan_1.3.txid());
4038+
assert_eq!(revoked_local_txn[1].input.len(), 1);
4039+
assert_eq!(revoked_local_txn[1].input[0].previous_output.txid, revoked_local_txn[0].txid());
4040+
assert_eq!(revoked_local_txn[1].input[0].witness.last().unwrap().len(), 133); // HTLC-Timeout
4041+
check_spends!(revoked_local_txn[1], revoked_local_txn[0].clone());
4042+
4043+
//Revoke the old state
4044+
claim_payment(&nodes[0], &vec!(&nodes[1])[..], payment_preimage_1);
4045+
4046+
{
4047+
let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
4048+
4049+
nodes[0].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 1);
4050+
4051+
nodes[1].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 1);
4052+
let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap();
4053+
assert_eq!(node_txn.len(), 4);
4054+
4055+
assert_eq!(node_txn[0].input.len(), 3); // Claim the revoked output + both revoked HTLC outputs
4056+
check_spends!(node_txn[0], revoked_local_txn[0].clone());
4057+
4058+
assert_eq!(node_txn[0], node_txn[3]); // justice tx is duplicated due to block re-scanning
4059+
4060+
let mut witness_lens = BTreeSet::new();
4061+
witness_lens.insert(node_txn[0].input[0].witness.last().unwrap().len());
4062+
witness_lens.insert(node_txn[0].input[1].witness.last().unwrap().len());
4063+
witness_lens.insert(node_txn[0].input[2].witness.last().unwrap().len());
4064+
assert_eq!(witness_lens.len(), 3);
4065+
assert_eq!(*witness_lens.iter().skip(0).next().unwrap(), 77); // revoked to_local
4066+
assert_eq!(*witness_lens.iter().skip(1).next().unwrap(), 133); // revoked offered HTLC
4067+
assert_eq!(*witness_lens.iter().skip(2).next().unwrap(), 138); // revoked received HTLC
4068+
4069+
// Next nodes[1] broadcasts its current local tx state:
4070+
assert_eq!(node_txn[1].input.len(), 1);
4071+
assert_eq!(node_txn[1].input[0].previous_output.txid, chan_1.3.txid()); //Spending funding tx unique txouput, tx broadcasted by ChannelManager
4072+
4073+
assert_eq!(node_txn[2].input.len(), 1);
4074+
let witness_script = node_txn[2].clone().input[0].witness.pop().unwrap();
4075+
assert_eq!(witness_script.len(), 133); //Spending an offered htlc output
4076+
assert_eq!(node_txn[2].input[0].previous_output.txid, node_txn[1].txid());
4077+
assert_ne!(node_txn[2].input[0].previous_output.txid, node_txn[0].input[0].previous_output.txid);
4078+
assert_ne!(node_txn[2].input[0].previous_output.txid, node_txn[0].input[1].previous_output.txid);
4079+
}
4080+
get_announce_close_broadcast_events(&nodes, 0, 1);
4081+
assert_eq!(nodes[0].node.list_channels().len(), 0);
4082+
assert_eq!(nodes[1].node.list_channels().len(), 0);
4083+
}
4084+
4085+
#[test]
4086+
fn claim_htlc_outputs_single_tx() {
4087+
// Node revoked old state, htlcs have timed out, claim each of them in separated justice tx
4088+
let nodes = create_network(2);
4089+
4090+
let chan_1 = create_announced_chan_between_nodes(&nodes, 0, 1);
4091+
4092+
// Rebalance the network to generate htlc in the two directions
4093+
send_payment(&nodes[0], &vec!(&nodes[1])[..], 8000000);
4094+
// 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
4095+
// time as two different claim transactions as we're gonna to timeout htlc with given a high current height
4096+
let payment_preimage_1 = route_payment(&nodes[0], &vec!(&nodes[1])[..], 3000000).0;
4097+
let _payment_preimage_2 = route_payment(&nodes[1], &vec!(&nodes[0])[..], 3000000).0;
4098+
4099+
// Get the will-be-revoked local txn from node[0]
4100+
let revoked_local_txn = nodes[0].node.channel_state.lock().unwrap().by_id.get(&chan_1.2).unwrap().last_local_commitment_txn.clone();
4101+
4102+
//Revoke the old state
4103+
claim_payment(&nodes[0], &vec!(&nodes[1])[..], payment_preimage_1);
4104+
4105+
{
4106+
let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
4107+
4108+
nodes[0].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 200);
4109+
4110+
nodes[1].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 200);
4111+
let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap();
4112+
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)
4113+
4114+
assert_eq!(node_txn[0], node_txn[7]);
4115+
assert_eq!(node_txn[1], node_txn[8]);
4116+
assert_eq!(node_txn[2], node_txn[9]);
4117+
assert_eq!(node_txn[3], node_txn[10]);
4118+
assert_eq!(node_txn[4], node_txn[11]);
4119+
assert_eq!(node_txn[3], node_txn[5]); //local commitment tx + htlc timeout tx broadcated by ChannelManger
4120+
assert_eq!(node_txn[4], node_txn[6]);
4121+
4122+
assert_eq!(node_txn[0].input.len(), 1);
4123+
assert_eq!(node_txn[1].input.len(), 1);
4124+
assert_eq!(node_txn[2].input.len(), 1);
4125+
4126+
let mut revoked_tx_map = HashMap::new();
4127+
revoked_tx_map.insert(revoked_local_txn[0].txid(), revoked_local_txn[0].clone());
4128+
node_txn[0].verify(&revoked_tx_map).unwrap();
4129+
node_txn[1].verify(&revoked_tx_map).unwrap();
4130+
node_txn[2].verify(&revoked_tx_map).unwrap();
4131+
4132+
let mut witness_lens = BTreeSet::new();
4133+
witness_lens.insert(node_txn[0].input[0].witness.last().unwrap().len());
4134+
witness_lens.insert(node_txn[1].input[0].witness.last().unwrap().len());
4135+
witness_lens.insert(node_txn[2].input[0].witness.last().unwrap().len());
4136+
assert_eq!(witness_lens.len(), 3);
4137+
assert_eq!(*witness_lens.iter().skip(0).next().unwrap(), 77); // revoked to_local
4138+
assert_eq!(*witness_lens.iter().skip(1).next().unwrap(), 133); // revoked offered HTLC
4139+
assert_eq!(*witness_lens.iter().skip(2).next().unwrap(), 138); // revoked received HTLC
4140+
4141+
assert_eq!(node_txn[3].input.len(), 1);
4142+
check_spends!(node_txn[3], chan_1.3.clone());
4143+
4144+
assert_eq!(node_txn[4].input.len(), 1);
4145+
let witness_script = node_txn[4].input[0].witness.last().unwrap();
4146+
assert_eq!(witness_script.len(), 133); //Spending an offered htlc output
4147+
assert_eq!(node_txn[4].input[0].previous_output.txid, node_txn[3].txid());
4148+
assert_ne!(node_txn[4].input[0].previous_output.txid, node_txn[0].input[0].previous_output.txid);
4149+
assert_ne!(node_txn[4].input[0].previous_output.txid, node_txn[1].input[0].previous_output.txid);
4150+
}
4151+
get_announce_close_broadcast_events(&nodes, 0, 1);
4152+
assert_eq!(nodes[0].node.list_channels().len(), 0);
4153+
assert_eq!(nodes[1].node.list_channels().len(), 0);
4154+
}
4155+
39844156
#[test]
39854157
fn test_htlc_ignore_latest_remote_commitment() {
39864158
// Test that HTLC transactions spending the latest remote commitment transaction are simply
@@ -4115,9 +4287,8 @@ mod tests {
41154287
assert_eq!(node_txn[0].input[0].previous_output.txid, tx.txid());
41164288
assert_eq!(node_txn[0].lock_time, 0); // Must be an HTLC-Success
41174289
assert_eq!(node_txn[0].input[0].witness.len(), 5); // Must be an HTLC-Success
4118-
let mut funding_tx_map = HashMap::new();
4119-
funding_tx_map.insert(tx.txid(), tx);
4120-
node_txn[0].verify(&funding_tx_map).unwrap();
4290+
4291+
check_spends!(node_txn[0], tx);
41214292
}
41224293

41234294
#[test]

src/ln/channelmonitor.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -759,7 +759,7 @@ impl ChannelMonitor {
759759
ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &per_commitment_point, &htlc_base_key)))
760760
},
761761
};
762-
let delayed_key = ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key), &self.delayed_payment_base_key));
762+
let delayed_key = ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key), &self.their_delayed_payment_base_key.unwrap()));
763763
let a_htlc_key = match self.their_htlc_base_key {
764764
None => return (txn_to_broadcast, (commitment_txid, watch_outputs)),
765765
Some(their_htlc_base_key) => ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key), &their_htlc_base_key)),
@@ -856,7 +856,7 @@ impl ChannelMonitor {
856856
};
857857
let sighash_parts = bip143::SighashComponents::new(&single_htlc_tx);
858858
sign_input!(sighash_parts, single_htlc_tx.input[0], Some(idx), htlc.amount_msat / 1000);
859-
txn_to_broadcast.push(single_htlc_tx); // TODO: This is not yet tested in ChannelManager!
859+
txn_to_broadcast.push(single_htlc_tx);
860860
}
861861
}
862862
}

0 commit comments

Comments
 (0)