use bitcoin::blockdata::transaction::Transaction;
use bitcoin::blockdata::constants::ChainHash;
use bitcoin::key::constants::SECRET_KEY_SIZE;
-use bitcoin::network::constants::Network;
+use bitcoin::network::Network;
use bitcoin::hashes::Hash;
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::{secp256k1, Sequence};
use crate::blinded_path::{BlindedPath, NodeIdLookUp};
+use crate::blinded_path::message::ForwardNode;
use crate::blinded_path::payment::{Bolt12OfferContext, Bolt12RefundContext, PaymentConstraints, PaymentContext, ReceiveTlvs};
use crate::chain;
use crate::chain::{Confirm, ChannelMonitorUpdateStatus, Watch, BestBlock};
receiver_node_id: PublicKey,
htlcs: Vec<events::ClaimedHTLC>,
sender_intended_value: Option<u64>,
+ onion_fields: Option<RecipientOnionFields>,
}
impl_writeable_tlv_based!(ClaimingPayment, {
(0, amount_msat, required),
(4, receiver_node_id, required),
(5, htlcs, optional_vec),
(7, sender_intended_value, option),
+ (9, onion_fields, option),
});
struct ClaimablePayment {
///
/// ```
/// use bitcoin::BlockHash;
-/// use bitcoin::network::constants::Network;
+/// use bitcoin::network::Network;
/// use lightning::chain::BestBlock;
/// # use lightning::chain::channelmonitor::ChannelMonitor;
/// use lightning::ln::channelmanager::{ChainParameters, ChannelManager, ChannelManagerReadArgs};
/// #
/// # fn example<T: AChannelManager>(channel_manager: T) {
/// # let channel_manager = channel_manager.get_cm();
+/// # let error_message = "Channel force-closed";
/// channel_manager.process_pending_events(&|event| match event {
/// Event::OpenChannelRequest { temporary_channel_id, counterparty_node_id, .. } => {
/// if !is_trusted(counterparty_node_id) {
/// match channel_manager.force_close_without_broadcasting_txn(
-/// &temporary_channel_id, &counterparty_node_id
+/// &temporary_channel_id, &counterparty_node_id, error_message.to_string()
/// ) {
/// Ok(()) => println!("Rejecting channel {}", temporary_channel_id),
/// Err(e) => println!("Error rejecting channel {}: {:?}", temporary_channel_id, e),
Ok(counterparty_node_id)
}
- fn force_close_sending_error(&self, channel_id: &ChannelId, counterparty_node_id: &PublicKey, broadcast: bool) -> Result<(), APIError> {
+ fn force_close_sending_error(&self, channel_id: &ChannelId, counterparty_node_id: &PublicKey, broadcast: bool, error_message: String)
+ -> Result<(), APIError> {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
+ log_debug!(self.logger,
+ "Force-closing channel, The error message sent to the peer : {}", error_message);
match self.force_close_channel_with_peer(channel_id, counterparty_node_id, None, broadcast) {
Ok(counterparty_node_id) => {
let per_peer_state = self.per_peer_state.read().unwrap();
events::MessageSendEvent::HandleError {
node_id: counterparty_node_id,
action: msgs::ErrorAction::DisconnectPeer {
- msg: Some(msgs::ErrorMessage { channel_id: *channel_id, data: "Channel force-closed".to_owned() })
+ msg: Some(msgs::ErrorMessage { channel_id: *channel_id, data: error_message})
},
}
);
}
}
- /// Force closes a channel, immediately broadcasting the latest local transaction(s) and
- /// rejecting new HTLCs on the given channel. Fails if `channel_id` is unknown to
- /// the manager, or if the `counterparty_node_id` isn't the counterparty of the corresponding
- /// channel.
- pub fn force_close_broadcasting_latest_txn(&self, channel_id: &ChannelId, counterparty_node_id: &PublicKey)
+ /// Force closes a channel, immediately broadcasting the latest local transaction(s),
+ /// rejecting new HTLCs.
+ ///
+ /// The provided `error_message` is sent to connected peers for closing
+ /// channels and should be a human-readable description of what went wrong.
+ ///
+ /// Fails if `channel_id` is unknown to the manager, or if the `counterparty_node_id`
+ /// isn't the counterparty of the corresponding channel.
+ pub fn force_close_broadcasting_latest_txn(&self, channel_id: &ChannelId, counterparty_node_id: &PublicKey, error_message: String)
-> Result<(), APIError> {
- self.force_close_sending_error(channel_id, counterparty_node_id, true)
+ self.force_close_sending_error(channel_id, counterparty_node_id, true, error_message)
}
/// Force closes a channel, rejecting new HTLCs on the given channel but skips broadcasting
- /// the latest local transaction(s). Fails if `channel_id` is unknown to the manager, or if the
- /// `counterparty_node_id` isn't the counterparty of the corresponding channel.
+ /// the latest local transaction(s).
///
+ /// The provided `error_message` is sent to connected peers for closing channels and should
+ /// be a human-readable description of what went wrong.
+ ///
+ /// Fails if `channel_id` is unknown to the manager, or if the
+ /// `counterparty_node_id` isn't the counterparty of the corresponding channel.
/// You can always broadcast the latest local transaction(s) via
/// [`ChannelMonitor::broadcast_latest_holder_commitment_txn`].
- pub fn force_close_without_broadcasting_txn(&self, channel_id: &ChannelId, counterparty_node_id: &PublicKey)
+ pub fn force_close_without_broadcasting_txn(&self, channel_id: &ChannelId, counterparty_node_id: &PublicKey, error_message: String)
-> Result<(), APIError> {
- self.force_close_sending_error(channel_id, counterparty_node_id, false)
+ self.force_close_sending_error(channel_id, counterparty_node_id, false, error_message)
}
/// Force close all channels, immediately broadcasting the latest local commitment transaction
/// for each to the chain and rejecting new HTLCs on each.
- pub fn force_close_all_channels_broadcasting_latest_txn(&self) {
+ ///
+ /// The provided `error_message` is sent to connected peers for closing channels and should
+ /// be a human-readable description of what went wrong.
+ pub fn force_close_all_channels_broadcasting_latest_txn(&self, error_message: String) {
for chan in self.list_channels() {
- let _ = self.force_close_broadcasting_latest_txn(&chan.channel_id, &chan.counterparty.node_id);
+ let _ = self.force_close_broadcasting_latest_txn(&chan.channel_id, &chan.counterparty.node_id, error_message.clone());
}
}
/// Force close all channels rejecting new HTLCs on each but without broadcasting the latest
/// local transaction(s).
- pub fn force_close_all_channels_without_broadcasting_txn(&self) {
+ ///
+ /// The provided `error_message` is sent to connected peers for closing channels and
+ /// should be a human-readable description of what went wrong.
+ pub fn force_close_all_channels_without_broadcasting_txn(&self, error_message: String) {
for chan in self.list_channels() {
- let _ = self.force_close_without_broadcasting_txn(&chan.channel_id, &chan.counterparty.node_id);
+ let _ = self.force_close_without_broadcasting_txn(&chan.channel_id, &chan.counterparty.node_id, error_message.clone());
}
}
pub(crate) fn test_send_payment_along_path(&self, path: &Path, payment_hash: &PaymentHash, recipient_onion: RecipientOnionFields, total_value: u64, cur_height: u32, payment_id: PaymentId, keysend_preimage: &Option<PaymentPreimage>, session_priv_bytes: [u8; 32]) -> Result<(), APIError> {
let _lck = self.total_consistency_lock.read().unwrap();
self.send_payment_along_path(SendAlongPathArgs {
- path, payment_hash, recipient_onion, total_value, cur_height, payment_id, keysend_preimage,
- session_priv_bytes
+ path, payment_hash, recipient_onion: &recipient_onion, total_value,
+ cur_height, payment_id, keysend_preimage, session_priv_bytes
})
}
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
let mut result = Ok(());
- if !funding_transaction.is_coin_base() {
+ if !funding_transaction.is_coinbase() {
for inp in funding_transaction.input.iter() {
if inp.witness.is_empty() {
result = result.and(Err(APIError::APIMisuseError {
is_batch_funding,
|chan, tx| {
let mut output_index = None;
- let expected_spk = chan.context.get_funding_redeemscript().to_v0_p2wsh();
+ let expected_spk = chan.context.get_funding_redeemscript().to_p2wsh();
for (idx, outp) in tx.output.iter().enumerate() {
- if outp.script_pubkey == expected_spk && outp.value == chan.context.get_value_satoshis() {
+ if outp.script_pubkey == expected_spk && outp.value.to_sat() == chan.context.get_value_satoshis() {
if output_index.is_some() {
return Err("Multiple outputs matched the expected script and value");
}
}
}
- let htlcs = payment.htlcs.iter().map(events::ClaimedHTLC::from).collect();
- let sender_intended_value = payment.htlcs.first().map(|htlc| htlc.total_msat);
- let dup_purpose = claimable_payments.pending_claiming_payments.insert(payment_hash,
- ClaimingPayment { amount_msat: payment.htlcs.iter().map(|source| source.value).sum(),
- payment_purpose: payment.purpose, receiver_node_id, htlcs, sender_intended_value
- });
- if dup_purpose.is_some() {
- debug_assert!(false, "Shouldn't get a duplicate pending claim event ever");
- log_error!(self.logger, "Got a duplicate pending claimable event on payment hash {}! Please report this bug",
- &payment_hash);
- }
+ let claiming_payment = claimable_payments.pending_claiming_payments
+ .entry(payment_hash)
+ .and_modify(|_| {
+ debug_assert!(false, "Shouldn't get a duplicate pending claim event ever");
+ log_error!(self.logger, "Got a duplicate pending claimable event on payment hash {}! Please report this bug",
+ &payment_hash);
+ })
+ .or_insert_with(|| {
+ let htlcs = payment.htlcs.iter().map(events::ClaimedHTLC::from).collect();
+ let sender_intended_value = payment.htlcs.first().map(|htlc| htlc.total_msat);
+ ClaimingPayment {
+ amount_msat: payment.htlcs.iter().map(|source| source.value).sum(),
+ payment_purpose: payment.purpose,
+ receiver_node_id,
+ htlcs,
+ sender_intended_value,
+ onion_fields: payment.onion_fields,
+ }
+ });
- if let Some(RecipientOnionFields { ref custom_tlvs, .. }) = payment.onion_fields {
+ if let Some(RecipientOnionFields { ref custom_tlvs, .. }) = claiming_payment.onion_fields {
if !custom_tlvs_known && custom_tlvs.iter().any(|(typ, _)| typ % 2 == 0) {
log_info!(self.logger, "Rejecting payment with payment hash {} as we cannot accept payment with unknown even TLVs: {}",
&payment_hash, log_iter!(custom_tlvs.iter().map(|(typ, _)| typ).filter(|typ| *typ % 2 == 0)));
receiver_node_id,
htlcs,
sender_intended_value: sender_intended_total_msat,
+ onion_fields,
}) = payment {
self.pending_events.lock().unwrap().push_back((events::Event::PaymentClaimed {
payment_hash,
receiver_node_id: Some(receiver_node_id),
htlcs,
sender_intended_total_msat,
+ onion_fields,
}, None));
}
},
match phase.get_mut() {
ChannelPhase::UnfundedOutboundV1(chan) => {
try_chan_phase_entry!(self, chan.accept_channel(&msg, &self.default_configuration.channel_handshake_limits, &peer_state.latest_features), phase);
- (chan.context.get_value_satoshis(), chan.context.get_funding_redeemscript().to_v0_p2wsh(), chan.context.get_user_id())
+ (chan.context.get_value_satoshis(), chan.context.get_funding_redeemscript().to_p2wsh(), chan.context.get_user_id())
},
_ => {
return Err(MsgHandleErrInternal::send_err_msg_no_close(format!("Got an unexpected accept_channel message from peer with counterparty_node_id {}", counterparty_node_id), msg.common_fields.temporary_channel_id));
let peers = self.per_peer_state.read().unwrap()
.iter()
- .filter(|(_, peer)| peer.lock().unwrap().latest_features.supports_onion_messages())
- .map(|(node_id, _)| *node_id)
+ .map(|(node_id, peer_state)| (node_id, peer_state.lock().unwrap()))
+ .filter(|(_, peer)| peer.latest_features.supports_onion_messages())
+ .map(|(node_id, peer)| ForwardNode {
+ node_id: *node_id,
+ short_channel_id: peer.channel_by_id
+ .iter()
+ .filter(|(_, channel)| channel.context().is_usable())
+ .min_by_key(|(_, channel)| channel.context().channel_creation_height)
+ .and_then(|(_, channel)| channel.context().get_short_channel_id()),
+ })
.collect::<Vec<_>>();
self.router
}
&mut chan.context
},
- // We retain UnfundedOutboundV1 channel for some time in case
- // peer unexpectedly disconnects, and intends to reconnect again.
- ChannelPhase::UnfundedOutboundV1(_) => {
- return true;
- },
+ // If we get disconnected and haven't yet committed to a funding
+ // transaction, we can replay the `open_channel` on reconnection, so don't
+ // bother dropping the channel here. However, if we already committed to
+ // the funding transaction we don't yet support replaying the funding
+ // handshake (and bailing if the peer rejects it), so we force-close in
+ // that case.
+ ChannelPhase::UnfundedOutboundV1(chan) if chan.is_resumable() => return true,
+ ChannelPhase::UnfundedOutboundV1(chan) => &mut chan.context,
// Unfunded inbound channels will always be removed.
ChannelPhase::UnfundedInboundV1(chan) => {
&mut chan.context
amount_msat: claimable_amt_msat,
htlcs: payment.htlcs.iter().map(events::ClaimedHTLC::from).collect(),
sender_intended_total_msat: payment.htlcs.first().map(|htlc| htlc.total_msat),
+ onion_fields: payment.onion_fields,
}, None));
}
}
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id());
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id());
-
- nodes[0].node.force_close_broadcasting_latest_txn(&chan.2, &nodes[1].node.get_our_node_id()).unwrap();
+ let error_message = "Channel force-closed";
+ nodes[0].node.force_close_broadcasting_latest_txn(&chan.2, &nodes[1].node.get_our_node_id(), error_message.to_string()).unwrap();
check_closed_broadcast!(nodes[0], true);
check_added_monitors!(nodes[0], 1);
check_closed_event!(nodes[0], 1, ClosureReason::HolderForceClosed, [nodes[1].node.get_our_node_id()], 100000);
let channel_id = ChannelId::from_bytes([4; 32]);
let unkown_public_key = PublicKey::from_secret_key(&Secp256k1::signing_only(), &SecretKey::from_slice(&[42; 32]).unwrap());
let intercept_id = InterceptId([0; 32]);
+ let error_message = "Channel force-closed";
// Test the API functions.
check_not_connected_to_peer_error(nodes[0].node.create_channel(unkown_public_key, 1_000_000, 500_000_000, 42, None, None), unkown_public_key);
check_unkown_peer_error(nodes[0].node.close_channel(&channel_id, &unkown_public_key), unkown_public_key);
- check_unkown_peer_error(nodes[0].node.force_close_broadcasting_latest_txn(&channel_id, &unkown_public_key), unkown_public_key);
+ check_unkown_peer_error(nodes[0].node.force_close_broadcasting_latest_txn(&channel_id, &unkown_public_key, error_message.to_string()), unkown_public_key);
- check_unkown_peer_error(nodes[0].node.force_close_without_broadcasting_txn(&channel_id, &unkown_public_key), unkown_public_key);
+ check_unkown_peer_error(nodes[0].node.force_close_without_broadcasting_txn(&channel_id, &unkown_public_key, error_message.to_string()), unkown_public_key);
check_unkown_peer_error(nodes[0].node.forward_intercepted_htlc(intercept_id, &channel_id, unkown_public_key, 1_000_000), unkown_public_key);
// Dummy values
let channel_id = ChannelId::from_bytes([4; 32]);
+ let error_message = "Channel force-closed";
// Test the API functions.
check_api_misuse_error(nodes[0].node.accept_inbound_channel(&channel_id, &counterparty_node_id, 42));
check_channel_unavailable_error(nodes[0].node.close_channel(&channel_id, &counterparty_node_id), channel_id, counterparty_node_id);
- check_channel_unavailable_error(nodes[0].node.force_close_broadcasting_latest_txn(&channel_id, &counterparty_node_id), channel_id, counterparty_node_id);
+ check_channel_unavailable_error(nodes[0].node.force_close_broadcasting_latest_txn(&channel_id, &counterparty_node_id, error_message.to_string()), channel_id, counterparty_node_id);
- check_channel_unavailable_error(nodes[0].node.force_close_without_broadcasting_txn(&channel_id, &counterparty_node_id), channel_id, counterparty_node_id);
+ check_channel_unavailable_error(nodes[0].node.force_close_without_broadcasting_txn(&channel_id, &counterparty_node_id, error_message.to_string()), channel_id, counterparty_node_id);
check_channel_unavailable_error(nodes[0].node.forward_intercepted_htlc(InterceptId([0; 32]), &channel_id, counterparty_node_id, 1_000_000), channel_id, counterparty_node_id);
anchors_config.manually_accept_inbound_channels = true;
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[Some(anchors_config.clone()), Some(anchors_config.clone())]);
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
+ let error_message = "Channel force-closed";
nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100_000, 0, 0, None, None).unwrap();
let open_channel_msg = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
let events = nodes[1].node.get_and_clear_pending_events();
match events[0] {
Event::OpenChannelRequest { temporary_channel_id, .. } => {
- nodes[1].node.force_close_broadcasting_latest_txn(&temporary_channel_id, &nodes[0].node.get_our_node_id()).unwrap();
+ nodes[1].node.force_close_broadcasting_latest_txn(&temporary_channel_id, &nodes[0].node.get_our_node_id(), error_message.to_string()).unwrap();
}
_ => panic!("Unexpected event"),
}
let user_config = test_default_channel_config();
let node_chanmgr = create_node_chanmgrs(2, &node_cfg, &[Some(user_config), Some(user_config)]);
let nodes = create_network(2, &node_cfg, &node_chanmgr);
+ let error_message = "Channel force-closed";
// Open a channel, immediately disconnect each other, and broadcast Alice's latest state.
let (_, _, chan_id, funding_tx) = create_announced_chan_between_nodes(&nodes, 0, 1);
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id());
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id());
- nodes[0].node.force_close_broadcasting_latest_txn(&chan_id, &nodes[1].node.get_our_node_id()).unwrap();
+ nodes[0].node.force_close_broadcasting_latest_txn(&chan_id, &nodes[1].node.get_our_node_id(), error_message.to_string()).unwrap();
check_closed_broadcast(&nodes[0], 1, true);
check_added_monitors(&nodes[0], 1);
check_closed_event!(nodes[0], 1, ClosureReason::HolderForceClosed, [nodes[1].node.get_our_node_id()], 100000);
use crate::util::test_utils;
use crate::util::config::{UserConfig, MaxDustHTLCExposure};
+ use bitcoin::amount::Amount;
use bitcoin::blockdata::locktime::absolute::LockTime;
use bitcoin::hashes::Hash;
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::{Transaction, TxOut};
+ use bitcoin::transaction::Version;
use crate::sync::{Arc, Mutex, RwLock};
let tx;
if let Event::FundingGenerationReady { temporary_channel_id, output_script, .. } = get_event!(node_a_holder, Event::FundingGenerationReady) {
- tx = Transaction { version: 2, lock_time: LockTime::ZERO, input: Vec::new(), output: vec![TxOut {
- value: 8_000_000, script_pubkey: output_script,
+ tx = Transaction { version: Version::TWO, lock_time: LockTime::ZERO, input: Vec::new(), output: vec![TxOut {
+ value: Amount::from_sat(8_000_000), script_pubkey: output_script,
}]};
node_a.funding_transaction_generated(&temporary_channel_id, &node_b.get_our_node_id(), tx.clone()).unwrap();
} else { panic!(); }