Skip to content

Allow get_per_commitment_point to fail. #2487

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions lightning/src/chain/channelmonitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2752,9 +2752,7 @@ impl<Signer: WriteableEcdsaChannelSigner> ChannelMonitorImpl<Signer> {
},
commitment_txid: htlc.commitment_txid,
per_commitment_number: htlc.per_commitment_number,
per_commitment_point: self.onchain_tx_handler.signer.get_per_commitment_point(
htlc.per_commitment_number, &self.onchain_tx_handler.secp_ctx,
),
per_commitment_point: htlc.per_commitment_point,
htlc: htlc.htlc,
preimage: htlc.preimage,
counterparty_sig: htlc.counterparty_sig,
Expand Down
8 changes: 8 additions & 0 deletions lightning/src/chain/onchaintx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ pub(crate) struct ExternalHTLCClaim {
pub(crate) htlc: HTLCOutputInCommitment,
pub(crate) preimage: Option<PaymentPreimage>,
pub(crate) counterparty_sig: Signature,
pub(crate) per_commitment_point: bitcoin::secp256k1::PublicKey,
}

// Represents the different types of claims for which events are yielded externally to satisfy said
Expand Down Expand Up @@ -1188,9 +1189,16 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
})
.map(|(htlc_idx, htlc)| {
let counterparty_htlc_sig = holder_commitment.counterparty_htlc_sigs[htlc_idx];

// TODO(waterson) fallible: move this somewhere!
let per_commitment_point = self.signer.get_per_commitment_point(
trusted_tx.commitment_number(), &self.secp_ctx,
).unwrap();

ExternalHTLCClaim {
commitment_txid: trusted_tx.txid(),
per_commitment_number: trusted_tx.commitment_number(),
per_commitment_point: per_commitment_point,
htlc: htlc.clone(),
preimage: *preimage,
counterparty_sig: counterparty_htlc_sig,
Expand Down
148 changes: 111 additions & 37 deletions lightning/src/ln/channel.rs

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions lightning/src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5622,6 +5622,12 @@ where
Some(inbound_chan) => {
match inbound_chan.funding_created(msg, best_block, &self.signer_provider, &self.logger) {
Ok(res) => res,
Err((inbound_chan, ChannelError::Ignore(_))) => {
// If we get an `Ignore` error then something transient went wrong. Put the channel
// back into the table and bail.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when does this get retried (would be good to doc here)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am assuming that there will be a to-be-determined mechanism that allows the remote signer's implementation to initiate a restart.

peer_state.inbound_v1_channel_by_id.insert(msg.temporary_channel_id, inbound_chan);
return Ok(());
},
Err((mut inbound_chan, err)) => {
// We've already removed this inbound channel from the map in `PeerState`
// above so at this point we just need to clean up any lingering entries
Expand Down
33 changes: 30 additions & 3 deletions lightning/src/ln/functional_test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ use crate::util::test_utils;
use crate::util::test_utils::{panicking, TestChainMonitor, TestScorer, TestKeysInterface};
use crate::util::errors::APIError;
use crate::util::config::{UserConfig, MaxDustHTLCExposure};
use crate::util::logger::Logger;
use crate::util::ser::{ReadableArgs, Writeable};

use bitcoin::blockdata::block::{Block, BlockHeader};
Expand Down Expand Up @@ -414,6 +415,31 @@ impl<'a, 'b, 'c> Node<'a, 'b, 'c> {
pub fn get_block_header(&self, height: u32) -> BlockHeader {
self.blocks.lock().unwrap()[height as usize].0.header
}
/// Changes the channel signer's availability for the specified peer and channel.
///
/// When `available` is set to `true`, the channel signer will behave normally. When set to
/// `false`, the channel signer will act like an off-line remote signer and will return
/// `SignerError::NotAvailable` for several of the signing methods. Currently, only
/// `get_per_commitment_point` and `release_commitment_secret` are affected by this setting.
#[cfg(test)]
pub fn set_channel_signer_available(&self, peer_id: &PublicKey, chan_id: &ChannelId, available: bool) {
let per_peer_state = self.node.per_peer_state.read().unwrap();
let chan_lock = per_peer_state.get(peer_id).unwrap().lock().unwrap();
let signer = (|| {
if let Some(local_chan) = chan_lock.channel_by_id.get(chan_id) {
return local_chan.get_signer();
}
if let Some(local_chan) = chan_lock.inbound_v1_channel_by_id.get(chan_id) {
return local_chan.context.get_signer();
}
if let Some(local_chan) = chan_lock.outbound_v1_channel_by_id.get(chan_id) {
return local_chan.context.get_signer();
}
panic!("Couldn't find a channel with id {}", chan_id);
})();
log_debug!(self.logger, "Setting channel {} as available={}", chan_id, available);
signer.as_ecdsa().unwrap().set_available(available);
}
}

/// If we need an unsafe pointer to a `Node` (ie to reference it in a thread
Expand Down Expand Up @@ -2031,12 +2057,13 @@ macro_rules! expect_channel_shutdown_state {
}

#[cfg(any(test, ldk_bench, feature = "_test_utils"))]
pub fn expect_channel_pending_event<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, expected_counterparty_node_id: &PublicKey) {
pub fn expect_channel_pending_event<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, expected_counterparty_node_id: &PublicKey) -> ChannelId {
let events = node.node.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
match events[0] {
crate::events::Event::ChannelPending { ref counterparty_node_id, .. } => {
match &events[0] {
crate::events::Event::ChannelPending { channel_id, counterparty_node_id, .. } => {
assert_eq!(*expected_counterparty_node_id, *counterparty_node_id);
*channel_id
},
_ => panic!("Unexpected event"),
}
Expand Down
201 changes: 194 additions & 7 deletions lightning/src/ln/functional_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -714,7 +714,7 @@ fn test_update_fee_that_funder_cannot_afford() {
let chan_signer = remote_chan.get_signer();
let pubkeys = chan_signer.as_ref().pubkeys();
(pubkeys.delayed_payment_basepoint, pubkeys.htlc_basepoint,
chan_signer.as_ref().get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 1, &secp_ctx),
chan_signer.as_ref().get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 1, &secp_ctx).unwrap(),
pubkeys.funding_pubkey)
};

Expand Down Expand Up @@ -1421,8 +1421,8 @@ fn test_fee_spike_violation_fails_htlc() {

let pubkeys = chan_signer.as_ref().pubkeys();
(pubkeys.revocation_basepoint, pubkeys.htlc_basepoint,
chan_signer.as_ref().release_commitment_secret(INITIAL_COMMITMENT_NUMBER),
chan_signer.as_ref().get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 2, &secp_ctx),
chan_signer.as_ref().release_commitment_secret(INITIAL_COMMITMENT_NUMBER).unwrap(),
chan_signer.as_ref().get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 2, &secp_ctx).unwrap(),
chan_signer.as_ref().pubkeys().funding_pubkey)
};
let (remote_delayed_payment_basepoint, remote_htlc_basepoint, remote_point, remote_funding) = {
Expand All @@ -1432,7 +1432,7 @@ fn test_fee_spike_violation_fails_htlc() {
let chan_signer = remote_chan.get_signer();
let pubkeys = chan_signer.as_ref().pubkeys();
(pubkeys.delayed_payment_basepoint, pubkeys.htlc_basepoint,
chan_signer.as_ref().get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 1, &secp_ctx),
chan_signer.as_ref().get_per_commitment_point(INITIAL_COMMITMENT_NUMBER - 1, &secp_ctx).unwrap(),
chan_signer.as_ref().pubkeys().funding_pubkey)
};

Expand Down Expand Up @@ -7660,15 +7660,15 @@ fn test_counterparty_raa_skip_no_crash() {

// Make signer believe we got a counterparty signature, so that it allows the revocation
keys.as_ecdsa().unwrap().get_enforcement_state().last_holder_commitment -= 1;
per_commitment_secret = keys.as_ref().release_commitment_secret(INITIAL_COMMITMENT_NUMBER);
per_commitment_secret = keys.as_ref().release_commitment_secret(INITIAL_COMMITMENT_NUMBER).unwrap();

// Must revoke without gaps
keys.as_ecdsa().unwrap().get_enforcement_state().last_holder_commitment -= 1;
keys.as_ref().release_commitment_secret(INITIAL_COMMITMENT_NUMBER - 1);
keys.as_ref().release_commitment_secret(INITIAL_COMMITMENT_NUMBER - 1).expect("unable to release commitment secret");

keys.as_ecdsa().unwrap().get_enforcement_state().last_holder_commitment -= 1;
next_per_commitment_point = PublicKey::from_secret_key(&Secp256k1::new(),
&SecretKey::from_slice(&keys.as_ref().release_commitment_secret(INITIAL_COMMITMENT_NUMBER - 2)).unwrap());
&SecretKey::from_slice(&keys.as_ref().release_commitment_secret(INITIAL_COMMITMENT_NUMBER - 2).unwrap()).unwrap());
}

nodes[1].node.handle_revoke_and_ack(&nodes[0].node.get_our_node_id(),
Expand Down Expand Up @@ -8979,6 +8979,193 @@ fn test_duplicate_chan_id() {
send_payment(&nodes[0], &[&nodes[1]], 8000000);
}

#[test]
fn test_signer_gpcp_unavailable_for_funding_signed() {
// Test that a transient failure of the Signer's get_per_commitment_point can be tolerated during
// channel open by specifically having it fail for an inbound channel during the handling of the
// funding_signed message.
let chanmon_cfgs = create_chanmon_cfgs(2);
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);

// Create an initial channel
nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100000, 10001, 42, None).unwrap();
let mut open_chan_msg = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), &open_chan_msg);
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()));

