Merge pull request #280 from TheBlueMatt/2018-12-no-to-remote-revoked-htlcs

Move fail-backwards up for no to-remote output claims
This commit is contained in:
Matt Corallo 2019-01-05 17:07:40 -05:00 committed by GitHub
commit a5bcd5651d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 51 additions and 34 deletions

View file

@ -1179,11 +1179,34 @@ impl ChannelMonitor {
}
}
if !inputs.is_empty() || !txn_to_broadcast.is_empty() { // ie we're confident this is actually ours
if !inputs.is_empty() || !txn_to_broadcast.is_empty() || per_commitment_option.is_some() { // ie we're confident this is actually ours
// We're definitely a remote commitment transaction!
log_trace!(self, "Got broadcast of revoked remote commitment transaction, generating general spend tx with {} inputs and {} other txn to broadcast", inputs.len(), txn_to_broadcast.len());
watch_outputs.append(&mut tx.output.clone());
self.remote_commitment_txn_on_chain.insert(commitment_txid, (commitment_number, tx.output.iter().map(|output| { output.script_pubkey.clone() }).collect()));
// TODO: We really should only fail backwards after our revocation claims have been
// confirmed, but we also need to do more other tracking of in-flight pre-confirm
// on-chain claims, so we can do that at the same time.
macro_rules! check_htlc_fails {
($txid: expr, $commitment_tx: expr) => {
if let Some(&(_, ref outpoints)) = self.remote_claimable_outpoints.get(&$txid) {
for &(ref payment_hash, ref source, _) in outpoints.iter() {
log_trace!(self, "Failing HTLC with payment_hash {} from {} remote commitment tx due to broadcast of revoked remote commitment transaction", log_bytes!(payment_hash.0), $commitment_tx);
htlc_updated.push(((*source).clone(), None, payment_hash.clone()));
}
}
}
}
if let Storage::Local { ref current_remote_commitment_txid, ref prev_remote_commitment_txid, .. } = self.key_storage {
if let &Some(ref txid) = current_remote_commitment_txid {
check_htlc_fails!(txid, "current");
}
if let &Some(ref txid) = prev_remote_commitment_txid {
check_htlc_fails!(txid, "remote");
}
}
// No need to check local commitment txn, symmetric HTLCSource must be present as per-htlc data on remote commitment tx
}
if inputs.is_empty() { return (txn_to_broadcast, (commitment_txid, watch_outputs), spendable_outputs, htlc_updated); } // Nothing to be done...probably a false positive/local tx
@ -1211,29 +1234,6 @@ impl ChannelMonitor {
output: spend_tx.output[0].clone(),
});
txn_to_broadcast.push(spend_tx);
// TODO: We really should only fail backwards after our revocation claims have been
// confirmed, but we also need to do more other tracking of in-flight pre-confirm
// on-chain claims, so we can do that at the same time.
if let Storage::Local { ref current_remote_commitment_txid, ref prev_remote_commitment_txid, .. } = self.key_storage {
if let &Some(ref txid) = current_remote_commitment_txid {
if let Some(&(_, ref latest_outpoints)) = self.remote_claimable_outpoints.get(&txid) {
for &(ref payment_hash, ref source, _) in latest_outpoints.iter() {
log_trace!(self, "Failing HTLC with payment_hash {} from current remote commitment tx due to broadcast of revoked remote commitment transaction", log_bytes!(payment_hash.0));
htlc_updated.push(((*source).clone(), None, payment_hash.clone()));
}
}
}
if let &Some(ref txid) = prev_remote_commitment_txid {
if let Some(&(_, ref prev_outpoint)) = self.remote_claimable_outpoints.get(&txid) {
for &(ref payment_hash, ref source, _) in prev_outpoint.iter() {
log_trace!(self, "Failing HTLC with payment_hash {} from previous remote commitment tx due to broadcast of revoked remote commitment transaction", log_bytes!(payment_hash.0));
htlc_updated.push(((*source).clone(), None, payment_hash.clone()));
}
}
}
}
// No need to check local commitment txn, symmetric HTLCSource must be present as per-htlc data on remote commitment tx
} else if let Some(per_commitment_data) = per_commitment_option {
// While this isn't useful yet, there is a potential race where if a counterparty
// revokes a state at the same time as the commitment transaction for that state is

View file

@ -2951,7 +2951,7 @@ fn test_simple_commitment_revoked_fail_backward() {
}
}
fn do_test_commitment_revoked_fail_backward_exhaustive(deliver_bs_raa: bool) {
fn do_test_commitment_revoked_fail_backward_exhaustive(deliver_bs_raa: bool, use_dust: bool, no_to_remote: bool) {
// Test that if our counterparty broadcasts a revoked commitment transaction we fail all
// pending HTLCs on that channel backwards even if the HTLCs aren't present in our latest
// commitment transaction anymore.
@ -2973,15 +2973,22 @@ fn do_test_commitment_revoked_fail_backward_exhaustive(deliver_bs_raa: bool) {
create_announced_chan_between_nodes(&nodes, 0, 1);
let chan_2 = create_announced_chan_between_nodes(&nodes, 1, 2);
let (payment_preimage, _payment_hash) = route_payment(&nodes[0], &[&nodes[1], &nodes[2]], 3000000);
let (payment_preimage, _payment_hash) = route_payment(&nodes[0], &[&nodes[1], &nodes[2]], if no_to_remote { 10_000 } else { 3_000_000 });
// Get the will-be-revoked local txn from nodes[2]
let revoked_local_txn = nodes[2].node.channel_state.lock().unwrap().by_id.get(&chan_2.2).unwrap().last_local_commitment_txn.clone();
assert_eq!(revoked_local_txn[0].output.len(), if no_to_remote { 1 } else { 2 });
// Revoke the old state
claim_payment(&nodes[0], &[&nodes[1], &nodes[2]], payment_preimage);
let (_, first_payment_hash) = route_payment(&nodes[0], &[&nodes[1], &nodes[2]], 3000000);
let (_, second_payment_hash) = route_payment(&nodes[0], &[&nodes[1], &nodes[2]], 3000000);
let (_, third_payment_hash) = route_payment(&nodes[0], &[&nodes[1], &nodes[2]], 3000000);
let value = if use_dust {
// The dust limit applied to HTLC outputs considers the fee of the HTLC transaction as
// well, so HTLCs at exactly the dust limit will not be included in commitment txn.
nodes[2].node.channel_state.lock().unwrap().by_id.get(&chan_2.2).unwrap().our_dust_limit_satoshis * 1000
} else { 3000000 };
let (_, first_payment_hash) = route_payment(&nodes[0], &[&nodes[1], &nodes[2]], value);
let (_, second_payment_hash) = route_payment(&nodes[0], &[&nodes[1], &nodes[2]], value);
let (_, third_payment_hash) = route_payment(&nodes[0], &[&nodes[1], &nodes[2]], value);
assert!(nodes[2].node.fail_htlc_backwards(&first_payment_hash, 0));
expect_pending_htlcs_forwardable!(nodes[2]);
@ -3043,8 +3050,8 @@ fn do_test_commitment_revoked_fail_backward_exhaustive(deliver_bs_raa: bool) {
if deliver_bs_raa {
nodes[1].node.handle_revoke_and_ack(&nodes[2].node.get_our_node_id(), &bs_raa).unwrap();
// One monitor for the new revocation preimage, one as we generate a commitment for
// nodes[0] to fail first_payment_hash backwards.
// One monitor for the new revocation preimage, no second on as we won't generate a new
// commitment transaction for nodes[0] until process_pending_htlc_forwards().
check_added_monitors!(nodes[1], 1);
let events = nodes[1].node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
@ -3152,9 +3159,19 @@ fn do_test_commitment_revoked_fail_backward_exhaustive(deliver_bs_raa: bool) {
}
#[test]
fn test_commitment_revoked_fail_backward_exhaustive() {
do_test_commitment_revoked_fail_backward_exhaustive(false);
do_test_commitment_revoked_fail_backward_exhaustive(true);
fn test_commitment_revoked_fail_backward_exhaustive_a() {
do_test_commitment_revoked_fail_backward_exhaustive(false, true, false);
do_test_commitment_revoked_fail_backward_exhaustive(true, true, false);
do_test_commitment_revoked_fail_backward_exhaustive(false, false, false);
do_test_commitment_revoked_fail_backward_exhaustive(true, false, false);
}
#[test]
fn test_commitment_revoked_fail_backward_exhaustive_b() {
do_test_commitment_revoked_fail_backward_exhaustive(false, true, true);
do_test_commitment_revoked_fail_backward_exhaustive(true, true, true);
do_test_commitment_revoked_fail_backward_exhaustive(false, false, true);
do_test_commitment_revoked_fail_backward_exhaustive(true, false, true);
}
#[test]