use crate::chain::channelmonitor;
use crate::chain::channelmonitor::{CLOSED_CHANNEL_UPDATE_ID, CLTV_CLAIM_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS, ANTI_REORG_DELAY};
use crate::chain::transaction::OutPoint;
-use crate::sign::{EcdsaChannelSigner, EntropySource, SignerProvider};
+use crate::sign::{ecdsa::EcdsaChannelSigner, EntropySource, SignerProvider};
use crate::events::{Event, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentPurpose, ClosureReason, HTLCDestination, PaymentFailureReason};
use crate::ln::{ChannelId, PaymentPreimage, PaymentSecret, PaymentHash};
use crate::ln::channel::{commitment_tx_base_weight, COMMITMENT_TX_WEIGHT_PER_HTLC, CONCURRENT_INBOUND_HTLC_FEE_BUFFER, FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE, MIN_AFFORDABLE_HTLC_COUNT, get_holder_selected_channel_reserve_satoshis, OutboundV1Channel, InboundV1Channel, COINBASE_MATURITY, ChannelPhase};
*feerate_lock += 4;
}
nodes[0].node.timer_tick_occurred();
- nodes[0].logger.assert_log("lightning::ln::channel".to_string(), format!("Cannot afford to send new feerate at {}", feerate + 4), 1);
+ nodes[0].logger.assert_log("lightning::ln::channel", format!("Cannot afford to send new feerate at {}", feerate + 4), 1);
check_added_monitors!(nodes[0], 0);
const INITIAL_COMMITMENT_NUMBER: u64 = 281474976710654;
&mut htlcs,
&local_chan.context.channel_transaction_parameters.as_counterparty_broadcastable()
);
- local_chan_signer.as_ecdsa().unwrap().sign_counterparty_commitment(&commitment_tx, Vec::new(), &secp_ctx).unwrap()
+ local_chan_signer.as_ecdsa().unwrap().sign_counterparty_commitment(&commitment_tx, Vec::new(), Vec::new(), &secp_ctx).unwrap()
};
let commit_signed_msg = msgs::CommitmentSigned {
//check to see if the funder, who sent the update_fee request, can afford the new fee (funder_balance >= fee+channel_reserve)
//Should produce and error.
nodes[1].node.handle_commitment_signed(&nodes[0].node.get_our_node_id(), &commit_signed_msg);
- nodes[1].logger.assert_log("lightning::ln::channelmanager".to_string(), "Funding remote cannot afford proposed new fee".to_string(), 1);
+ nodes[1].logger.assert_log_contains("lightning::ln::channelmanager", "Funding remote cannot afford proposed new fee", 3);
check_added_monitors!(nodes[1], 1);
check_closed_broadcast!(nodes[1], true);
check_closed_event!(nodes[1], 1, ClosureReason::ProcessingError { err: String::from("Funding remote cannot afford proposed new fee") },
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet,
skimmed_fee_msat: None,
+ blinding_point: None,
};
nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &msg);
&mut vec![(accepted_htlc_info, ())],
&local_chan.context.channel_transaction_parameters.as_counterparty_broadcastable()
);
- local_chan_signer.as_ecdsa().unwrap().sign_counterparty_commitment(&commitment_tx, Vec::new(), &secp_ctx).unwrap()
+ local_chan_signer.as_ecdsa().unwrap().sign_counterparty_commitment(&commitment_tx, Vec::new(), Vec::new(), &secp_ctx).unwrap()
};
let commit_signed_msg = msgs::CommitmentSigned {
},
_ => panic!("Unexpected event"),
};
- nodes[1].logger.assert_log("lightning::ln::channel".to_string(),
+ nodes[1].logger.assert_log("lightning::ln::channel",
format!("Attempting to fail HTLC due to fee spike buffer violation in channel {}. Rebalancing is required.", raa_msg.channel_id), 1);
check_added_monitors!(nodes[1], 2);
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet,
skimmed_fee_msat: None,
+ blinding_point: None,
};
nodes[0].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &msg);
// Check that the payment failed and the channel is closed in response to the malicious UpdateAdd.
- nodes[0].logger.assert_log("lightning::ln::channelmanager".to_string(), "Cannot accept HTLC that would put our balance under counterparty-announced channel reserve value".to_string(), 1);
+ nodes[0].logger.assert_log_contains("lightning::ln::channelmanager", "Cannot accept HTLC that would put our balance under counterparty-announced channel reserve value", 3);
assert_eq!(nodes[0].node.list_channels().len(), 0);
let err_msg = check_closed_broadcast!(nodes[0], true).unwrap();
assert_eq!(err_msg.data, "Cannot accept HTLC that would put our balance under counterparty-announced channel reserve value");
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet,
skimmed_fee_msat: None,
+ blinding_point: None,
};
nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &msg);
// Check that the payment failed and the channel is closed in response to the malicious UpdateAdd.
- nodes[1].logger.assert_log("lightning::ln::channelmanager".to_string(), "Remote HTLC add would put them under remote reserve value".to_string(), 1);
+ nodes[1].logger.assert_log_contains("lightning::ln::channelmanager", "Remote HTLC add would put them under remote reserve value", 3);
assert_eq!(nodes[1].node.list_channels().len(), 1);
let err_msg = check_closed_broadcast!(nodes[1], true).unwrap();
assert_eq!(err_msg.data, "Remote HTLC add would put them under remote reserve value");
nodes[1].node.force_close_broadcasting_latest_txn(&chan_1.2, &nodes[0].node.get_our_node_id()).unwrap();
check_added_monitors!(nodes[1], 1);
check_closed_broadcast!(nodes[1], true);
+ check_closed_event!(nodes[1], 1, ClosureReason::HolderForceClosed, [nodes[0].node.get_our_node_id()], 100000);
{
let mut node_txn = test_txn_broadcast(&nodes[1], &chan_1, None, HTLCType::NONE);
assert_eq!(node_txn.len(), 1);
+ mine_transaction(&nodes[1], &node_txn[0]);
+ if nodes[1].connect_style.borrow().updates_best_block_first() {
+ let _ = nodes[1].tx_broadcaster.txn_broadcast();
+ }
+
mine_transaction(&nodes[0], &node_txn[0]);
check_added_monitors!(nodes[0], 1);
test_txn_broadcast(&nodes[0], &chan_1, Some(node_txn[0].clone()), HTLCType::NONE);
assert_eq!(nodes[0].node.list_channels().len(), 0);
assert_eq!(nodes[1].node.list_channels().len(), 1);
check_closed_event!(nodes[0], 1, ClosureReason::CommitmentTxConfirmed, [nodes[1].node.get_our_node_id()], 100000);
- check_closed_event!(nodes[1], 1, ClosureReason::HolderForceClosed, [nodes[0].node.get_our_node_id()], 100000);
// One pending HTLC is discarded by the force-close:
let (payment_preimage_1, payment_hash_1, ..) = route_payment(&nodes[1], &[&nodes[2], &nodes[3]], 3_000_000);
// that a revoked commitment transaction is broadcasted
// (Similar to `revoked_output_claim` test but we get the justice tx + broadcast manually)
let chanmon_cfgs = create_chanmon_cfgs(2);
- let destination_script0 = chanmon_cfgs[0].keys_manager.get_destination_script().unwrap();
- let destination_script1 = chanmon_cfgs[1].keys_manager.get_destination_script().unwrap();
+ let destination_script0 = chanmon_cfgs[0].keys_manager.get_destination_script([0; 32]).unwrap();
+ let destination_script1 = chanmon_cfgs[1].keys_manager.get_destination_script([0; 32]).unwrap();
let persisters = vec![WatchtowerPersister::new(destination_script0),
WatchtowerPersister::new(destination_script1)];
let node_cfgs = create_node_cfgs_with_persisters(2, &chanmon_cfgs, persisters.iter().collect());
}
let chan_id = Some(chan_1.2);
match forwarded_events[1] {
- Event::PaymentForwarded { fee_earned_msat, prev_channel_id, claim_from_onchain_tx, next_channel_id, outbound_amount_forwarded_msat } => {
- assert_eq!(fee_earned_msat, Some(1000));
+ Event::PaymentForwarded { total_fee_earned_msat, prev_channel_id, claim_from_onchain_tx,
+ next_channel_id, outbound_amount_forwarded_msat
+ } => {
+ assert_eq!(total_fee_earned_msat, Some(1000));
assert_eq!(prev_channel_id, chan_id);
assert_eq!(claim_from_onchain_tx, true);
assert_eq!(next_channel_id, Some(chan_2.2));
_ => panic!()
}
match forwarded_events[2] {
- Event::PaymentForwarded { fee_earned_msat, prev_channel_id, claim_from_onchain_tx, next_channel_id, outbound_amount_forwarded_msat } => {
- assert_eq!(fee_earned_msat, Some(1000));
+ Event::PaymentForwarded { total_fee_earned_msat, prev_channel_id, claim_from_onchain_tx,
+ next_channel_id, outbound_amount_forwarded_msat
+ } => {
+ assert_eq!(total_fee_earned_msat, Some(1000));
assert_eq!(prev_channel_id, chan_id);
assert_eq!(claim_from_onchain_tx, true);
assert_eq!(next_channel_id, Some(chan_2.2));
let events = nodes[1].node.get_and_clear_pending_events();
assert_eq!(events.len(), if deliver_bs_raa { 3 + nodes.len() - 1 } else { 4 + nodes.len() });
- match events[0] {
- Event::ChannelClosed { reason: ClosureReason::CommitmentTxConfirmed, .. } => { },
- _ => panic!("Unexepected event"),
- }
- match events[1] {
- Event::PaymentPathFailed { ref payment_hash, .. } => {
- assert_eq!(*payment_hash, fourth_payment_hash);
- },
- _ => panic!("Unexpected event"),
- }
- match events[2] {
- Event::PaymentFailed { ref payment_hash, .. } => {
- assert_eq!(*payment_hash, fourth_payment_hash);
- },
- _ => panic!("Unexpected event"),
- }
+ assert!(events.iter().any(|ev| matches!(
+ ev,
+ Event::ChannelClosed { reason: ClosureReason::CommitmentTxConfirmed, .. }
+ )));
+ assert!(events.iter().any(|ev| matches!(
+ ev,
+ Event::PaymentPathFailed { ref payment_hash, .. } if *payment_hash == fourth_payment_hash
+ )));
+ assert!(events.iter().any(|ev| matches!(
+ ev,
+ Event::PaymentFailed { ref payment_hash, .. } if *payment_hash == fourth_payment_hash
+ )));
nodes[1].node.process_pending_htlc_forwards();
check_added_monitors!(nodes[1], 1);
cltv_expiry,
onion_routing_packet,
skimmed_fee_msat: None,
+ blinding_point: None,
};
nodes[0].node.handle_update_add_htlc(&nodes[1].node.get_our_node_id(), &update_add_htlc);
}
// connect_style.
return;
}
- create_announced_chan_between_nodes(&nodes, 0, 1);
+ let funding_tx = create_announced_chan_between_nodes(&nodes, 0, 1).3;
route_payment(&nodes[0], &[&nodes[1]], 10000000);
nodes[0].node.force_close_broadcasting_latest_txn(&nodes[0].node.list_channels()[0].channel_id, &nodes[1].node.get_our_node_id()).unwrap();
check_added_monitors!(nodes[0], 1);
check_closed_event!(nodes[0], 1, ClosureReason::HolderForceClosed, [nodes[1].node.get_our_node_id()], 100000);
- let node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
- assert_eq!(node_txn.len(), 3);
- assert_eq!(node_txn[0].txid(), node_txn[1].txid());
+ let node_txn = nodes[0].tx_broadcaster.unique_txn_broadcast();
+ assert_eq!(node_txn.len(), 2);
+ check_spends!(node_txn[0], funding_tx);
+ check_spends!(node_txn[1], node_txn[0]);
- let block = create_dummy_block(nodes[1].best_block_hash(), 42, vec![node_txn[0].clone(), node_txn[1].clone()]);
+ let block = create_dummy_block(nodes[1].best_block_hash(), 42, vec![node_txn[0].clone()]);
connect_block(&nodes[1], &block);
check_closed_broadcast!(nodes[1], true);
check_added_monitors!(nodes[1], 1);
check_closed_broadcast!(nodes[2], true);
check_added_monitors!(nodes[2], 1);
check_closed_event!(nodes[2], 1, ClosureReason::HolderForceClosed, [nodes[1].node.get_our_node_id()], 100000);
- let tx = {
+ let commitment_tx = {
let mut node_txn = nodes[2].tx_broadcaster.txn_broadcasted.lock().unwrap();
// Note that we don't bother broadcasting the HTLC-Success transaction here as we don't
// have a use for it unless nodes[2] learns the preimage somehow, the funds will go
node_txn.remove(0)
};
- mine_transaction(&nodes[1], &tx);
+ mine_transaction(&nodes[1], &commitment_tx);
// Note no UpdateHTLCs event here from nodes[1] to nodes[0]!
check_closed_broadcast!(nodes[1], true);
get_monitor!(nodes[2], payment_event.commitment_msg.channel_id)
.provide_payment_preimage(&our_payment_hash, &our_payment_preimage, &node_cfgs[2].tx_broadcaster, &LowerBoundedFeeEstimator::new(node_cfgs[2].fee_estimator), &node_cfgs[2].logger);
}
- mine_transaction(&nodes[2], &tx);
- let node_txn = nodes[2].tx_broadcaster.txn_broadcasted.lock().unwrap();
- assert_eq!(node_txn.len(), 1);
- assert_eq!(node_txn[0].input.len(), 1);
- assert_eq!(node_txn[0].input[0].previous_output.txid, tx.txid());
- assert_eq!(node_txn[0].lock_time, LockTime::ZERO); // Must be an HTLC-Success
- assert_eq!(node_txn[0].input[0].witness.len(), 5); // Must be an HTLC-Success
+ mine_transaction(&nodes[2], &commitment_tx);
+ let mut node_txn = nodes[2].tx_broadcaster.txn_broadcast();
+ assert_eq!(node_txn.len(), if nodes[2].connect_style.borrow().updates_best_block_first() { 2 } else { 1 });
+ let htlc_tx = node_txn.pop().unwrap();
+ assert_eq!(htlc_tx.input.len(), 1);
+ assert_eq!(htlc_tx.input[0].previous_output.txid, commitment_tx.txid());
+ assert_eq!(htlc_tx.lock_time, LockTime::ZERO); // Must be an HTLC-Success
+ assert_eq!(htlc_tx.input[0].witness.len(), 5); // Must be an HTLC-Success
- check_spends!(node_txn[0], tx);
+ check_spends!(htlc_tx, commitment_tx);
}
#[test]
_ => panic!("Unexpected event"),
}
match events[1] {
- Event::PaymentForwarded { fee_earned_msat, prev_channel_id, claim_from_onchain_tx, next_channel_id, outbound_amount_forwarded_msat } => {
- assert_eq!(fee_earned_msat, Some(1000));
+ Event::PaymentForwarded { total_fee_earned_msat, prev_channel_id, claim_from_onchain_tx,
+ next_channel_id, outbound_amount_forwarded_msat
+ } => {
+ assert_eq!(total_fee_earned_msat, Some(1000));
assert_eq!(prev_channel_id, Some(chan_1.2));
assert_eq!(claim_from_onchain_tx, true);
assert_eq!(next_channel_id, Some(chan_2.2));
let chain_monitor = test_utils::TestChainMonitor::new(Some(&chanmon_cfgs[0].chain_source), &chanmon_cfgs[0].tx_broadcaster, &chanmon_cfgs[0].logger, &chanmon_cfgs[0].fee_estimator, &chanmon_cfgs[0].persister, &keys_manager);
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &chanmon_cfgs[0].logger));
let scorer = RwLock::new(test_utils::TestScorer::new());
- let router = test_utils::TestRouter::new(network_graph.clone(), &scorer);
- let node = NodeCfg { chain_source: &chanmon_cfgs[0].chain_source, logger: &chanmon_cfgs[0].logger, tx_broadcaster: &chanmon_cfgs[0].tx_broadcaster, fee_estimator: &chanmon_cfgs[0].fee_estimator, router, chain_monitor, keys_manager: &keys_manager, network_graph, node_seed: seed, override_init_features: alloc::rc::Rc::new(core::cell::RefCell::new(None)) };
+ let router = test_utils::TestRouter::new(network_graph.clone(), &chanmon_cfgs[0].logger, &scorer);
+ let message_router = test_utils::TestMessageRouter::new(network_graph.clone());
+ let node = NodeCfg { chain_source: &chanmon_cfgs[0].chain_source, logger: &chanmon_cfgs[0].logger, tx_broadcaster: &chanmon_cfgs[0].tx_broadcaster, fee_estimator: &chanmon_cfgs[0].fee_estimator, router, message_router, chain_monitor, keys_manager: &keys_manager, network_graph, node_seed: seed, override_init_features: alloc::rc::Rc::new(core::cell::RefCell::new(None)) };
let mut node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
node_cfgs.remove(0);
node_cfgs.insert(0, node);
// us to surface its failure to the user.
chan_stat = get_channel_value_stat!(nodes[0], nodes[1], chan.2);
assert_eq!(chan_stat.holding_cell_outbound_amount_msat, 0);
- nodes[0].logger.assert_log("lightning::ln::channel".to_string(), format!("Freeing holding cell with 1 HTLC updates in channel {}", chan.2), 1);
+ nodes[0].logger.assert_log("lightning::ln::channel", format!("Freeing holding cell with 1 HTLC updates in channel {}", chan.2), 1);
// Check that the payment failed to be sent out.
let events = nodes[0].node.get_and_clear_pending_events();
// to surface its failure to the user. The first payment should succeed.
chan_stat = get_channel_value_stat!(nodes[0], nodes[1], chan.2);
assert_eq!(chan_stat.holding_cell_outbound_amount_msat, 0);
- nodes[0].logger.assert_log("lightning::ln::channel".to_string(), format!("Freeing holding cell with 2 HTLC updates in channel {}", chan.2), 1);
+ nodes[0].logger.assert_log("lightning::ln::channel", format!("Freeing holding cell with 2 HTLC updates in channel {}", chan.2), 1);
// Check that the second payment failed to be sent out.
let events = nodes[0].node.get_and_clear_pending_events();
updates.update_add_htlcs[0].amount_msat = 0;
nodes[1].node.handle_update_add_htlc(&nodes[0].node.get_our_node_id(), &updates.update_add_htlcs[0]);
- nodes[1].logger.assert_log("lightning::ln::channelmanager".to_string(), "Remote side tried to send a 0-msat HTLC".to_string(), 1);
+ nodes[1].logger.assert_log_contains("lightning::ln::channelmanager", "Remote side tried to send a 0-msat HTLC", 3);
check_closed_broadcast!(nodes[1], true).unwrap();
check_added_monitors!(nodes[1], 1);
check_closed_event!(nodes[1], 1, ClosureReason::ProcessingError { err: "Remote side tried to send a 0-msat HTLC".to_string() },
cltv_expiry: htlc_cltv,
onion_routing_packet: onion_packet.clone(),
skimmed_fee_msat: None,
+ blinding_point: None,
};
for i in 0..50 {
RecipientOnionFields::secret_only(our_payment_secret), height + 1, &None).unwrap();
// Edit amt_to_forward to simulate the sender having set
// the final amount and the routing node taking less fee
- if let msgs::OutboundOnionPayload::Receive { ref mut amt_msat, .. } = onion_payloads[1] {
- *amt_msat = 99_000;
+ if let msgs::OutboundOnionPayload::Receive {
+ ref mut sender_intended_htlc_amt_msat, ..
+ } = onion_payloads[1] {
+ *sender_intended_htlc_amt_msat = 99_000;
} else { panic!() }
let new_onion_packet = onion_utils::construct_onion_packet(onion_payloads, onion_keys, [0; 32], &our_payment_hash).unwrap();
payment_event.msgs[0].onion_routing_packet = new_onion_packet;
watchtower_alice.chain_monitor.block_connected(&block, HTLC_TIMEOUT_BROADCAST);
// Watchtower Alice should have broadcast a commitment/HTLC-timeout
- let alice_state = {
+ {
let mut txn = alice_broadcaster.txn_broadcast();
assert_eq!(txn.len(), 2);
- txn.remove(0)
+ check_spends!(txn[0], chan_1.3);
+ check_spends!(txn[1], txn[0]);
};
// Copy ChainMonitor to simulate watchtower Bob and make it receive a commitment update first.
check_added_monitors(&nodes[0], 1);
{
let htlc_txn = alice_broadcaster.txn_broadcast();
- assert_eq!(htlc_txn.len(), 2);
+ assert_eq!(htlc_txn.len(), 1);
check_spends!(htlc_txn[0], bob_state_y);
- // Alice doesn't clean up the old HTLC claim since it hasn't seen a conflicting spend for
- // it. However, she should, because it now has an invalid parent.
- check_spends!(htlc_txn[1], alice_state);
}
}
check_added_monitors!(nodes[0], 0);
let funding_created_msg = get_event_msg!(nodes[0], MessageSendEvent::SendFundingCreated, nodes[1].node.get_our_node_id());
- let channel_id = crate::chain::transaction::OutPoint { txid: funding_created_msg.funding_txid, index: funding_created_msg.funding_output_index }.to_channel_id();
+ let channel_id = ChannelId::v1_from_funding_outpoint(crate::chain::transaction::OutPoint { txid: funding_created_msg.funding_txid, index: funding_created_msg.funding_output_index });
nodes[0].node.handle_error(&nodes[1].node.get_our_node_id(), &msgs::ErrorMessage { channel_id, data: "Hi".to_owned() });
assert!(nodes[0].chain_monitor.added_monitors.lock().unwrap().is_empty());
check_closed_event!(nodes[0], 2, ClosureReason::CounterpartyForceClosed { peer_msg: UntrustedString("Hi".to_string()) }, true,
assert_eq!(bob_txn.len(), 1);
check_spends!(bob_txn[0], txn_to_broadcast[0]);
} else {
- assert_eq!(bob_txn.len(), 2);
+ if nodes[1].connect_style.borrow().updates_best_block_first() {
+ assert_eq!(bob_txn.len(), 3);
+ assert_eq!(bob_txn[0].txid(), bob_txn[1].txid());
+ } else {
+ assert_eq!(bob_txn.len(), 2);
+ }
check_spends!(bob_txn[0], chan_ab.3);
}
}
// If Alice force-closed, Bob only broadcasts a HTLC-output-claiming transaction. Otherwise,
// Bob force-closed and broadcasts the commitment transaction along with a
// HTLC-output-claiming transaction.
- let bob_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().clone();
+ let mut bob_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().clone();
if broadcast_alice {
assert_eq!(bob_txn.len(), 1);
check_spends!(bob_txn[0], txn_to_broadcast[0]);
assert_eq!(bob_txn[0].input[0].witness.last().unwrap().len(), script_weight);
} else {
- assert_eq!(bob_txn.len(), 2);
- check_spends!(bob_txn[1], txn_to_broadcast[0]);
- assert_eq!(bob_txn[1].input[0].witness.last().unwrap().len(), script_weight);
+ assert_eq!(bob_txn.len(), if nodes[1].connect_style.borrow().updates_best_block_first() { 3 } else { 2 });
+ let htlc_tx = bob_txn.pop().unwrap();
+ check_spends!(htlc_tx, txn_to_broadcast[0]);
+ assert_eq!(htlc_tx.input[0].witness.last().unwrap().len(), script_weight);
}
}
}
}
}
+#[test]
+fn test_peer_funding_sidechannel() {
+ // Test that if a peer somehow learns which txid we'll use for our channel funding before we
+ // receive `funding_transaction_generated` the peer cannot cause us to crash. We'd previously
+ // assumed that LDK would receive `funding_transaction_generated` prior to our peer learning
+ // the txid and panicked if the peer tried to open a redundant channel to us with the same
+ // funding outpoint.
+ //
+ // While this assumption is generally safe, some users may have out-of-band protocols where
+ // they notify their LSP about a funding outpoint first, or this may be violated in the future
+ // with collaborative transaction construction protocols, i.e. dual-funding.
+ let chanmon_cfgs = create_chanmon_cfgs(3);
+ let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
+ let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
+
+ let temp_chan_id_ab = exchange_open_accept_chan(&nodes[0], &nodes[1], 1_000_000, 0);
+ let temp_chan_id_ca = exchange_open_accept_chan(&nodes[2], &nodes[0], 1_000_000, 0);
+
+ let (_, tx, funding_output) =
+ create_funding_transaction(&nodes[0], &nodes[1].node.get_our_node_id(), 1_000_000, 42);
+
+ let cs_funding_events = nodes[2].node.get_and_clear_pending_events();
+ assert_eq!(cs_funding_events.len(), 1);
+ match cs_funding_events[0] {
+ Event::FundingGenerationReady { .. } => {}
+ _ => panic!("Unexpected event {:?}", cs_funding_events),
+ }
+
+ nodes[2].node.funding_transaction_generated_unchecked(&temp_chan_id_ca, &nodes[0].node.get_our_node_id(), tx.clone(), funding_output.index).unwrap();
+ let funding_created_msg = get_event_msg!(nodes[2], MessageSendEvent::SendFundingCreated, nodes[0].node.get_our_node_id());
+ nodes[0].node.handle_funding_created(&nodes[2].node.get_our_node_id(), &funding_created_msg);
+ get_event_msg!(nodes[0], MessageSendEvent::SendFundingSigned, nodes[2].node.get_our_node_id());
+ expect_channel_pending_event(&nodes[0], &nodes[2].node.get_our_node_id());
+ check_added_monitors!(nodes[0], 1);
+
+ let res = nodes[0].node.funding_transaction_generated(&temp_chan_id_ab, &nodes[1].node.get_our_node_id(), tx.clone());
+ let err_msg = format!("{:?}", res.unwrap_err());
+ assert!(err_msg.contains("An existing channel using outpoint "));
+ assert!(err_msg.contains(" is open with peer"));
+ // Even though the last funding_transaction_generated errored, it still generated a
+ // SendFundingCreated. However, when the peer responds with a funding_signed it will send the
+ // appropriate error message.
+ let as_funding_created = get_event_msg!(nodes[0], MessageSendEvent::SendFundingCreated, nodes[1].node.get_our_node_id());
+ nodes[1].node.handle_funding_created(&nodes[0].node.get_our_node_id(), &as_funding_created);
+ check_added_monitors!(nodes[1], 1);
+ expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id());
+ let reason = ClosureReason::ProcessingError { err: format!("An existing channel using outpoint {} is open with peer {}", funding_output, nodes[2].node.get_our_node_id()), };
+ check_closed_events(&nodes[0], &[ExpectedCloseEvent::from_id_reason(ChannelId::v1_from_funding_outpoint(funding_output), true, reason)]);
+
+ let funding_signed = get_event_msg!(nodes[1], MessageSendEvent::SendFundingSigned, nodes[0].node.get_our_node_id());
+ nodes[0].node.handle_funding_signed(&nodes[1].node.get_our_node_id(), &funding_signed);
+ get_err_msg(&nodes[0], &nodes[1].node.get_our_node_id());
+}
+
+#[test]
+fn test_duplicate_conflicting_funding_from_second_peer() {
+ // Test that if a user tries to fund a channel with a funding outpoint they'd previously used
+ // we don't try to remove the previous ChannelMonitor. This is largely a test to ensure we
+ // don't regress in the fuzzer, as such funding getting passed our outpoint-matches checks
+ // implies the user (and our counterparty) has reused cryptographic keys across channels, which
+ // we require the user not do.
+ let chanmon_cfgs = create_chanmon_cfgs(4);
+ let node_cfgs = create_node_cfgs(4, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs, &[None, None, None, None]);
+ let nodes = create_network(4, &node_cfgs, &node_chanmgrs);
+
+ let temp_chan_id = exchange_open_accept_chan(&nodes[0], &nodes[1], 1_000_000, 0);
+
+ let (_, tx, funding_output) =
+ create_funding_transaction(&nodes[0], &nodes[1].node.get_our_node_id(), 1_000_000, 42);
+
+ // Now that we have a funding outpoint, create a dummy `ChannelMonitor` and insert it into
+ // nodes[0]'s ChainMonitor so that the initial `ChannelMonitor` write fails.
+ let dummy_chan_id = create_chan_between_nodes(&nodes[2], &nodes[3]).3;
+ let dummy_monitor = get_monitor!(nodes[2], dummy_chan_id).clone();
+ nodes[0].chain_monitor.chain_monitor.watch_channel(funding_output, dummy_monitor).unwrap();
+
+ nodes[0].node.funding_transaction_generated(&temp_chan_id, &nodes[1].node.get_our_node_id(), tx.clone()).unwrap();
+
+ let mut funding_created_msg = get_event_msg!(nodes[0], MessageSendEvent::SendFundingCreated, nodes[1].node.get_our_node_id());
+ nodes[1].node.handle_funding_created(&nodes[0].node.get_our_node_id(), &funding_created_msg);
+ let funding_signed_msg = get_event_msg!(nodes[1], MessageSendEvent::SendFundingSigned, nodes[0].node.get_our_node_id());
+ check_added_monitors!(nodes[1], 1);
+ expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id());
+
+ nodes[0].node.handle_funding_signed(&nodes[1].node.get_our_node_id(), &funding_signed_msg);
+ // At this point, the channel should be closed, after having generated one monitor write (the
+ // watch_channel call which failed), but zero monitor updates.
+ check_added_monitors!(nodes[0], 1);
+ get_err_msg(&nodes[0], &nodes[1].node.get_our_node_id());
+ let err_reason = ClosureReason::ProcessingError { err: "Channel funding outpoint was a duplicate".to_owned() };
+ check_closed_events(&nodes[0], &[ExpectedCloseEvent::from_id_reason(funding_signed_msg.channel_id, true, err_reason)]);
+}
+
+#[test]
+fn test_duplicate_funding_err_in_funding() {
+ // Test that if we have a live channel with one peer, then another peer comes along and tries
+ // to create a second channel with the same txid we'll fail and not overwrite the
+ // outpoint_to_peer map in `ChannelManager`.
+ //
+ // This was previously broken.
+ let chanmon_cfgs = create_chanmon_cfgs(3);
+ let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
+ let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
+ let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
+
+ let (_, _, _, real_channel_id, funding_tx) = create_chan_between_nodes(&nodes[0], &nodes[1]);
+ let real_chan_funding_txo = chain::transaction::OutPoint { txid: funding_tx.txid(), index: 0 };
+ assert_eq!(ChannelId::v1_from_funding_outpoint(real_chan_funding_txo), real_channel_id);
+
+ nodes[2].node.create_channel(nodes[1].node.get_our_node_id(), 100_000, 0, 42, None, None).unwrap();
+ let mut open_chan_msg = get_event_msg!(nodes[2], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
+ let node_c_temp_chan_id = open_chan_msg.temporary_channel_id;
+ open_chan_msg.temporary_channel_id = real_channel_id;
+ nodes[1].node.handle_open_channel(&nodes[2].node.get_our_node_id(), &open_chan_msg);
+ let mut accept_chan_msg = get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, nodes[2].node.get_our_node_id());
+ accept_chan_msg.temporary_channel_id = node_c_temp_chan_id;
+ nodes[2].node.handle_accept_channel(&nodes[1].node.get_our_node_id(), &accept_chan_msg);
+
+ // Now that we have a second channel with the same funding txo, send a bogus funding message
+ // and let nodes[1] remove the inbound channel.
+ let (_, funding_tx, _) = create_funding_transaction(&nodes[2], &nodes[1].node.get_our_node_id(), 100_000, 42);
+
+ nodes[2].node.funding_transaction_generated(&node_c_temp_chan_id, &nodes[1].node.get_our_node_id(), funding_tx).unwrap();
+
+ let mut funding_created_msg = get_event_msg!(nodes[2], MessageSendEvent::SendFundingCreated, nodes[1].node.get_our_node_id());
+ funding_created_msg.temporary_channel_id = real_channel_id;
+ // Make the signature invalid by changing the funding output
+ funding_created_msg.funding_output_index += 10;
+ nodes[1].node.handle_funding_created(&nodes[2].node.get_our_node_id(), &funding_created_msg);
+ get_err_msg(&nodes[1], &nodes[2].node.get_our_node_id());
+ let err = "Invalid funding_created signature from peer".to_owned();
+ let reason = ClosureReason::ProcessingError { err };
+ let expected_closing = ExpectedCloseEvent::from_id_reason(real_channel_id, false, reason);
+ check_closed_events(&nodes[1], &[expected_closing]);
+
+ assert_eq!(
+ *nodes[1].node.outpoint_to_peer.lock().unwrap().get(&real_chan_funding_txo).unwrap(),
+ nodes[0].node.get_our_node_id()
+ );
+}
+
#[test]
fn test_duplicate_chan_id() {
// Test that if a given peer tries to open a channel with the same channel_id as one that is
let funding_signed_msg = get_event_msg!(nodes[1], MessageSendEvent::SendFundingSigned, nodes[0].node.get_our_node_id());
let funding_outpoint = crate::chain::transaction::OutPoint { txid: funding_created_msg.funding_txid, index: funding_created_msg.funding_output_index };
- let channel_id = funding_outpoint.to_channel_id();
+ let channel_id = ChannelId::v1_from_funding_outpoint(funding_outpoint);
// Now we have the first channel past funding_created (ie it has a txid-based channel_id, not a
// temporary one).
nodes[0].node.handle_accept_channel(&nodes[1].node.get_our_node_id(), &get_event_msg!(nodes[1], MessageSendEvent::SendAcceptChannel, nodes[0].node.get_our_node_id()));
create_funding_transaction(&nodes[0], &nodes[1].node.get_our_node_id(), 100000, 42); // Get and check the FundingGenerationReady event
- let (_, funding_created) = {
+ let funding_created = {
let per_peer_state = nodes[0].node.per_peer_state.read().unwrap();
let mut a_peer_state = per_peer_state.get(&nodes[1].node.get_our_node_id()).unwrap().lock().unwrap();
// Once we call `get_funding_created` the channel has a duplicate channel_id as
// try to create another channel. Instead, we drop the channel entirely here (leaving the
// channelmanager in a possibly nonsense state instead).
match a_peer_state.channel_by_id.remove(&open_chan_2_msg.temporary_channel_id).unwrap() {
- ChannelPhase::UnfundedOutboundV1(chan) => {
+ ChannelPhase::UnfundedOutboundV1(mut chan) => {
let logger = test_utils::TestLogger::new();
chan.get_funding_created(tx.clone(), funding_outpoint, false, &&logger).map_err(|_| ()).unwrap()
},
_ => panic!("Unexpected ChannelPhase variant"),
- }
+ }.unwrap()
};
check_added_monitors!(nodes[0], 0);
- nodes[1].node.handle_funding_created(&nodes[0].node.get_our_node_id(), &funding_created.unwrap());
+ nodes[1].node.handle_funding_created(&nodes[0].node.get_our_node_id(), &funding_created);
// At this point we'll look up if the channel_id is present and immediately fail the channel
// without trying to persist the `ChannelMonitor`.
check_added_monitors!(nodes[1], 0);
+ check_closed_events(&nodes[1], &[
+ ExpectedCloseEvent::from_id_reason(funding_created.temporary_channel_id, false, ClosureReason::ProcessingError {
+ err: "Already had channel with the new channel_id".to_owned()
+ })
+ ]);
+
// ...still, nodes[1] will reject the duplicate channel.
{
let events = nodes[1].node.get_and_clear_pending_msg_events();
// We should broadcast an HTLC transaction spending our funding transaction first
let spending_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
assert_eq!(spending_txn.len(), 2);
- assert_eq!(spending_txn[0].txid(), node_txn[0].txid());
- check_spends!(spending_txn[1], node_txn[0]);
+ let htlc_tx = if spending_txn[0].txid() == node_txn[0].txid() {
+ &spending_txn[1]
+ } else {
+ &spending_txn[0]
+ };
+ check_spends!(htlc_tx, node_txn[0]);
// We should also generate a SpendableOutputs event with the to_self output (as its
// timelock is up).
let descriptor_spend_txn = check_spendable_outputs!(nodes[1], node_cfgs[1].keys_manager);
// should immediately fail-backwards the HTLC to the previous hop, without waiting for an
// additional block built on top of the current chain.
nodes[1].chain_monitor.chain_monitor.transactions_confirmed(
- &nodes[1].get_block_header(conf_height + 1), &[(0, &spending_txn[1])], conf_height + 1);
+ &nodes[1].get_block_header(conf_height + 1), &[(0, htlc_tx)], conf_height + 1);
expect_pending_htlcs_forwardable_and_htlc_handling_failed!(nodes[1], vec![HTLCDestination::NextHopChannel { node_id: Some(nodes[2].node.get_our_node_id()), channel_id: channel_id }]);
check_added_monitors!(nodes[1], 1);
// Outbound dust balance: 6399 sats
let dust_inbound_overflow = dust_inbound_htlc_on_holder_tx_msat * (dust_inbound_htlc_on_holder_tx + 1);
let dust_outbound_overflow = dust_outbound_htlc_on_holder_tx_msat * dust_outbound_htlc_on_holder_tx + dust_inbound_htlc_on_holder_tx_msat;
- nodes[0].logger.assert_log("lightning::ln::channel".to_string(), format!("Cannot accept value that would put our exposure to dust HTLCs at {} over the limit {} on holder commitment tx", if dust_outbound_balance { dust_outbound_overflow } else { dust_inbound_overflow }, max_dust_htlc_exposure_msat), 1);
+ nodes[0].logger.assert_log("lightning::ln::channel", format!("Cannot accept value that would put our exposure to dust HTLCs at {} over the limit {} on holder commitment tx", if dust_outbound_balance { dust_outbound_overflow } else { dust_inbound_overflow }, max_dust_htlc_exposure_msat), 1);
} else {
// Outbound dust balance: 5200 sats
- nodes[0].logger.assert_log("lightning::ln::channel".to_string(),
+ nodes[0].logger.assert_log("lightning::ln::channel",
format!("Cannot accept value that would put our exposure to dust HTLCs at {} over the limit {} on counterparty commitment tx",
dust_htlc_on_counterparty_tx_msat * (dust_htlc_on_counterparty_tx - 1) + dust_htlc_on_counterparty_tx_msat + 4,
max_dust_htlc_exposure_msat), 1);
// Complete the persistence of the monitor.
nodes[0].chain_monitor.complete_sole_pending_chan_update(
- &OutPoint { txid: tx.txid(), index: 1 }.to_channel_id()
+ &ChannelId::v1_from_funding_outpoint(OutPoint { txid: tx.txid(), index: 1 })
);
let events = nodes[0].node.get_and_clear_pending_events();
nodes[0].node.peer_disconnected(&nodes[2].node.get_our_node_id());
// The channels in the batch will close immediately.
- let channel_id_1 = OutPoint { txid: tx.txid(), index: 0 }.to_channel_id();
- let channel_id_2 = OutPoint { txid: tx.txid(), index: 1 }.to_channel_id();
+ let funding_txo_1 = OutPoint { txid: tx.txid(), index: 0 };
+ let funding_txo_2 = OutPoint { txid: tx.txid(), index: 1 };
+ let channel_id_1 = ChannelId::v1_from_funding_outpoint(funding_txo_1);
+ let channel_id_2 = ChannelId::v1_from_funding_outpoint(funding_txo_2);
check_closed_events(&nodes[0], &[
ExpectedCloseEvent {
channel_id: Some(channel_id_1),
discard_funding: true,
+ channel_funding_txo: Some(funding_txo_1),
+ user_channel_id: Some(42),
..Default::default()
},
ExpectedCloseEvent {
channel_id: Some(channel_id_2),
discard_funding: true,
+ channel_funding_txo: Some(funding_txo_2),
+ user_channel_id: Some(43),
..Default::default()
},
]);
assert_eq!(nodes[0].tx_broadcaster.txn_broadcast().len(), 0);
// Force-close the channel for which we've completed the initial monitor.
- let channel_id_1 = OutPoint { txid: tx.txid(), index: 0 }.to_channel_id();
- let channel_id_2 = OutPoint { txid: tx.txid(), index: 1 }.to_channel_id();
+ let funding_txo_1 = OutPoint { txid: tx.txid(), index: 0 };
+ let funding_txo_2 = OutPoint { txid: tx.txid(), index: 1 };
+ let channel_id_1 = ChannelId::v1_from_funding_outpoint(funding_txo_1);
+ let channel_id_2 = ChannelId::v1_from_funding_outpoint(funding_txo_2);
nodes[0].node.force_close_broadcasting_latest_txn(&channel_id_1, &nodes[1].node.get_our_node_id()).unwrap();
check_added_monitors(&nodes[0], 2);
{
ExpectedCloseEvent {
channel_id: Some(channel_id_1),
discard_funding: true,
+ channel_funding_txo: Some(funding_txo_1),
+ user_channel_id: Some(42),
..Default::default()
},
ExpectedCloseEvent {
channel_id: Some(channel_id_2),
discard_funding: true,
+ channel_funding_txo: Some(funding_txo_2),
+ user_channel_id: Some(43),
..Default::default()
},
]);
let mut nodes = create_network(2, &node_cfgs, &node_chanmgrs);
let funding_tx = create_chan_between_nodes_with_value_init(&nodes[0], &nodes[1], 1_000_000, 0);
- let chan_id = chain::transaction::OutPoint { txid: funding_tx.txid(), index: 0 }.to_channel_id();
+ let chan_id = ChannelId::v1_from_funding_outpoint(chain::transaction::OutPoint { txid: funding_tx.txid(), index: 0 });
assert_eq!(nodes[0].node.list_channels().len(), 1);
assert_eq!(nodes[1].node.list_channels().len(), 1);