// Move the channel through the funding flow...
let (temporary_channel_id, tx, _) = create_funding_transaction(&nodes[0], &nodes[1].node.get_our_node_id(), 100000, 42);

nodes[0].node.funding_transaction_generated(&temporary_channel_id, &nodes[1].node.get_our_node_id(), tx.clone()).unwrap();
check_added_monitors(&nodes[0], 0);

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);
check_added_monitors(&nodes[1], 1);

let chan_id = expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id());

// Tweak the node[0] channel signer to produce an "unavailable" message before we ask it to handle
// the funding_signed message.
nodes[0].set_channel_signer_available(&nodes[1].node.get_our_node_id(), &chan_id, false);
let funding_signed_msg = 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_msg);

let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 0);
check_added_monitors(&nodes[0], 0);

// Now make it available and verify that we can process the message.
nodes[0].set_channel_signer_available(&nodes[1].node.get_our_node_id(), &chan_id, true);
nodes[0].node.handle_funding_signed(&nodes[1].node.get_our_node_id(), &funding_signed_msg);
check_added_monitors(&nodes[0], 1);

expect_channel_pending_event(&nodes[0], &nodes[1].node.get_our_node_id());

assert_eq!(nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 1);
assert_eq!(nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap()[0], tx);

let (channel_ready, _) = create_chan_between_nodes_with_value_confirm(&nodes[0], &nodes[1], &tx);
let (announcement, as_update, bs_update) = create_chan_between_nodes_with_value_b(&nodes[0], &nodes[1], &channel_ready);
update_nodes_with_chan_announce(&nodes, 0, 1, &announcement, &as_update, &bs_update);

send_payment(&nodes[0], &[&nodes[1]], 8000000);
}

#[test]
fn test_signer_gpcp_unavailable_for_funding_created() {
// Test that a transient failure of the Signer's get_per_commitment_point can be tolerated during
// channel open by specifically having it fail for an outbound channel during generation of the
// funding_created message.
let chanmon_cfgs = create_chanmon_cfgs(2);
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);

// Create an initial channel
nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100000, 10001, 42, None).unwrap();
let mut open_chan_msg = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), &open_chan_msg);
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()));

// Move the channel through the funding flow...
let (temporary_channel_id, tx, _) = create_funding_transaction(&nodes[0], &nodes[1].node.get_our_node_id(), 100000, 42);

nodes[0].node.funding_transaction_generated(&temporary_channel_id, &nodes[1].node.get_our_node_id(), tx.clone()).unwrap();
check_added_monitors(&nodes[0], 0);

// Tweak the node[1] channel signer to produce an "unavailable" result before we ask it to handle
// the funding_created message.
nodes[1].set_channel_signer_available(&nodes[0].node.get_our_node_id(), &temporary_channel_id, false);
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);
check_added_monitors(&nodes[1], 0);

let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 0);

// Now make it available and verify that we can process the message.
nodes[1].set_channel_signer_available(&nodes[0].node.get_our_node_id(), &temporary_channel_id, true);
nodes[1].node.handle_funding_created(&nodes[0].node.get_our_node_id(), &funding_created_msg);
check_added_monitors(&nodes[1], 1);

let funding_signed_msg = 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_msg);
check_added_monitors(&nodes[0], 1);

expect_channel_pending_event(&nodes[0], &nodes[1].node.get_our_node_id());
expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id());

assert_eq!(nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 1);
assert_eq!(nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap()[0], tx);

let (channel_ready, _) = create_chan_between_nodes_with_value_confirm(&nodes[0], &nodes[1], &tx);
let (announcement, as_update, bs_update) = create_chan_between_nodes_with_value_b(&nodes[0], &nodes[1], &channel_ready);
update_nodes_with_chan_announce(&nodes, 0, 1, &announcement, &as_update, &bs_update);
send_payment(&nodes[0], &[&nodes[1]], 8000000);
}

#[test]
fn test_dest_signer_gpcp_unavailable_for_commitment_signed() {
// Test that a transient failure of the Signer's get_per_commitment_point can be tolerated by the
// destination node for a payment.
let chanmon_cfgs = create_chanmon_cfgs(2);
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);

// Create an initial channel
nodes[0].node.create_channel(nodes[1].node.get_our_node_id(), 100000, 10001, 42, None).unwrap();
let mut open_chan_msg = get_event_msg!(nodes[0], MessageSendEvent::SendOpenChannel, nodes[1].node.get_our_node_id());
nodes[1].node.handle_open_channel(&nodes[0].node.get_our_node_id(), &open_chan_msg);
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()));

// Move the channel through the funding flow...
let (temporary_channel_id, tx, _) = create_funding_transaction(&nodes[0], &nodes[1].node.get_our_node_id(), 100000, 42);

nodes[0].node.funding_transaction_generated(&temporary_channel_id, &nodes[1].node.get_our_node_id(), tx.clone()).unwrap();
check_added_monitors(&nodes[0], 0);

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);
check_added_monitors(&nodes[1], 1);

let funding_signed_msg = 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_msg);
check_added_monitors(&nodes[0], 1);

let chan_id = expect_channel_pending_event(&nodes[0], &nodes[1].node.get_our_node_id());
expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id());

assert_eq!(nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().len(), 1);
assert_eq!(nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap()[0], tx);

let (channel_ready, _) = create_chan_between_nodes_with_value_confirm(&nodes[0], &nodes[1], &tx);
let (announcement, as_update, bs_update) = create_chan_between_nodes_with_value_b(&nodes[0], &nodes[1], &channel_ready);
update_nodes_with_chan_announce(&nodes, 0, 1, &announcement, &as_update, &bs_update);

// Send a payment.
let src = &nodes[0];
let dst = &nodes[1];
let (route, our_payment_hash, _our_payment_preimage, our_payment_secret) = get_route_and_payment_hash!(src, dst, 8000000);
src.node.send_payment_with_route(&route, our_payment_hash,
RecipientOnionFields::secret_only(our_payment_secret), PaymentId(our_payment_hash.0)).unwrap();
check_added_monitors!(src, 1);

// Pass the payment along the route.
let payment_event = {
let mut events = src.node.get_and_clear_pending_msg_events();
assert_eq!(events.len(), 1);
SendEvent::from_event(events.remove(0))
};
assert_eq!(payment_event.node_id, dst.node.get_our_node_id());
assert_eq!(payment_event.msgs.len(), 1);

dst.node.handle_update_add_htlc(&src.node.get_our_node_id(), &payment_event.msgs[0]);

// Mark dst's signer as unavailable.
dst.set_channel_signer_available(&src.node.get_our_node_id(), &chan_id, false);
dst.node.handle_commitment_signed(&src.node.get_our_node_id(), &payment_event.commitment_msg);
check_added_monitors(src, 0);
check_added_monitors(dst, 0);

{
let src_events = src.node.get_and_clear_pending_msg_events();
assert_eq!(src_events.len(), 0);
let dst_events = dst.node.get_and_clear_pending_msg_events();
assert_eq!(dst_events.len(), 0);
}

// Mark dst's signer as available and re-handle commitment_signed. We expect to see both the RAA
// and the CS.
dst.set_channel_signer_available(&src.node.get_our_node_id(), &chan_id, true);
dst.node.handle_commitment_signed(&src.node.get_our_node_id(), &payment_event.commitment_msg);
get_revoke_commit_msgs(dst, &src.node.get_our_node_id());
check_added_monitors!(dst, 1);
}

#[test]
fn test_error_chans_closed() {
// Test that we properly handle error messages, closing appropriate channels.
Expand Down
Loading