Merged
Show file tree
Hide file tree
Changes from all commits
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
11 changes: 11 additions & 0 deletions fuzz/src/chanmon_consistency.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -526,7 +526,18 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out) {
msg.clone()
} else { panic!("Wrong event type"); }
};
let events = $dest.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
if let events::Event::ChannelPending{ ref counterparty_node_id, .. } = events[0] {
assert_eq!(counterparty_node_id, &$source.get_our_node_id());
} else { panic!("Wrong event type"); }

$source.handle_funding_signed(&$dest.get_our_node_id(), &funding_signed);
let events = $source.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
if let events::Event::ChannelPending{ ref counterparty_node_id, .. } = events[0] {
assert_eq!(counterparty_node_id, &$dest.get_our_node_id());
} else { panic!("Wrong event type"); }

funding_output
} }
Expand Down
5 changes: 4 additions & 1 deletion lightning-background-processor/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -710,7 +710,7 @@ mod tests {
use lightning::chain::keysinterface::{InMemorySigner, KeysManager};
use lightning::chain::transaction::OutPoint;
use lightning::events::{Event, PathFailure, MessageSendEventsProvider, MessageSendEvent};
use lightning::get_event_msg;
use lightning::{get_event_msg, get_event};
use lightning::ln::PaymentHash;
use lightning::ln::channelmanager;
use lightning::ln::channelmanager::{BREAKDOWN_TIMEOUT, ChainParameters, MIN_CLTV_EXPIRY_DELTA, PaymentId};
Expand DownExpand Up@@ -1058,7 +1058,10 @@ mod tests {
($node_a: expr, $node_b: expr, $temporary_channel_id: expr, $tx: expr) => {{
$node_a.node.funding_transaction_generated(&$temporary_channel_id, &$node_b.node.get_our_node_id(), $tx.clone()).unwrap();
$node_b.node.handle_funding_created(&$node_a.node.get_our_node_id(), &get_event_msg!($node_a, MessageSendEvent::SendFundingCreated, $node_b.node.get_our_node_id()));
get_event!($node_b, Event::ChannelPending);

$node_a.node.handle_funding_signed(&$node_b.node.get_our_node_id(), &get_event_msg!($node_b, MessageSendEvent::SendFundingSigned, $node_a.node.get_our_node_id()));
get_event!($node_a, Event::ChannelPending);
}}
}

Expand Down
72 changes: 68 additions & 4 deletions lightning/src/events/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -32,7 +32,9 @@ use crate::util::ser::{BigSize, FixedLengthReader, Writeable, Writer, MaybeReada
use crate::util::string::UntrustedString;
use crate::routing::router::{RouteHop, RouteParameters};

use bitcoin::{PackedLockTime, Transaction};
use bitcoin::{PackedLockTime, Transaction, OutPoint};
#[cfg(anchors)]
use bitcoin::{Txid, TxIn, TxOut, Witness};
use bitcoin::blockdata::script::Script;
use bitcoin::hashes::Hash;
use bitcoin::hashes::sha256::Hash as Sha256;
Expand DownExpand Up@@ -608,12 +610,39 @@ pub enum Event {
/// The caveat described above the `fee_earned_msat` field applies here as well.
outbound_amount_forwarded_msat: Option<u64>,
},
/// Used to indicate that a channel with the given `channel_id` is being opened and pending
/// confirmation on-chain.
///
/// This event is emitted when the funding transaction has been signed and is broadcast to the

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should we have a note that, in rare cases, this event may be missed if a channel is ultimately confirmed quickly prior to the event being processed and then we restart? Its a stupid rare case but its possible I think?

@tnulltnullMar 30, 2023

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I'm confused, which scenario are you describing exactly? Wouldn't rather be an edge case that we emit the event twice when we decide to rebroadcast on restart?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think we can: (a) fund the channel, with an async monitor update, (b) not have that complete, but persist the manager, then (c) complete the monitor update and broadcast the funding tx (d) then restart, replay the chain and see the funding tx confirm, then (e) re-complete the monitor update completed in c, then we wont get the event again but the user will never have used it.

I think with "full" async monitor updates this becomes more realistic.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we have a note that, in rare cases, this event may be missed if a channel is ultimately confirmed quickly prior to the event being processed and then we restart?

Since we plan to leverage it for #2137, seems we should always regenerate it in cases like this? The channel will actually be ready, but as long as it's queued before the ChannelReady event, it should be fine.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yea, that's my biggest concern here.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I think we can: (a) fund the channel, with an async monitor update, (b) not have that complete, but persist the manager, then (c) complete the monitor update and broadcast the funding tx (d) then restart, replay the chain and see the funding tx confirm, then (e) re-complete the monitor update completed in c, then we wont get the event again but the user will never have used it.

Ugh., I see. In that scenario it would persist the event queue when the event hasn't been emitted yet and wouldn't re-emit it after restart. Mh, instead of leaving a note I went ahead and am now tracking the emission of ChannelPending in the same way as ChannelReady and removed it from internal_funding_created entirely.
This means that we're now only emitting it after CMU completion or reestablish, i.e., would re-emit it if the "emitted" state wasn't persisted before.

/// network. For 0conf channels it will be immediately followed by the corresponding
/// [`Event::ChannelReady`] event.
ChannelPending {
/// The `channel_id` of the channel that is pending confirmation.
channel_id: [u8; 32],
/// The `user_channel_id` value passed in to [`ChannelManager::create_channel`] for outbound
/// channels, or to [`ChannelManager::accept_inbound_channel`] for inbound channels if
/// [`UserConfig::manually_accept_inbound_channels`] config flag is set to true. Otherwise
/// `user_channel_id` will be randomized for an inbound channel.
///
/// [`ChannelManager::create_channel`]: crate::ln::channelmanager::ChannelManager::create_channel
/// [`ChannelManager::accept_inbound_channel`]: crate::ln::channelmanager::ChannelManager::accept_inbound_channel
/// [`UserConfig::manually_accept_inbound_channels`]: crate::util::config::UserConfig::manually_accept_inbound_channels
user_channel_id: u128,
/// The `temporary_channel_id` this channel used to be known by during channel establishment.
///
/// Will be `None` for channels created prior to LDK version 0.0.115.
former_temporary_channel_id: Option<[u8; 32]>,
/// The `node_id` of the channel counterparty.
counterparty_node_id: PublicKey,
/// The outpoint of the channel's funding transaction.
funding_txo: OutPoint,
},
/// Used to indicate that a channel with the given `channel_id` is ready to
/// be used. This event is emitted either when the funding transaction has been confirmed
/// on-chain, or, in case of a 0conf channel, when both parties have confirmed the channel
/// establishment.
ChannelReady {
/// The channel_id of the channel that is ready.
/// The `channel_id` of the channel that is ready.
channel_id: [u8; 32],
/// The `user_channel_id` value passed in to [`ChannelManager::create_channel`] for outbound
/// channels, or to [`ChannelManager::accept_inbound_channel`] for inbound channels if
Expand All@@ -624,15 +653,15 @@ pub enum Event {
/// [`ChannelManager::accept_inbound_channel`]: crate::ln::channelmanager::ChannelManager::accept_inbound_channel
/// [`UserConfig::manually_accept_inbound_channels`]: crate::util::config::UserConfig::manually_accept_inbound_channels
user_channel_id: u128,
/// The node_id of the channel counterparty.
/// The `node_id` of the channel counterparty.
counterparty_node_id: PublicKey,
/// The features that this channel will operate with.
channel_type: ChannelTypeFeatures,
},
/// Used to indicate that a previously opened channel with the given `channel_id` is in the
/// process of closure.
ChannelClosed {
/// The channel_id of the channel which has been closed. Note that on-chain transactions
/// The `channel_id` of the channel which has been closed. Note that on-chain transactions
/// resolving the channel are likely still awaiting confirmation.
channel_id: [u8; 32],
/// The `user_channel_id` value passed in to [`ChannelManager::create_channel`] for outbound
Expand DownExpand Up@@ -931,6 +960,16 @@ impl Writeable for Event {
(6, channel_type, required),
});
},
&Event::ChannelPending { ref channel_id, ref user_channel_id, ref former_temporary_channel_id, ref counterparty_node_id, ref funding_txo } => {
31u8.write(writer)?;
write_tlv_fields!(writer, {
(0, channel_id, required),
(2, user_channel_id, required),
(4, former_temporary_channel_id, required),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: option is a bit more straightforward/saves a few bytes on the wire. It doesn't matter, though, just for future reference.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Yes, it's a tiny bit more efficient but to be honest I don't liking mixing implementation type and serialization type semantics if it can be avoided. In my mind option -> field is optional, not field is Option.

(6, counterparty_node_id, required),
(8, funding_txo, required),
});
},
// Note that, going forward, all new events must only write data inside of
// `write_tlv_fields`. Versions 0.0.101+ will ignore odd-numbered events that write
// data via `write_tlv_fields`.
Expand DownExpand Up@@ -1271,6 +1310,31 @@ impl MaybeReadable for Event {
};
f()
},
31u8 => {
let f = || {
let mut channel_id = [0; 32];
let mut user_channel_id: u128 = 0;
let mut former_temporary_channel_id = None;
let mut counterparty_node_id = RequiredWrapper(None);
let mut funding_txo = RequiredWrapper(None);
read_tlv_fields!(reader, {
(0, channel_id, required),
(2, user_channel_id, required),
(4, former_temporary_channel_id, required),
(6, counterparty_node_id, required),
(8, funding_txo, required),
});

Ok(Some(Event::ChannelPending {
channel_id,
user_channel_id,
former_temporary_channel_id,
counterparty_node_id: counterparty_node_id.0.unwrap(),
funding_txo: funding_txo.0.unwrap()
}))
};
f()
},
// Versions prior to 0.0.100 did not ignore odd types, instead returning InvalidValue.
// Version 0.0.100 failed to properly ignore odd types, possibly resulting in corrupt
// reads.
Expand Down
6 changes: 6 additions & 0 deletions lightning/src/ln/chanmon_update_fail_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1864,6 +1864,7 @@ fn do_during_funding_monitor_fail(confirm_a_first: bool, restore_b_before_conf:
let (outpoint, latest_update, _) = nodes[0].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
nodes[0].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
check_added_monitors!(nodes[0], 0);
expect_channel_pending_event(&nodes[0], &nodes[1].node.get_our_node_id());

let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 0);
Expand All@@ -1885,6 +1886,9 @@ fn do_during_funding_monitor_fail(confirm_a_first: bool, restore_b_before_conf:
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id());
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id());
reconnect_nodes(&nodes[0], &nodes[1], (false, confirm_a_first), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (false, false));

// But we want to re-emit ChannelPending
expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id());
assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());

Expand DownExpand Up@@ -2808,6 +2812,7 @@ fn do_test_outbound_reload_without_init_mon(use_0conf: bool) {
let 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);
expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id());

let bs_signed_locked = nodes[1].node.get_and_clear_pending_msg_events();
assert_eq!(bs_signed_locked.len(), if use_0conf { 2 } else { 1 });
Expand DownExpand Up@@ -2906,6 +2911,7 @@ fn do_test_inbound_reload_without_init_mon(use_0conf: bool, lock_commitment: boo

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());

let as_funding_tx = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
if lock_commitment {
Expand Down
43 changes: 42 additions & 1 deletion lightning/src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -499,6 +499,7 @@ pub(super) struct Channel<Signer: ChannelSigner> {
user_id: u128,

channel_id: [u8; 32],
temporary_channel_id: Option<[u8; 32]>, // Will be `None` for channels created prior to 0.0.115.
channel_state: u32,

// When we reach max(6 blocks, minimum_depth), we need to send an AnnouncementSigs message to
Expand DownExpand Up@@ -729,6 +730,9 @@ pub(super) struct Channel<Signer: ChannelSigner> {
// blinded paths instead of simple scid+node_id aliases.
outbound_scid_alias: u64,

// We track whether we already emitted a `ChannelPending` event.
channel_pending_event_emitted: bool,

// We track whether we already emitted a `ChannelReady` event.
channel_ready_event_emitted: bool,

Expand DownExpand Up@@ -991,6 +995,8 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
}
}

let temporary_channel_id = entropy_source.get_secure_random_bytes();

Ok(Channel {
user_id,

Expand All@@ -1004,7 +1010,8 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {

inbound_handshake_limits_override: Some(config.channel_handshake_limits.clone()),

channel_id: entropy_source.get_secure_random_bytes(),
channel_id: temporary_channel_id,
temporary_channel_id: Some(temporary_channel_id),
channel_state: ChannelState::OurInitSent as u32,
announcement_sigs_state: AnnouncementSigsState::NotSent,
secp_ctx,
Expand DownExpand Up@@ -1103,6 +1110,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
latest_inbound_scid_alias: None,
outbound_scid_alias,

channel_pending_event_emitted: false,
channel_ready_event_emitted: false,

#[cfg(any(test, fuzzing))]
Expand DownExpand Up@@ -1350,6 +1358,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
inbound_handshake_limits_override: None,

channel_id: msg.temporary_channel_id,
temporary_channel_id: Some(msg.temporary_channel_id),
channel_state: (ChannelState::OurInitSent as u32) | (ChannelState::TheirInitSent as u32),
announcement_sigs_state: AnnouncementSigsState::NotSent,
secp_ctx,
Expand DownExpand Up@@ -1451,6 +1460,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
latest_inbound_scid_alias: None,
outbound_scid_alias,

channel_pending_event_emitted: false,
channel_ready_event_emitted: false,

#[cfg(any(test, fuzzing))]
Expand DownExpand Up@@ -4550,6 +4560,13 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
self.channel_id
}

// Return the `temporary_channel_id` used during channel establishment.
//
// Will return `None` for channels created prior to LDK version 0.0.115.
pub fn temporary_channel_id(&self) -> Option<[u8; 32]> {
self.temporary_channel_id
}

pub fn minimum_depth(&self) -> Option<u32> {
self.minimum_depth
}
Expand DownExpand Up@@ -4694,6 +4711,21 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
self.prev_config.map(|prev_config| prev_config.0)
}

// Checks whether we should emit a `ChannelPending` event.
pub(crate) fn should_emit_channel_pending_event(&mut self) -> bool {
self.is_funding_initiated() && !self.channel_pending_event_emitted
}

// Returns whether we already emitted a `ChannelPending` event.
pub(crate) fn channel_pending_event_emitted(&self) -> bool {
self.channel_pending_event_emitted
}

// Remembers that we already emitted a `ChannelPending` event.
pub(crate) fn set_channel_pending_event_emitted(&mut self) {
self.channel_pending_event_emitted = true;
}

// Checks whether we should emit a `ChannelReady` event.
pub(crate) fn should_emit_channel_ready_event(&mut self) -> bool {
self.is_usable() && !self.channel_ready_event_emitted
Expand DownExpand Up@@ -6420,6 +6452,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Writeable for Channel<Signer> {
if self.holder_max_htlc_value_in_flight_msat != Self::get_holder_max_htlc_value_in_flight_msat(self.channel_value_satoshis, &old_max_in_flight_percent_config)
{ Some(self.holder_max_htlc_value_in_flight_msat) } else { None };

let channel_pending_event_emitted = Some(self.channel_pending_event_emitted);
let channel_ready_event_emitted = Some(self.channel_ready_event_emitted);

// `user_id` used to be a single u64 value. In order to remain backwards compatible with
Expand DownExpand Up@@ -6452,6 +6485,8 @@ impl<Signer: WriteableEcdsaChannelSigner> Writeable for Channel<Signer> {
(23, channel_ready_event_emitted, option),
(25, user_id_high_opt, option),
(27, self.channel_keys_id, required),
(29, self.temporary_channel_id, option),
(31, channel_pending_event_emitted, option),
});

Ok(())
Expand DownExpand Up@@ -6719,10 +6754,12 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
let mut announcement_sigs_state = Some(AnnouncementSigsState::NotSent);
let mut latest_inbound_scid_alias = None;
let mut outbound_scid_alias = None;
let mut channel_pending_event_emitted = None;
let mut channel_ready_event_emitted = None;

let mut user_id_high_opt: Option<u64> = None;
let mut channel_keys_id: Option<[u8; 32]> = None;
let mut temporary_channel_id: Option<[u8; 32]> = None;

read_tlv_fields!(reader, {
(0, announcement_sigs, option),
Expand All@@ -6743,6 +6780,8 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
(23, channel_ready_event_emitted, option),
(25, user_id_high_opt, option),
(27, channel_keys_id, option),
(29, temporary_channel_id, option),
(31, channel_pending_event_emitted, option),
});

let (channel_keys_id, holder_signer) = if let Some(channel_keys_id) = channel_keys_id {
Expand DownExpand Up@@ -6807,6 +6846,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
inbound_handshake_limits_override: None,

channel_id,
temporary_channel_id,
channel_state,
announcement_sigs_state: announcement_sigs_state.unwrap(),
secp_ctx,
Expand DownExpand Up@@ -6899,6 +6939,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
// Later in the ChannelManager deserialization phase we scan for channels and assign scid aliases if its missing
outbound_scid_alias: outbound_scid_alias.unwrap_or(0),

channel_pending_event_emitted: channel_pending_event_emitted.unwrap_or(true),
channel_ready_event_emitted: channel_ready_event_emitted.unwrap_or(true),

#[cfg(any(test, fuzzing))]
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
11 changes: 11 additions & 0 deletions fuzz/src/chanmon_consistency.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -526,7 +526,18 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out) {
msg.clone()
} else { panic!("Wrong event type"); }
};
let events = $dest.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
if let events::Event::ChannelPending{ ref counterparty_node_id, .. } = events[0] {
assert_eq!(counterparty_node_id, &$source.get_our_node_id());
} else { panic!("Wrong event type"); }

$source.handle_funding_signed(&$dest.get_our_node_id(), &funding_signed);
let events = $source.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
if let events::Event::ChannelPending{ ref counterparty_node_id, .. } = events[0] {
assert_eq!(counterparty_node_id, &$dest.get_our_node_id());
} else { panic!("Wrong event type"); }

funding_output
} }
Expand Down
5 changes: 4 additions & 1 deletion lightning-background-processor/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -710,7 +710,7 @@ mod tests {
use lightning::chain::keysinterface::{InMemorySigner, KeysManager};
use lightning::chain::transaction::OutPoint;
use lightning::events::{Event, PathFailure, MessageSendEventsProvider, MessageSendEvent};
use lightning::get_event_msg;
use lightning::{get_event_msg, get_event};
use lightning::ln::PaymentHash;
use lightning::ln::channelmanager;
use lightning::ln::channelmanager::{BREAKDOWN_TIMEOUT, ChainParameters, MIN_CLTV_EXPIRY_DELTA, PaymentId};
Expand DownExpand Up@@ -1058,7 +1058,10 @@ mod tests {
($node_a: expr, $node_b: expr, $temporary_channel_id: expr, $tx: expr) => {{
$node_a.node.funding_transaction_generated(&$temporary_channel_id, &$node_b.node.get_our_node_id(), $tx.clone()).unwrap();
$node_b.node.handle_funding_created(&$node_a.node.get_our_node_id(), &get_event_msg!($node_a, MessageSendEvent::SendFundingCreated, $node_b.node.get_our_node_id()));
get_event!($node_b, Event::ChannelPending);

$node_a.node.handle_funding_signed(&$node_b.node.get_our_node_id(), &get_event_msg!($node_b, MessageSendEvent::SendFundingSigned, $node_a.node.get_our_node_id()));
get_event!($node_a, Event::ChannelPending);
}}
}

Expand Down
72 changes: 68 additions & 4 deletions lightning/src/events/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -32,7 +32,9 @@ use crate::util::ser::{BigSize, FixedLengthReader, Writeable, Writer, MaybeReada
use crate::util::string::UntrustedString;
use crate::routing::router::{RouteHop, RouteParameters};

use bitcoin::{PackedLockTime, Transaction};
use bitcoin::{PackedLockTime, Transaction, OutPoint};
#[cfg(anchors)]
use bitcoin::{Txid, TxIn, TxOut, Witness};
use bitcoin::blockdata::script::Script;
use bitcoin::hashes::Hash;
use bitcoin::hashes::sha256::Hash as Sha256;
Expand DownExpand Up@@ -608,12 +610,39 @@ pub enum Event {
/// The caveat described above the `fee_earned_msat` field applies here as well.
outbound_amount_forwarded_msat: Option<u64>,
},
/// Used to indicate that a channel with the given `channel_id` is being opened and pending
/// confirmation on-chain.
///
/// This event is emitted when the funding transaction has been signed and is broadcast to the

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should we have a note that, in rare cases, this event may be missed if a channel is ultimately confirmed quickly prior to the event being processed and then we restart? Its a stupid rare case but its possible I think?

@tnulltnullMar 30, 2023

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I'm confused, which scenario are you describing exactly? Wouldn't rather be an edge case that we emit the event twice when we decide to rebroadcast on restart?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think we can: (a) fund the channel, with an async monitor update, (b) not have that complete, but persist the manager, then (c) complete the monitor update and broadcast the funding tx (d) then restart, replay the chain and see the funding tx confirm, then (e) re-complete the monitor update completed in c, then we wont get the event again but the user will never have used it.

I think with "full" async monitor updates this becomes more realistic.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we have a note that, in rare cases, this event may be missed if a channel is ultimately confirmed quickly prior to the event being processed and then we restart?

Since we plan to leverage it for #2137, seems we should always regenerate it in cases like this? The channel will actually be ready, but as long as it's queued before the ChannelReady event, it should be fine.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yea, that's my biggest concern here.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I think we can: (a) fund the channel, with an async monitor update, (b) not have that complete, but persist the manager, then (c) complete the monitor update and broadcast the funding tx (d) then restart, replay the chain and see the funding tx confirm, then (e) re-complete the monitor update completed in c, then we wont get the event again but the user will never have used it.

Ugh., I see. In that scenario it would persist the event queue when the event hasn't been emitted yet and wouldn't re-emit it after restart. Mh, instead of leaving a note I went ahead and am now tracking the emission of ChannelPending in the same way as ChannelReady and removed it from internal_funding_created entirely.
This means that we're now only emitting it after CMU completion or reestablish, i.e., would re-emit it if the "emitted" state wasn't persisted before.

/// network. For 0conf channels it will be immediately followed by the corresponding
/// [`Event::ChannelReady`] event.
ChannelPending {
/// The `channel_id` of the channel that is pending confirmation.
channel_id: [u8; 32],
/// The `user_channel_id` value passed in to [`ChannelManager::create_channel`] for outbound
/// channels, or to [`ChannelManager::accept_inbound_channel`] for inbound channels if
/// [`UserConfig::manually_accept_inbound_channels`] config flag is set to true. Otherwise
/// `user_channel_id` will be randomized for an inbound channel.
///
/// [`ChannelManager::create_channel`]: crate::ln::channelmanager::ChannelManager::create_channel
/// [`ChannelManager::accept_inbound_channel`]: crate::ln::channelmanager::ChannelManager::accept_inbound_channel
/// [`UserConfig::manually_accept_inbound_channels`]: crate::util::config::UserConfig::manually_accept_inbound_channels
user_channel_id: u128,
/// The `temporary_channel_id` this channel used to be known by during channel establishment.
///
/// Will be `None` for channels created prior to LDK version 0.0.115.
former_temporary_channel_id: Option<[u8; 32]>,
/// The `node_id` of the channel counterparty.
counterparty_node_id: PublicKey,
/// The outpoint of the channel's funding transaction.
funding_txo: OutPoint,
},
/// Used to indicate that a channel with the given `channel_id` is ready to
/// be used. This event is emitted either when the funding transaction has been confirmed
/// on-chain, or, in case of a 0conf channel, when both parties have confirmed the channel
/// establishment.
ChannelReady {
/// The channel_id of the channel that is ready.
/// The `channel_id` of the channel that is ready.
channel_id: [u8; 32],
/// The `user_channel_id` value passed in to [`ChannelManager::create_channel`] for outbound
/// channels, or to [`ChannelManager::accept_inbound_channel`] for inbound channels if
Expand All@@ -624,15 +653,15 @@ pub enum Event {
/// [`ChannelManager::accept_inbound_channel`]: crate::ln::channelmanager::ChannelManager::accept_inbound_channel
/// [`UserConfig::manually_accept_inbound_channels`]: crate::util::config::UserConfig::manually_accept_inbound_channels
user_channel_id: u128,
/// The node_id of the channel counterparty.
/// The `node_id` of the channel counterparty.
counterparty_node_id: PublicKey,
/// The features that this channel will operate with.
channel_type: ChannelTypeFeatures,
},
/// Used to indicate that a previously opened channel with the given `channel_id` is in the
/// process of closure.
ChannelClosed {
/// The channel_id of the channel which has been closed. Note that on-chain transactions
/// The `channel_id` of the channel which has been closed. Note that on-chain transactions
/// resolving the channel are likely still awaiting confirmation.
channel_id: [u8; 32],
/// The `user_channel_id` value passed in to [`ChannelManager::create_channel`] for outbound
Expand DownExpand Up@@ -931,6 +960,16 @@ impl Writeable for Event {
(6, channel_type, required),
});
},
&Event::ChannelPending { ref channel_id, ref user_channel_id, ref former_temporary_channel_id, ref counterparty_node_id, ref funding_txo } => {
31u8.write(writer)?;
write_tlv_fields!(writer, {
(0, channel_id, required),
(2, user_channel_id, required),
(4, former_temporary_channel_id, required),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: option is a bit more straightforward/saves a few bytes on the wire. It doesn't matter, though, just for future reference.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Yes, it's a tiny bit more efficient but to be honest I don't liking mixing implementation type and serialization type semantics if it can be avoided. In my mind option -> field is optional, not field is Option.

(6, counterparty_node_id, required),
(8, funding_txo, required),
});
},
// Note that, going forward, all new events must only write data inside of
// `write_tlv_fields`. Versions 0.0.101+ will ignore odd-numbered events that write
// data via `write_tlv_fields`.
Expand DownExpand Up@@ -1271,6 +1310,31 @@ impl MaybeReadable for Event {
};
f()
},
31u8 => {
let f = || {
let mut channel_id = [0; 32];
let mut user_channel_id: u128 = 0;
let mut former_temporary_channel_id = None;
let mut counterparty_node_id = RequiredWrapper(None);
let mut funding_txo = RequiredWrapper(None);
read_tlv_fields!(reader, {
(0, channel_id, required),
(2, user_channel_id, required),
(4, former_temporary_channel_id, required),
(6, counterparty_node_id, required),
(8, funding_txo, required),
});

Ok(Some(Event::ChannelPending {
channel_id,
user_channel_id,
former_temporary_channel_id,
counterparty_node_id: counterparty_node_id.0.unwrap(),
funding_txo: funding_txo.0.unwrap()
}))
};
f()
},
// Versions prior to 0.0.100 did not ignore odd types, instead returning InvalidValue.
// Version 0.0.100 failed to properly ignore odd types, possibly resulting in corrupt
// reads.
Expand Down
6 changes: 6 additions & 0 deletions lightning/src/ln/chanmon_update_fail_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1864,6 +1864,7 @@ fn do_during_funding_monitor_fail(confirm_a_first: bool, restore_b_before_conf:
let (outpoint, latest_update, _) = nodes[0].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
nodes[0].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
check_added_monitors!(nodes[0], 0);
expect_channel_pending_event(&nodes[0], &nodes[1].node.get_our_node_id());

let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 0);
Expand All@@ -1885,6 +1886,9 @@ fn do_during_funding_monitor_fail(confirm_a_first: bool, restore_b_before_conf:
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id());
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id());
reconnect_nodes(&nodes[0], &nodes[1], (false, confirm_a_first), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (false, false));

// But we want to re-emit ChannelPending
expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id());
assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());

Expand DownExpand Up@@ -2808,6 +2812,7 @@ fn do_test_outbound_reload_without_init_mon(use_0conf: bool) {
let 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);
expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id());

let bs_signed_locked = nodes[1].node.get_and_clear_pending_msg_events();
assert_eq!(bs_signed_locked.len(), if use_0conf { 2 } else { 1 });
Expand DownExpand Up@@ -2906,6 +2911,7 @@ fn do_test_inbound_reload_without_init_mon(use_0conf: bool, lock_commitment: boo

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());

let as_funding_tx = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
if lock_commitment {
Expand Down
43 changes: 42 additions & 1 deletion lightning/src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -499,6 +499,7 @@ pub(super) struct Channel<Signer: ChannelSigner> {
user_id: u128,

channel_id: [u8; 32],
temporary_channel_id: Option<[u8; 32]>, // Will be `None` for channels created prior to 0.0.115.
channel_state: u32,

// When we reach max(6 blocks, minimum_depth), we need to send an AnnouncementSigs message to
Expand DownExpand Up@@ -729,6 +730,9 @@ pub(super) struct Channel<Signer: ChannelSigner> {
// blinded paths instead of simple scid+node_id aliases.
outbound_scid_alias: u64,

// We track whether we already emitted a `ChannelPending` event.
channel_pending_event_emitted: bool,

// We track whether we already emitted a `ChannelReady` event.
channel_ready_event_emitted: bool,

Expand DownExpand Up@@ -991,6 +995,8 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
}
}

let temporary_channel_id = entropy_source.get_secure_random_bytes();

Ok(Channel {
user_id,

Expand All@@ -1004,7 +1010,8 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {

inbound_handshake_limits_override: Some(config.channel_handshake_limits.clone()),

channel_id: entropy_source.get_secure_random_bytes(),
channel_id: temporary_channel_id,
temporary_channel_id: Some(temporary_channel_id),
channel_state: ChannelState::OurInitSent as u32,
announcement_sigs_state: AnnouncementSigsState::NotSent,
secp_ctx,
Expand DownExpand Up@@ -1103,6 +1110,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
latest_inbound_scid_alias: None,
outbound_scid_alias,

channel_pending_event_emitted: false,
channel_ready_event_emitted: false,

#[cfg(any(test, fuzzing))]
Expand DownExpand Up@@ -1350,6 +1358,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
inbound_handshake_limits_override: None,

channel_id: msg.temporary_channel_id,
temporary_channel_id: Some(msg.temporary_channel_id),
channel_state: (ChannelState::OurInitSent as u32) | (ChannelState::TheirInitSent as u32),
announcement_sigs_state: AnnouncementSigsState::NotSent,
secp_ctx,
Expand DownExpand Up@@ -1451,6 +1460,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
latest_inbound_scid_alias: None,
outbound_scid_alias,

channel_pending_event_emitted: false,
channel_ready_event_emitted: false,

#[cfg(any(test, fuzzing))]
Expand DownExpand Up@@ -4550,6 +4560,13 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
self.channel_id
}

// Return the `temporary_channel_id` used during channel establishment.
//
// Will return `None` for channels created prior to LDK version 0.0.115.
pub fn temporary_channel_id(&self) -> Option<[u8; 32]> {
self.temporary_channel_id
}

pub fn minimum_depth(&self) -> Option<u32> {
self.minimum_depth
}
Expand DownExpand Up@@ -4694,6 +4711,21 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
self.prev_config.map(|prev_config| prev_config.0)
}

// Checks whether we should emit a `ChannelPending` event.
pub(crate) fn should_emit_channel_pending_event(&mut self) -> bool {
self.is_funding_initiated() && !self.channel_pending_event_emitted
}

// Returns whether we already emitted a `ChannelPending` event.
pub(crate) fn channel_pending_event_emitted(&self) -> bool {
self.channel_pending_event_emitted
}

// Remembers that we already emitted a `ChannelPending` event.
pub(crate) fn set_channel_pending_event_emitted(&mut self) {
self.channel_pending_event_emitted = true;
}

// Checks whether we should emit a `ChannelReady` event.
pub(crate) fn should_emit_channel_ready_event(&mut self) -> bool {
self.is_usable() && !self.channel_ready_event_emitted
Expand DownExpand Up@@ -6420,6 +6452,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Writeable for Channel<Signer> {
if self.holder_max_htlc_value_in_flight_msat != Self::get_holder_max_htlc_value_in_flight_msat(self.channel_value_satoshis, &old_max_in_flight_percent_config)
{ Some(self.holder_max_htlc_value_in_flight_msat) } else { None };

let channel_pending_event_emitted = Some(self.channel_pending_event_emitted);
let channel_ready_event_emitted = Some(self.channel_ready_event_emitted);

// `user_id` used to be a single u64 value. In order to remain backwards compatible with
Expand DownExpand Up@@ -6452,6 +6485,8 @@ impl<Signer: WriteableEcdsaChannelSigner> Writeable for Channel<Signer> {
(23, channel_ready_event_emitted, option),
(25, user_id_high_opt, option),
(27, self.channel_keys_id, required),
(29, self.temporary_channel_id, option),
(31, channel_pending_event_emitted, option),
});

Ok(())
Expand DownExpand Up@@ -6719,10 +6754,12 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
let mut announcement_sigs_state = Some(AnnouncementSigsState::NotSent);
let mut latest_inbound_scid_alias = None;
let mut outbound_scid_alias = None;
let mut channel_pending_event_emitted = None;
let mut channel_ready_event_emitted = None;

let mut user_id_high_opt: Option<u64> = None;
let mut channel_keys_id: Option<[u8; 32]> = None;
let mut temporary_channel_id: Option<[u8; 32]> = None;

read_tlv_fields!(reader, {
(0, announcement_sigs, option),
Expand All@@ -6743,6 +6780,8 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
(23, channel_ready_event_emitted, option),
(25, user_id_high_opt, option),
(27, channel_keys_id, option),
(29, temporary_channel_id, option),
(31, channel_pending_event_emitted, option),
});

let (channel_keys_id, holder_signer) = if let Some(channel_keys_id) = channel_keys_id {
Expand DownExpand Up@@ -6807,6 +6846,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
inbound_handshake_limits_override: None,

channel_id,
temporary_channel_id,
channel_state,
announcement_sigs_state: announcement_sigs_state.unwrap(),
secp_ctx,
Expand DownExpand Up@@ -6899,6 +6939,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
// Later in the ChannelManager deserialization phase we scan for channels and assign scid aliases if its missing
outbound_scid_alias: outbound_scid_alias.unwrap_or(0),

channel_pending_event_emitted: channel_pending_event_emitted.unwrap_or(true),
channel_ready_event_emitted: channel_ready_event_emitted.unwrap_or(true),

#[cfg(any(test, fuzzing))]
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
11 changes: 11 additions & 0 deletions fuzz/src/chanmon_consistency.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -526,7 +526,18 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out) {
msg.clone()
} else { panic!("Wrong event type"); }
};
let events = $dest.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
if let events::Event::ChannelPending{ ref counterparty_node_id, .. } = events[0] {
assert_eq!(counterparty_node_id, &$source.get_our_node_id());
} else { panic!("Wrong event type"); }

$source.handle_funding_signed(&$dest.get_our_node_id(), &funding_signed);
let events = $source.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
if let events::Event::ChannelPending{ ref counterparty_node_id, .. } = events[0] {
assert_eq!(counterparty_node_id, &$dest.get_our_node_id());
} else { panic!("Wrong event type"); }

funding_output
} }
Expand Down
5 changes: 4 additions & 1 deletion lightning-background-processor/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -710,7 +710,7 @@ mod tests {
use lightning::chain::keysinterface::{InMemorySigner, KeysManager};
use lightning::chain::transaction::OutPoint;
use lightning::events::{Event, PathFailure, MessageSendEventsProvider, MessageSendEvent};
use lightning::get_event_msg;
use lightning::{get_event_msg, get_event};
use lightning::ln::PaymentHash;
use lightning::ln::channelmanager;
use lightning::ln::channelmanager::{BREAKDOWN_TIMEOUT, ChainParameters, MIN_CLTV_EXPIRY_DELTA, PaymentId};
Expand DownExpand Up@@ -1058,7 +1058,10 @@ mod tests {
($node_a: expr, $node_b: expr, $temporary_channel_id: expr, $tx: expr) => {{
$node_a.node.funding_transaction_generated(&$temporary_channel_id, &$node_b.node.get_our_node_id(), $tx.clone()).unwrap();
$node_b.node.handle_funding_created(&$node_a.node.get_our_node_id(), &get_event_msg!($node_a, MessageSendEvent::SendFundingCreated, $node_b.node.get_our_node_id()));
get_event!($node_b, Event::ChannelPending);

$node_a.node.handle_funding_signed(&$node_b.node.get_our_node_id(), &get_event_msg!($node_b, MessageSendEvent::SendFundingSigned, $node_a.node.get_our_node_id()));
get_event!($node_a, Event::ChannelPending);
}}
}

Expand Down
72 changes: 68 additions & 4 deletions lightning/src/events/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -32,7 +32,9 @@ use crate::util::ser::{BigSize, FixedLengthReader, Writeable, Writer, MaybeReada
use crate::util::string::UntrustedString;
use crate::routing::router::{RouteHop, RouteParameters};

use bitcoin::{PackedLockTime, Transaction};
use bitcoin::{PackedLockTime, Transaction, OutPoint};
#[cfg(anchors)]
use bitcoin::{Txid, TxIn, TxOut, Witness};
use bitcoin::blockdata::script::Script;
use bitcoin::hashes::Hash;
use bitcoin::hashes::sha256::Hash as Sha256;
Expand DownExpand Up@@ -608,12 +610,39 @@ pub enum Event {
/// The caveat described above the `fee_earned_msat` field applies here as well.
outbound_amount_forwarded_msat: Option<u64>,
},
/// Used to indicate that a channel with the given `channel_id` is being opened and pending
/// confirmation on-chain.
///
/// This event is emitted when the funding transaction has been signed and is broadcast to the

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should we have a note that, in rare cases, this event may be missed if a channel is ultimately confirmed quickly prior to the event being processed and then we restart? Its a stupid rare case but its possible I think?

@tnulltnullMar 30, 2023

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I'm confused, which scenario are you describing exactly? Wouldn't rather be an edge case that we emit the event twice when we decide to rebroadcast on restart?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think we can: (a) fund the channel, with an async monitor update, (b) not have that complete, but persist the manager, then (c) complete the monitor update and broadcast the funding tx (d) then restart, replay the chain and see the funding tx confirm, then (e) re-complete the monitor update completed in c, then we wont get the event again but the user will never have used it.

I think with "full" async monitor updates this becomes more realistic.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we have a note that, in rare cases, this event may be missed if a channel is ultimately confirmed quickly prior to the event being processed and then we restart?

Since we plan to leverage it for #2137, seems we should always regenerate it in cases like this? The channel will actually be ready, but as long as it's queued before the ChannelReady event, it should be fine.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yea, that's my biggest concern here.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I think we can: (a) fund the channel, with an async monitor update, (b) not have that complete, but persist the manager, then (c) complete the monitor update and broadcast the funding tx (d) then restart, replay the chain and see the funding tx confirm, then (e) re-complete the monitor update completed in c, then we wont get the event again but the user will never have used it.

Ugh., I see. In that scenario it would persist the event queue when the event hasn't been emitted yet and wouldn't re-emit it after restart. Mh, instead of leaving a note I went ahead and am now tracking the emission of ChannelPending in the same way as ChannelReady and removed it from internal_funding_created entirely.
This means that we're now only emitting it after CMU completion or reestablish, i.e., would re-emit it if the "emitted" state wasn't persisted before.

/// network. For 0conf channels it will be immediately followed by the corresponding
/// [`Event::ChannelReady`] event.
ChannelPending {
/// The `channel_id` of the channel that is pending confirmation.
channel_id: [u8; 32],
/// The `user_channel_id` value passed in to [`ChannelManager::create_channel`] for outbound
/// channels, or to [`ChannelManager::accept_inbound_channel`] for inbound channels if
/// [`UserConfig::manually_accept_inbound_channels`] config flag is set to true. Otherwise
/// `user_channel_id` will be randomized for an inbound channel.
///
/// [`ChannelManager::create_channel`]: crate::ln::channelmanager::ChannelManager::create_channel
/// [`ChannelManager::accept_inbound_channel`]: crate::ln::channelmanager::ChannelManager::accept_inbound_channel
/// [`UserConfig::manually_accept_inbound_channels`]: crate::util::config::UserConfig::manually_accept_inbound_channels
user_channel_id: u128,
/// The `temporary_channel_id` this channel used to be known by during channel establishment.
///
/// Will be `None` for channels created prior to LDK version 0.0.115.
former_temporary_channel_id: Option<[u8; 32]>,
/// The `node_id` of the channel counterparty.
counterparty_node_id: PublicKey,
/// The outpoint of the channel's funding transaction.
funding_txo: OutPoint,
},
/// Used to indicate that a channel with the given `channel_id` is ready to
/// be used. This event is emitted either when the funding transaction has been confirmed
/// on-chain, or, in case of a 0conf channel, when both parties have confirmed the channel
/// establishment.
ChannelReady {
/// The channel_id of the channel that is ready.
/// The `channel_id` of the channel that is ready.
channel_id: [u8; 32],
/// The `user_channel_id` value passed in to [`ChannelManager::create_channel`] for outbound
/// channels, or to [`ChannelManager::accept_inbound_channel`] for inbound channels if
Expand All@@ -624,15 +653,15 @@ pub enum Event {
/// [`ChannelManager::accept_inbound_channel`]: crate::ln::channelmanager::ChannelManager::accept_inbound_channel
/// [`UserConfig::manually_accept_inbound_channels`]: crate::util::config::UserConfig::manually_accept_inbound_channels
user_channel_id: u128,
/// The node_id of the channel counterparty.
/// The `node_id` of the channel counterparty.
counterparty_node_id: PublicKey,
/// The features that this channel will operate with.
channel_type: ChannelTypeFeatures,
},
/// Used to indicate that a previously opened channel with the given `channel_id` is in the
/// process of closure.
ChannelClosed {
/// The channel_id of the channel which has been closed. Note that on-chain transactions
/// The `channel_id` of the channel which has been closed. Note that on-chain transactions
/// resolving the channel are likely still awaiting confirmation.
channel_id: [u8; 32],
/// The `user_channel_id` value passed in to [`ChannelManager::create_channel`] for outbound
Expand DownExpand Up@@ -931,6 +960,16 @@ impl Writeable for Event {
(6, channel_type, required),
});
},
&Event::ChannelPending { ref channel_id, ref user_channel_id, ref former_temporary_channel_id, ref counterparty_node_id, ref funding_txo } => {
31u8.write(writer)?;
write_tlv_fields!(writer, {
(0, channel_id, required),
(2, user_channel_id, required),
(4, former_temporary_channel_id, required),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: option is a bit more straightforward/saves a few bytes on the wire. It doesn't matter, though, just for future reference.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Yes, it's a tiny bit more efficient but to be honest I don't liking mixing implementation type and serialization type semantics if it can be avoided. In my mind option -> field is optional, not field is Option.

(6, counterparty_node_id, required),
(8, funding_txo, required),
});
},
// Note that, going forward, all new events must only write data inside of
// `write_tlv_fields`. Versions 0.0.101+ will ignore odd-numbered events that write
// data via `write_tlv_fields`.
Expand DownExpand Up@@ -1271,6 +1310,31 @@ impl MaybeReadable for Event {
};
f()
},
31u8 => {
let f = || {
let mut channel_id = [0; 32];
let mut user_channel_id: u128 = 0;
let mut former_temporary_channel_id = None;
let mut counterparty_node_id = RequiredWrapper(None);
let mut funding_txo = RequiredWrapper(None);
read_tlv_fields!(reader, {
(0, channel_id, required),
(2, user_channel_id, required),
(4, former_temporary_channel_id, required),
(6, counterparty_node_id, required),
(8, funding_txo, required),
});

Ok(Some(Event::ChannelPending {
channel_id,
user_channel_id,
former_temporary_channel_id,
counterparty_node_id: counterparty_node_id.0.unwrap(),
funding_txo: funding_txo.0.unwrap()
}))
};
f()
},
// Versions prior to 0.0.100 did not ignore odd types, instead returning InvalidValue.
// Version 0.0.100 failed to properly ignore odd types, possibly resulting in corrupt
// reads.
Expand Down
6 changes: 6 additions & 0 deletions lightning/src/ln/chanmon_update_fail_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1864,6 +1864,7 @@ fn do_during_funding_monitor_fail(confirm_a_first: bool, restore_b_before_conf:
let (outpoint, latest_update, _) = nodes[0].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
nodes[0].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
check_added_monitors!(nodes[0], 0);
expect_channel_pending_event(&nodes[0], &nodes[1].node.get_our_node_id());

let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 0);
Expand All@@ -1885,6 +1886,9 @@ fn do_during_funding_monitor_fail(confirm_a_first: bool, restore_b_before_conf:
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id());
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id());
reconnect_nodes(&nodes[0], &nodes[1], (false, confirm_a_first), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (false, false));

// But we want to re-emit ChannelPending
expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id());
assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());

Expand DownExpand Up@@ -2808,6 +2812,7 @@ fn do_test_outbound_reload_without_init_mon(use_0conf: bool) {
let 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);
expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id());

let bs_signed_locked = nodes[1].node.get_and_clear_pending_msg_events();
assert_eq!(bs_signed_locked.len(), if use_0conf { 2 } else { 1 });
Expand DownExpand Up@@ -2906,6 +2911,7 @@ fn do_test_inbound_reload_without_init_mon(use_0conf: bool, lock_commitment: boo

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());

let as_funding_tx = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
if lock_commitment {
Expand Down
43 changes: 42 additions & 1 deletion lightning/src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -499,6 +499,7 @@ pub(super) struct Channel<Signer: ChannelSigner> {
user_id: u128,

channel_id: [u8; 32],
temporary_channel_id: Option<[u8; 32]>, // Will be `None` for channels created prior to 0.0.115.
channel_state: u32,

// When we reach max(6 blocks, minimum_depth), we need to send an AnnouncementSigs message to
Expand DownExpand Up@@ -729,6 +730,9 @@ pub(super) struct Channel<Signer: ChannelSigner> {
// blinded paths instead of simple scid+node_id aliases.
outbound_scid_alias: u64,

// We track whether we already emitted a `ChannelPending` event.
channel_pending_event_emitted: bool,

// We track whether we already emitted a `ChannelReady` event.
channel_ready_event_emitted: bool,

Expand DownExpand Up@@ -991,6 +995,8 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
}
}

let temporary_channel_id = entropy_source.get_secure_random_bytes();

Ok(Channel {
user_id,

Expand All@@ -1004,7 +1010,8 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {

inbound_handshake_limits_override: Some(config.channel_handshake_limits.clone()),

channel_id: entropy_source.get_secure_random_bytes(),
channel_id: temporary_channel_id,
temporary_channel_id: Some(temporary_channel_id),
channel_state: ChannelState::OurInitSent as u32,
announcement_sigs_state: AnnouncementSigsState::NotSent,
secp_ctx,
Expand DownExpand Up@@ -1103,6 +1110,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
latest_inbound_scid_alias: None,
outbound_scid_alias,

channel_pending_event_emitted: false,
channel_ready_event_emitted: false,

#[cfg(any(test, fuzzing))]
Expand DownExpand Up@@ -1350,6 +1358,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
inbound_handshake_limits_override: None,

channel_id: msg.temporary_channel_id,
temporary_channel_id: Some(msg.temporary_channel_id),
channel_state: (ChannelState::OurInitSent as u32) | (ChannelState::TheirInitSent as u32),
announcement_sigs_state: AnnouncementSigsState::NotSent,
secp_ctx,
Expand DownExpand Up@@ -1451,6 +1460,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
latest_inbound_scid_alias: None,
outbound_scid_alias,

channel_pending_event_emitted: false,
channel_ready_event_emitted: false,

#[cfg(any(test, fuzzing))]
Expand DownExpand Up@@ -4550,6 +4560,13 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
self.channel_id
}

// Return the `temporary_channel_id` used during channel establishment.
//
// Will return `None` for channels created prior to LDK version 0.0.115.
pub fn temporary_channel_id(&self) -> Option<[u8; 32]> {
self.temporary_channel_id
}

pub fn minimum_depth(&self) -> Option<u32> {
self.minimum_depth
}
Expand DownExpand Up@@ -4694,6 +4711,21 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
self.prev_config.map(|prev_config| prev_config.0)
}

// Checks whether we should emit a `ChannelPending` event.
pub(crate) fn should_emit_channel_pending_event(&mut self) -> bool {
self.is_funding_initiated() && !self.channel_pending_event_emitted
}

// Returns whether we already emitted a `ChannelPending` event.
pub(crate) fn channel_pending_event_emitted(&self) -> bool {
self.channel_pending_event_emitted
}

// Remembers that we already emitted a `ChannelPending` event.
pub(crate) fn set_channel_pending_event_emitted(&mut self) {
self.channel_pending_event_emitted = true;
}

// Checks whether we should emit a `ChannelReady` event.
pub(crate) fn should_emit_channel_ready_event(&mut self) -> bool {
self.is_usable() && !self.channel_ready_event_emitted
Expand DownExpand Up@@ -6420,6 +6452,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Writeable for Channel<Signer> {
if self.holder_max_htlc_value_in_flight_msat != Self::get_holder_max_htlc_value_in_flight_msat(self.channel_value_satoshis, &old_max_in_flight_percent_config)
{ Some(self.holder_max_htlc_value_in_flight_msat) } else { None };

let channel_pending_event_emitted = Some(self.channel_pending_event_emitted);
let channel_ready_event_emitted = Some(self.channel_ready_event_emitted);

// `user_id` used to be a single u64 value. In order to remain backwards compatible with
Expand DownExpand Up@@ -6452,6 +6485,8 @@ impl<Signer: WriteableEcdsaChannelSigner> Writeable for Channel<Signer> {
(23, channel_ready_event_emitted, option),
(25, user_id_high_opt, option),
(27, self.channel_keys_id, required),
(29, self.temporary_channel_id, option),
(31, channel_pending_event_emitted, option),
});

Ok(())
Expand DownExpand Up@@ -6719,10 +6754,12 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
let mut announcement_sigs_state = Some(AnnouncementSigsState::NotSent);
let mut latest_inbound_scid_alias = None;
let mut outbound_scid_alias = None;
let mut channel_pending_event_emitted = None;
let mut channel_ready_event_emitted = None;

let mut user_id_high_opt: Option<u64> = None;
let mut channel_keys_id: Option<[u8; 32]> = None;
let mut temporary_channel_id: Option<[u8; 32]> = None;

read_tlv_fields!(reader, {
(0, announcement_sigs, option),
Expand All@@ -6743,6 +6780,8 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
(23, channel_ready_event_emitted, option),
(25, user_id_high_opt, option),
(27, channel_keys_id, option),
(29, temporary_channel_id, option),
(31, channel_pending_event_emitted, option),
});

let (channel_keys_id, holder_signer) = if let Some(channel_keys_id) = channel_keys_id {
Expand DownExpand Up@@ -6807,6 +6846,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
inbound_handshake_limits_override: None,

channel_id,
temporary_channel_id,
channel_state,
announcement_sigs_state: announcement_sigs_state.unwrap(),
secp_ctx,
Expand DownExpand Up@@ -6899,6 +6939,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
// Later in the ChannelManager deserialization phase we scan for channels and assign scid aliases if its missing
outbound_scid_alias: outbound_scid_alias.unwrap_or(0),

channel_pending_event_emitted: channel_pending_event_emitted.unwrap_or(true),
channel_ready_event_emitted: channel_ready_event_emitted.unwrap_or(true),

#[cfg(any(test, fuzzing))]
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
11 changes: 11 additions & 0 deletions fuzz/src/chanmon_consistency.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -526,7 +526,18 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out) {
msg.clone()
} else { panic!("Wrong event type"); }
};
let events = $dest.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
if let events::Event::ChannelPending{ ref counterparty_node_id, .. } = events[0] {
assert_eq!(counterparty_node_id, &$source.get_our_node_id());
} else { panic!("Wrong event type"); }

$source.handle_funding_signed(&$dest.get_our_node_id(), &funding_signed);
let events = $source.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
if let events::Event::ChannelPending{ ref counterparty_node_id, .. } = events[0] {
assert_eq!(counterparty_node_id, &$dest.get_our_node_id());
} else { panic!("Wrong event type"); }

funding_output
} }
Expand Down
5 changes: 4 additions & 1 deletion lightning-background-processor/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -710,7 +710,7 @@ mod tests {
use lightning::chain::keysinterface::{InMemorySigner, KeysManager};
use lightning::chain::transaction::OutPoint;
use lightning::events::{Event, PathFailure, MessageSendEventsProvider, MessageSendEvent};
use lightning::get_event_msg;
use lightning::{get_event_msg, get_event};
use lightning::ln::PaymentHash;
use lightning::ln::channelmanager;
use lightning::ln::channelmanager::{BREAKDOWN_TIMEOUT, ChainParameters, MIN_CLTV_EXPIRY_DELTA, PaymentId};
Expand DownExpand Up@@ -1058,7 +1058,10 @@ mod tests {
($node_a: expr, $node_b: expr, $temporary_channel_id: expr, $tx: expr) => {{
$node_a.node.funding_transaction_generated(&$temporary_channel_id, &$node_b.node.get_our_node_id(), $tx.clone()).unwrap();
$node_b.node.handle_funding_created(&$node_a.node.get_our_node_id(), &get_event_msg!($node_a, MessageSendEvent::SendFundingCreated, $node_b.node.get_our_node_id()));
get_event!($node_b, Event::ChannelPending);

$node_a.node.handle_funding_signed(&$node_b.node.get_our_node_id(), &get_event_msg!($node_b, MessageSendEvent::SendFundingSigned, $node_a.node.get_our_node_id()));
get_event!($node_a, Event::ChannelPending);
}}
}

Expand Down
72 changes: 68 additions & 4 deletions lightning/src/events/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -32,7 +32,9 @@ use crate::util::ser::{BigSize, FixedLengthReader, Writeable, Writer, MaybeReada
use crate::util::string::UntrustedString;
use crate::routing::router::{RouteHop, RouteParameters};

use bitcoin::{PackedLockTime, Transaction};
use bitcoin::{PackedLockTime, Transaction, OutPoint};
#[cfg(anchors)]
use bitcoin::{Txid, TxIn, TxOut, Witness};
use bitcoin::blockdata::script::Script;
use bitcoin::hashes::Hash;
use bitcoin::hashes::sha256::Hash as Sha256;
Expand DownExpand Up@@ -608,12 +610,39 @@ pub enum Event {
/// The caveat described above the `fee_earned_msat` field applies here as well.
outbound_amount_forwarded_msat: Option<u64>,
},
/// Used to indicate that a channel with the given `channel_id` is being opened and pending
/// confirmation on-chain.
///
/// This event is emitted when the funding transaction has been signed and is broadcast to the

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should we have a note that, in rare cases, this event may be missed if a channel is ultimately confirmed quickly prior to the event being processed and then we restart? Its a stupid rare case but its possible I think?

@tnulltnullMar 30, 2023

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I'm confused, which scenario are you describing exactly? Wouldn't rather be an edge case that we emit the event twice when we decide to rebroadcast on restart?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think we can: (a) fund the channel, with an async monitor update, (b) not have that complete, but persist the manager, then (c) complete the monitor update and broadcast the funding tx (d) then restart, replay the chain and see the funding tx confirm, then (e) re-complete the monitor update completed in c, then we wont get the event again but the user will never have used it.

I think with "full" async monitor updates this becomes more realistic.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we have a note that, in rare cases, this event may be missed if a channel is ultimately confirmed quickly prior to the event being processed and then we restart?

Since we plan to leverage it for #2137, seems we should always regenerate it in cases like this? The channel will actually be ready, but as long as it's queued before the ChannelReady event, it should be fine.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yea, that's my biggest concern here.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I think we can: (a) fund the channel, with an async monitor update, (b) not have that complete, but persist the manager, then (c) complete the monitor update and broadcast the funding tx (d) then restart, replay the chain and see the funding tx confirm, then (e) re-complete the monitor update completed in c, then we wont get the event again but the user will never have used it.

Ugh., I see. In that scenario it would persist the event queue when the event hasn't been emitted yet and wouldn't re-emit it after restart. Mh, instead of leaving a note I went ahead and am now tracking the emission of ChannelPending in the same way as ChannelReady and removed it from internal_funding_created entirely.
This means that we're now only emitting it after CMU completion or reestablish, i.e., would re-emit it if the "emitted" state wasn't persisted before.

/// network. For 0conf channels it will be immediately followed by the corresponding
/// [`Event::ChannelReady`] event.
ChannelPending {
/// The `channel_id` of the channel that is pending confirmation.
channel_id: [u8; 32],
/// The `user_channel_id` value passed in to [`ChannelManager::create_channel`] for outbound
/// channels, or to [`ChannelManager::accept_inbound_channel`] for inbound channels if
/// [`UserConfig::manually_accept_inbound_channels`] config flag is set to true. Otherwise
/// `user_channel_id` will be randomized for an inbound channel.
///
/// [`ChannelManager::create_channel`]: crate::ln::channelmanager::ChannelManager::create_channel
/// [`ChannelManager::accept_inbound_channel`]: crate::ln::channelmanager::ChannelManager::accept_inbound_channel
/// [`UserConfig::manually_accept_inbound_channels`]: crate::util::config::UserConfig::manually_accept_inbound_channels
user_channel_id: u128,
/// The `temporary_channel_id` this channel used to be known by during channel establishment.
///
/// Will be `None` for channels created prior to LDK version 0.0.115.
former_temporary_channel_id: Option<[u8; 32]>,
/// The `node_id` of the channel counterparty.
counterparty_node_id: PublicKey,
/// The outpoint of the channel's funding transaction.
funding_txo: OutPoint,
},
/// Used to indicate that a channel with the given `channel_id` is ready to
/// be used. This event is emitted either when the funding transaction has been confirmed
/// on-chain, or, in case of a 0conf channel, when both parties have confirmed the channel
/// establishment.
ChannelReady {
/// The channel_id of the channel that is ready.
/// The `channel_id` of the channel that is ready.
channel_id: [u8; 32],
/// The `user_channel_id` value passed in to [`ChannelManager::create_channel`] for outbound
/// channels, or to [`ChannelManager::accept_inbound_channel`] for inbound channels if
Expand All@@ -624,15 +653,15 @@ pub enum Event {
/// [`ChannelManager::accept_inbound_channel`]: crate::ln::channelmanager::ChannelManager::accept_inbound_channel
/// [`UserConfig::manually_accept_inbound_channels`]: crate::util::config::UserConfig::manually_accept_inbound_channels
user_channel_id: u128,
/// The node_id of the channel counterparty.
/// The `node_id` of the channel counterparty.
counterparty_node_id: PublicKey,
/// The features that this channel will operate with.
channel_type: ChannelTypeFeatures,
},
/// Used to indicate that a previously opened channel with the given `channel_id` is in the
/// process of closure.
ChannelClosed {
/// The channel_id of the channel which has been closed. Note that on-chain transactions
/// The `channel_id` of the channel which has been closed. Note that on-chain transactions
/// resolving the channel are likely still awaiting confirmation.
channel_id: [u8; 32],
/// The `user_channel_id` value passed in to [`ChannelManager::create_channel`] for outbound
Expand DownExpand Up@@ -931,6 +960,16 @@ impl Writeable for Event {
(6, channel_type, required),
});
},
&Event::ChannelPending { ref channel_id, ref user_channel_id, ref former_temporary_channel_id, ref counterparty_node_id, ref funding_txo } => {
31u8.write(writer)?;
write_tlv_fields!(writer, {
(0, channel_id, required),
(2, user_channel_id, required),
(4, former_temporary_channel_id, required),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: option is a bit more straightforward/saves a few bytes on the wire. It doesn't matter, though, just for future reference.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Yes, it's a tiny bit more efficient but to be honest I don't liking mixing implementation type and serialization type semantics if it can be avoided. In my mind option -> field is optional, not field is Option.

(6, counterparty_node_id, required),
(8, funding_txo, required),
});
},
// Note that, going forward, all new events must only write data inside of
// `write_tlv_fields`. Versions 0.0.101+ will ignore odd-numbered events that write
// data via `write_tlv_fields`.
Expand DownExpand Up@@ -1271,6 +1310,31 @@ impl MaybeReadable for Event {
};
f()
},
31u8 => {
let f = || {
let mut channel_id = [0; 32];
let mut user_channel_id: u128 = 0;
let mut former_temporary_channel_id = None;
let mut counterparty_node_id = RequiredWrapper(None);
let mut funding_txo = RequiredWrapper(None);
read_tlv_fields!(reader, {
(0, channel_id, required),
(2, user_channel_id, required),
(4, former_temporary_channel_id, required),
(6, counterparty_node_id, required),
(8, funding_txo, required),
});

Ok(Some(Event::ChannelPending {
channel_id,
user_channel_id,
former_temporary_channel_id,
counterparty_node_id: counterparty_node_id.0.unwrap(),
funding_txo: funding_txo.0.unwrap()
}))
};
f()
},
// Versions prior to 0.0.100 did not ignore odd types, instead returning InvalidValue.
// Version 0.0.100 failed to properly ignore odd types, possibly resulting in corrupt
// reads.
Expand Down
6 changes: 6 additions & 0 deletions lightning/src/ln/chanmon_update_fail_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1864,6 +1864,7 @@ fn do_during_funding_monitor_fail(confirm_a_first: bool, restore_b_before_conf:
let (outpoint, latest_update, _) = nodes[0].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
nodes[0].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
check_added_monitors!(nodes[0], 0);
expect_channel_pending_event(&nodes[0], &nodes[1].node.get_our_node_id());

let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 0);
Expand All@@ -1885,6 +1886,9 @@ fn do_during_funding_monitor_fail(confirm_a_first: bool, restore_b_before_conf:
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id());
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id());
reconnect_nodes(&nodes[0], &nodes[1], (false, confirm_a_first), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (false, false));

// But we want to re-emit ChannelPending
expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id());
assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());

Expand DownExpand Up@@ -2808,6 +2812,7 @@ fn do_test_outbound_reload_without_init_mon(use_0conf: bool) {
let 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);
expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id());

let bs_signed_locked = nodes[1].node.get_and_clear_pending_msg_events();
assert_eq!(bs_signed_locked.len(), if use_0conf { 2 } else { 1 });
Expand DownExpand Up@@ -2906,6 +2911,7 @@ fn do_test_inbound_reload_without_init_mon(use_0conf: bool, lock_commitment: boo

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());

let as_funding_tx = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
if lock_commitment {
Expand Down
43 changes: 42 additions & 1 deletion lightning/src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -499,6 +499,7 @@ pub(super) struct Channel<Signer: ChannelSigner> {
user_id: u128,

channel_id: [u8; 32],
temporary_channel_id: Option<[u8; 32]>, // Will be `None` for channels created prior to 0.0.115.
channel_state: u32,

// When we reach max(6 blocks, minimum_depth), we need to send an AnnouncementSigs message to
Expand DownExpand Up@@ -729,6 +730,9 @@ pub(super) struct Channel<Signer: ChannelSigner> {
// blinded paths instead of simple scid+node_id aliases.
outbound_scid_alias: u64,

// We track whether we already emitted a `ChannelPending` event.
channel_pending_event_emitted: bool,

// We track whether we already emitted a `ChannelReady` event.
channel_ready_event_emitted: bool,

Expand DownExpand Up@@ -991,6 +995,8 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
}
}

let temporary_channel_id = entropy_source.get_secure_random_bytes();

Ok(Channel {
user_id,

Expand All@@ -1004,7 +1010,8 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {

inbound_handshake_limits_override: Some(config.channel_handshake_limits.clone()),

channel_id: entropy_source.get_secure_random_bytes(),
channel_id: temporary_channel_id,
temporary_channel_id: Some(temporary_channel_id),
channel_state: ChannelState::OurInitSent as u32,
announcement_sigs_state: AnnouncementSigsState::NotSent,
secp_ctx,
Expand DownExpand Up@@ -1103,6 +1110,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
latest_inbound_scid_alias: None,
outbound_scid_alias,

channel_pending_event_emitted: false,
channel_ready_event_emitted: false,

#[cfg(any(test, fuzzing))]
Expand DownExpand Up@@ -1350,6 +1358,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
inbound_handshake_limits_override: None,

channel_id: msg.temporary_channel_id,
temporary_channel_id: Some(msg.temporary_channel_id),
channel_state: (ChannelState::OurInitSent as u32) | (ChannelState::TheirInitSent as u32),
announcement_sigs_state: AnnouncementSigsState::NotSent,
secp_ctx,
Expand DownExpand Up@@ -1451,6 +1460,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
latest_inbound_scid_alias: None,
outbound_scid_alias,

channel_pending_event_emitted: false,
channel_ready_event_emitted: false,

#[cfg(any(test, fuzzing))]
Expand DownExpand Up@@ -4550,6 +4560,13 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
self.channel_id
}

// Return the `temporary_channel_id` used during channel establishment.
//
// Will return `None` for channels created prior to LDK version 0.0.115.
pub fn temporary_channel_id(&self) -> Option<[u8; 32]> {
self.temporary_channel_id
}

pub fn minimum_depth(&self) -> Option<u32> {
self.minimum_depth
}
Expand DownExpand Up@@ -4694,6 +4711,21 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
self.prev_config.map(|prev_config| prev_config.0)
}

// Checks whether we should emit a `ChannelPending` event.
pub(crate) fn should_emit_channel_pending_event(&mut self) -> bool {
self.is_funding_initiated() && !self.channel_pending_event_emitted
}

// Returns whether we already emitted a `ChannelPending` event.
pub(crate) fn channel_pending_event_emitted(&self) -> bool {
self.channel_pending_event_emitted
}

// Remembers that we already emitted a `ChannelPending` event.
pub(crate) fn set_channel_pending_event_emitted(&mut self) {
self.channel_pending_event_emitted = true;
}

// Checks whether we should emit a `ChannelReady` event.
pub(crate) fn should_emit_channel_ready_event(&mut self) -> bool {
self.is_usable() && !self.channel_ready_event_emitted
Expand DownExpand Up@@ -6420,6 +6452,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Writeable for Channel<Signer> {
if self.holder_max_htlc_value_in_flight_msat != Self::get_holder_max_htlc_value_in_flight_msat(self.channel_value_satoshis, &old_max_in_flight_percent_config)
{ Some(self.holder_max_htlc_value_in_flight_msat) } else { None };

let channel_pending_event_emitted = Some(self.channel_pending_event_emitted);
let channel_ready_event_emitted = Some(self.channel_ready_event_emitted);

// `user_id` used to be a single u64 value. In order to remain backwards compatible with
Expand DownExpand Up@@ -6452,6 +6485,8 @@ impl<Signer: WriteableEcdsaChannelSigner> Writeable for Channel<Signer> {
(23, channel_ready_event_emitted, option),
(25, user_id_high_opt, option),
(27, self.channel_keys_id, required),
(29, self.temporary_channel_id, option),
(31, channel_pending_event_emitted, option),
});

Ok(())
Expand DownExpand Up@@ -6719,10 +6754,12 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
let mut announcement_sigs_state = Some(AnnouncementSigsState::NotSent);
let mut latest_inbound_scid_alias = None;
let mut outbound_scid_alias = None;
let mut channel_pending_event_emitted = None;
let mut channel_ready_event_emitted = None;

let mut user_id_high_opt: Option<u64> = None;
let mut channel_keys_id: Option<[u8; 32]> = None;
let mut temporary_channel_id: Option<[u8; 32]> = None;

read_tlv_fields!(reader, {
(0, announcement_sigs, option),
Expand All@@ -6743,6 +6780,8 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
(23, channel_ready_event_emitted, option),
(25, user_id_high_opt, option),
(27, channel_keys_id, option),
(29, temporary_channel_id, option),
(31, channel_pending_event_emitted, option),
});

let (channel_keys_id, holder_signer) = if let Some(channel_keys_id) = channel_keys_id {
Expand DownExpand Up@@ -6807,6 +6846,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
inbound_handshake_limits_override: None,

channel_id,
temporary_channel_id,
channel_state,
announcement_sigs_state: announcement_sigs_state.unwrap(),
secp_ctx,
Expand DownExpand Up@@ -6899,6 +6939,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
// Later in the ChannelManager deserialization phase we scan for channels and assign scid aliases if its missing
outbound_scid_alias: outbound_scid_alias.unwrap_or(0),

channel_pending_event_emitted: channel_pending_event_emitted.unwrap_or(true),
channel_ready_event_emitted: channel_ready_event_emitted.unwrap_or(true),

#[cfg(any(test, fuzzing))]
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
11 changes: 11 additions & 0 deletions fuzz/src/chanmon_consistency.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -526,7 +526,18 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out) {
msg.clone()
} else { panic!("Wrong event type"); }
};
let events = $dest.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
if let events::Event::ChannelPending{ ref counterparty_node_id, .. } = events[0] {
assert_eq!(counterparty_node_id, &$source.get_our_node_id());
} else { panic!("Wrong event type"); }

$source.handle_funding_signed(&$dest.get_our_node_id(), &funding_signed);
let events = $source.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
if let events::Event::ChannelPending{ ref counterparty_node_id, .. } = events[0] {
assert_eq!(counterparty_node_id, &$dest.get_our_node_id());
} else { panic!("Wrong event type"); }

funding_output
} }
Expand Down
5 changes: 4 additions & 1 deletion lightning-background-processor/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -710,7 +710,7 @@ mod tests {
use lightning::chain::keysinterface::{InMemorySigner, KeysManager};
use lightning::chain::transaction::OutPoint;
use lightning::events::{Event, PathFailure, MessageSendEventsProvider, MessageSendEvent};
use lightning::get_event_msg;
use lightning::{get_event_msg, get_event};
use lightning::ln::PaymentHash;
use lightning::ln::channelmanager;
use lightning::ln::channelmanager::{BREAKDOWN_TIMEOUT, ChainParameters, MIN_CLTV_EXPIRY_DELTA, PaymentId};
Expand DownExpand Up@@ -1058,7 +1058,10 @@ mod tests {
($node_a: expr, $node_b: expr, $temporary_channel_id: expr, $tx: expr) => {{
$node_a.node.funding_transaction_generated(&$temporary_channel_id, &$node_b.node.get_our_node_id(), $tx.clone()).unwrap();
$node_b.node.handle_funding_created(&$node_a.node.get_our_node_id(), &get_event_msg!($node_a, MessageSendEvent::SendFundingCreated, $node_b.node.get_our_node_id()));
get_event!($node_b, Event::ChannelPending);

$node_a.node.handle_funding_signed(&$node_b.node.get_our_node_id(), &get_event_msg!($node_b, MessageSendEvent::SendFundingSigned, $node_a.node.get_our_node_id()));
get_event!($node_a, Event::ChannelPending);
}}
}

Expand Down
72 changes: 68 additions & 4 deletions lightning/src/events/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -32,7 +32,9 @@ use crate::util::ser::{BigSize, FixedLengthReader, Writeable, Writer, MaybeReada
use crate::util::string::UntrustedString;
use crate::routing::router::{RouteHop, RouteParameters};

use bitcoin::{PackedLockTime, Transaction};
use bitcoin::{PackedLockTime, Transaction, OutPoint};
#[cfg(anchors)]
use bitcoin::{Txid, TxIn, TxOut, Witness};
use bitcoin::blockdata::script::Script;
use bitcoin::hashes::Hash;
use bitcoin::hashes::sha256::Hash as Sha256;
Expand DownExpand Up@@ -608,12 +610,39 @@ pub enum Event {
/// The caveat described above the `fee_earned_msat` field applies here as well.
outbound_amount_forwarded_msat: Option<u64>,
},
/// Used to indicate that a channel with the given `channel_id` is being opened and pending
/// confirmation on-chain.
///
/// This event is emitted when the funding transaction has been signed and is broadcast to the

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should we have a note that, in rare cases, this event may be missed if a channel is ultimately confirmed quickly prior to the event being processed and then we restart? Its a stupid rare case but its possible I think?

@tnulltnullMar 30, 2023

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I'm confused, which scenario are you describing exactly? Wouldn't rather be an edge case that we emit the event twice when we decide to rebroadcast on restart?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think we can: (a) fund the channel, with an async monitor update, (b) not have that complete, but persist the manager, then (c) complete the monitor update and broadcast the funding tx (d) then restart, replay the chain and see the funding tx confirm, then (e) re-complete the monitor update completed in c, then we wont get the event again but the user will never have used it.

I think with "full" async monitor updates this becomes more realistic.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we have a note that, in rare cases, this event may be missed if a channel is ultimately confirmed quickly prior to the event being processed and then we restart?

Since we plan to leverage it for #2137, seems we should always regenerate it in cases like this? The channel will actually be ready, but as long as it's queued before the ChannelReady event, it should be fine.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yea, that's my biggest concern here.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I think we can: (a) fund the channel, with an async monitor update, (b) not have that complete, but persist the manager, then (c) complete the monitor update and broadcast the funding tx (d) then restart, replay the chain and see the funding tx confirm, then (e) re-complete the monitor update completed in c, then we wont get the event again but the user will never have used it.

Ugh., I see. In that scenario it would persist the event queue when the event hasn't been emitted yet and wouldn't re-emit it after restart. Mh, instead of leaving a note I went ahead and am now tracking the emission of ChannelPending in the same way as ChannelReady and removed it from internal_funding_created entirely.
This means that we're now only emitting it after CMU completion or reestablish, i.e., would re-emit it if the "emitted" state wasn't persisted before.

/// network. For 0conf channels it will be immediately followed by the corresponding
/// [`Event::ChannelReady`] event.
ChannelPending {
/// The `channel_id` of the channel that is pending confirmation.
channel_id: [u8; 32],
/// The `user_channel_id` value passed in to [`ChannelManager::create_channel`] for outbound
/// channels, or to [`ChannelManager::accept_inbound_channel`] for inbound channels if
/// [`UserConfig::manually_accept_inbound_channels`] config flag is set to true. Otherwise
/// `user_channel_id` will be randomized for an inbound channel.
///
/// [`ChannelManager::create_channel`]: crate::ln::channelmanager::ChannelManager::create_channel
/// [`ChannelManager::accept_inbound_channel`]: crate::ln::channelmanager::ChannelManager::accept_inbound_channel
/// [`UserConfig::manually_accept_inbound_channels`]: crate::util::config::UserConfig::manually_accept_inbound_channels
user_channel_id: u128,
/// The `temporary_channel_id` this channel used to be known by during channel establishment.
///
/// Will be `None` for channels created prior to LDK version 0.0.115.
former_temporary_channel_id: Option<[u8; 32]>,
/// The `node_id` of the channel counterparty.
counterparty_node_id: PublicKey,
/// The outpoint of the channel's funding transaction.
funding_txo: OutPoint,
},
/// Used to indicate that a channel with the given `channel_id` is ready to
/// be used. This event is emitted either when the funding transaction has been confirmed
/// on-chain, or, in case of a 0conf channel, when both parties have confirmed the channel
/// establishment.
ChannelReady {
/// The channel_id of the channel that is ready.
/// The `channel_id` of the channel that is ready.
channel_id: [u8; 32],
/// The `user_channel_id` value passed in to [`ChannelManager::create_channel`] for outbound
/// channels, or to [`ChannelManager::accept_inbound_channel`] for inbound channels if
Expand All@@ -624,15 +653,15 @@ pub enum Event {
/// [`ChannelManager::accept_inbound_channel`]: crate::ln::channelmanager::ChannelManager::accept_inbound_channel
/// [`UserConfig::manually_accept_inbound_channels`]: crate::util::config::UserConfig::manually_accept_inbound_channels
user_channel_id: u128,
/// The node_id of the channel counterparty.
/// The `node_id` of the channel counterparty.
counterparty_node_id: PublicKey,
/// The features that this channel will operate with.
channel_type: ChannelTypeFeatures,
},
/// Used to indicate that a previously opened channel with the given `channel_id` is in the
/// process of closure.
ChannelClosed {
/// The channel_id of the channel which has been closed. Note that on-chain transactions
/// The `channel_id` of the channel which has been closed. Note that on-chain transactions
/// resolving the channel are likely still awaiting confirmation.
channel_id: [u8; 32],
/// The `user_channel_id` value passed in to [`ChannelManager::create_channel`] for outbound
Expand DownExpand Up@@ -931,6 +960,16 @@ impl Writeable for Event {
(6, channel_type, required),
});
},
&Event::ChannelPending { ref channel_id, ref user_channel_id, ref former_temporary_channel_id, ref counterparty_node_id, ref funding_txo } => {
31u8.write(writer)?;
write_tlv_fields!(writer, {
(0, channel_id, required),
(2, user_channel_id, required),
(4, former_temporary_channel_id, required),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: option is a bit more straightforward/saves a few bytes on the wire. It doesn't matter, though, just for future reference.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Yes, it's a tiny bit more efficient but to be honest I don't liking mixing implementation type and serialization type semantics if it can be avoided. In my mind option -> field is optional, not field is Option.

(6, counterparty_node_id, required),
(8, funding_txo, required),
});
},
// Note that, going forward, all new events must only write data inside of
// `write_tlv_fields`. Versions 0.0.101+ will ignore odd-numbered events that write
// data via `write_tlv_fields`.
Expand DownExpand Up@@ -1271,6 +1310,31 @@ impl MaybeReadable for Event {
};
f()
},
31u8 => {
let f = || {
let mut channel_id = [0; 32];
let mut user_channel_id: u128 = 0;
let mut former_temporary_channel_id = None;
let mut counterparty_node_id = RequiredWrapper(None);
let mut funding_txo = RequiredWrapper(None);
read_tlv_fields!(reader, {
(0, channel_id, required),
(2, user_channel_id, required),
(4, former_temporary_channel_id, required),
(6, counterparty_node_id, required),
(8, funding_txo, required),
});

Ok(Some(Event::ChannelPending {
channel_id,
user_channel_id,
former_temporary_channel_id,
counterparty_node_id: counterparty_node_id.0.unwrap(),
funding_txo: funding_txo.0.unwrap()
}))
};
f()
},
// Versions prior to 0.0.100 did not ignore odd types, instead returning InvalidValue.
// Version 0.0.100 failed to properly ignore odd types, possibly resulting in corrupt
// reads.
Expand Down
6 changes: 6 additions & 0 deletions lightning/src/ln/chanmon_update_fail_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1864,6 +1864,7 @@ fn do_during_funding_monitor_fail(confirm_a_first: bool, restore_b_before_conf:
let (outpoint, latest_update, _) = nodes[0].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
nodes[0].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
check_added_monitors!(nodes[0], 0);
expect_channel_pending_event(&nodes[0], &nodes[1].node.get_our_node_id());

let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 0);
Expand All@@ -1885,6 +1886,9 @@ fn do_during_funding_monitor_fail(confirm_a_first: bool, restore_b_before_conf:
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id());
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id());
reconnect_nodes(&nodes[0], &nodes[1], (false, confirm_a_first), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (false, false));

// But we want to re-emit ChannelPending
expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id());
assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());

Expand DownExpand Up@@ -2808,6 +2812,7 @@ fn do_test_outbound_reload_without_init_mon(use_0conf: bool) {
let 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);
expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id());

let bs_signed_locked = nodes[1].node.get_and_clear_pending_msg_events();
assert_eq!(bs_signed_locked.len(), if use_0conf { 2 } else { 1 });
Expand DownExpand Up@@ -2906,6 +2911,7 @@ fn do_test_inbound_reload_without_init_mon(use_0conf: bool, lock_commitment: boo

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());

let as_funding_tx = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
if lock_commitment {
Expand Down
43 changes: 42 additions & 1 deletion lightning/src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -499,6 +499,7 @@ pub(super) struct Channel<Signer: ChannelSigner> {
user_id: u128,

channel_id: [u8; 32],
temporary_channel_id: Option<[u8; 32]>, // Will be `None` for channels created prior to 0.0.115.
channel_state: u32,

// When we reach max(6 blocks, minimum_depth), we need to send an AnnouncementSigs message to
Expand DownExpand Up@@ -729,6 +730,9 @@ pub(super) struct Channel<Signer: ChannelSigner> {
// blinded paths instead of simple scid+node_id aliases.
outbound_scid_alias: u64,

// We track whether we already emitted a `ChannelPending` event.
channel_pending_event_emitted: bool,

// We track whether we already emitted a `ChannelReady` event.
channel_ready_event_emitted: bool,

Expand DownExpand Up@@ -991,6 +995,8 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
}
}

let temporary_channel_id = entropy_source.get_secure_random_bytes();

Ok(Channel {
user_id,

Expand All@@ -1004,7 +1010,8 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {

inbound_handshake_limits_override: Some(config.channel_handshake_limits.clone()),

channel_id: entropy_source.get_secure_random_bytes(),
channel_id: temporary_channel_id,
temporary_channel_id: Some(temporary_channel_id),
channel_state: ChannelState::OurInitSent as u32,
announcement_sigs_state: AnnouncementSigsState::NotSent,
secp_ctx,
Expand DownExpand Up@@ -1103,6 +1110,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
latest_inbound_scid_alias: None,
outbound_scid_alias,

channel_pending_event_emitted: false,
channel_ready_event_emitted: false,

#[cfg(any(test, fuzzing))]
Expand DownExpand Up@@ -1350,6 +1358,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
inbound_handshake_limits_override: None,

channel_id: msg.temporary_channel_id,
temporary_channel_id: Some(msg.temporary_channel_id),
channel_state: (ChannelState::OurInitSent as u32) | (ChannelState::TheirInitSent as u32),
announcement_sigs_state: AnnouncementSigsState::NotSent,
secp_ctx,
Expand DownExpand Up@@ -1451,6 +1460,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
latest_inbound_scid_alias: None,
outbound_scid_alias,

channel_pending_event_emitted: false,
channel_ready_event_emitted: false,

#[cfg(any(test, fuzzing))]
Expand DownExpand Up@@ -4550,6 +4560,13 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
self.channel_id
}

// Return the `temporary_channel_id` used during channel establishment.
//
// Will return `None` for channels created prior to LDK version 0.0.115.
pub fn temporary_channel_id(&self) -> Option<[u8; 32]> {
self.temporary_channel_id
}

pub fn minimum_depth(&self) -> Option<u32> {
self.minimum_depth
}
Expand DownExpand Up@@ -4694,6 +4711,21 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
self.prev_config.map(|prev_config| prev_config.0)
}

// Checks whether we should emit a `ChannelPending` event.
pub(crate) fn should_emit_channel_pending_event(&mut self) -> bool {
self.is_funding_initiated() && !self.channel_pending_event_emitted
}

// Returns whether we already emitted a `ChannelPending` event.
pub(crate) fn channel_pending_event_emitted(&self) -> bool {
self.channel_pending_event_emitted
}

// Remembers that we already emitted a `ChannelPending` event.
pub(crate) fn set_channel_pending_event_emitted(&mut self) {
self.channel_pending_event_emitted = true;
}

// Checks whether we should emit a `ChannelReady` event.
pub(crate) fn should_emit_channel_ready_event(&mut self) -> bool {
self.is_usable() && !self.channel_ready_event_emitted
Expand DownExpand Up@@ -6420,6 +6452,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Writeable for Channel<Signer> {
if self.holder_max_htlc_value_in_flight_msat != Self::get_holder_max_htlc_value_in_flight_msat(self.channel_value_satoshis, &old_max_in_flight_percent_config)
{ Some(self.holder_max_htlc_value_in_flight_msat) } else { None };

let channel_pending_event_emitted = Some(self.channel_pending_event_emitted);
let channel_ready_event_emitted = Some(self.channel_ready_event_emitted);

// `user_id` used to be a single u64 value. In order to remain backwards compatible with
Expand DownExpand Up@@ -6452,6 +6485,8 @@ impl<Signer: WriteableEcdsaChannelSigner> Writeable for Channel<Signer> {
(23, channel_ready_event_emitted, option),
(25, user_id_high_opt, option),
(27, self.channel_keys_id, required),
(29, self.temporary_channel_id, option),
(31, channel_pending_event_emitted, option),
});

Ok(())
Expand DownExpand Up@@ -6719,10 +6754,12 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
let mut announcement_sigs_state = Some(AnnouncementSigsState::NotSent);
let mut latest_inbound_scid_alias = None;
let mut outbound_scid_alias = None;
let mut channel_pending_event_emitted = None;
let mut channel_ready_event_emitted = None;

let mut user_id_high_opt: Option<u64> = None;
let mut channel_keys_id: Option<[u8; 32]> = None;
let mut temporary_channel_id: Option<[u8; 32]> = None;

read_tlv_fields!(reader, {
(0, announcement_sigs, option),
Expand All@@ -6743,6 +6780,8 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
(23, channel_ready_event_emitted, option),
(25, user_id_high_opt, option),
(27, channel_keys_id, option),
(29, temporary_channel_id, option),
(31, channel_pending_event_emitted, option),
});

let (channel_keys_id, holder_signer) = if let Some(channel_keys_id) = channel_keys_id {
Expand DownExpand Up@@ -6807,6 +6846,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
inbound_handshake_limits_override: None,

channel_id,
temporary_channel_id,
channel_state,
announcement_sigs_state: announcement_sigs_state.unwrap(),
secp_ctx,
Expand DownExpand Up@@ -6899,6 +6939,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
// Later in the ChannelManager deserialization phase we scan for channels and assign scid aliases if its missing
outbound_scid_alias: outbound_scid_alias.unwrap_or(0),

channel_pending_event_emitted: channel_pending_event_emitted.unwrap_or(true),
channel_ready_event_emitted: channel_ready_event_emitted.unwrap_or(true),

#[cfg(any(test, fuzzing))]
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
11 changes: 11 additions & 0 deletions fuzz/src/chanmon_consistency.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -526,7 +526,18 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out) {
msg.clone()
} else { panic!("Wrong event type"); }
};
let events = $dest.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
if let events::Event::ChannelPending{ ref counterparty_node_id, .. } = events[0] {
assert_eq!(counterparty_node_id, &$source.get_our_node_id());
} else { panic!("Wrong event type"); }

$source.handle_funding_signed(&$dest.get_our_node_id(), &funding_signed);
let events = $source.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
if let events::Event::ChannelPending{ ref counterparty_node_id, .. } = events[0] {
assert_eq!(counterparty_node_id, &$dest.get_our_node_id());
} else { panic!("Wrong event type"); }

funding_output
} }
Expand Down
5 changes: 4 additions & 1 deletion lightning-background-processor/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -710,7 +710,7 @@ mod tests {
use lightning::chain::keysinterface::{InMemorySigner, KeysManager};
use lightning::chain::transaction::OutPoint;
use lightning::events::{Event, PathFailure, MessageSendEventsProvider, MessageSendEvent};
use lightning::get_event_msg;
use lightning::{get_event_msg, get_event};
use lightning::ln::PaymentHash;
use lightning::ln::channelmanager;
use lightning::ln::channelmanager::{BREAKDOWN_TIMEOUT, ChainParameters, MIN_CLTV_EXPIRY_DELTA, PaymentId};
Expand DownExpand Up@@ -1058,7 +1058,10 @@ mod tests {
($node_a: expr, $node_b: expr, $temporary_channel_id: expr, $tx: expr) => {{
$node_a.node.funding_transaction_generated(&$temporary_channel_id, &$node_b.node.get_our_node_id(), $tx.clone()).unwrap();
$node_b.node.handle_funding_created(&$node_a.node.get_our_node_id(), &get_event_msg!($node_a, MessageSendEvent::SendFundingCreated, $node_b.node.get_our_node_id()));
get_event!($node_b, Event::ChannelPending);

$node_a.node.handle_funding_signed(&$node_b.node.get_our_node_id(), &get_event_msg!($node_b, MessageSendEvent::SendFundingSigned, $node_a.node.get_our_node_id()));
get_event!($node_a, Event::ChannelPending);
}}
}

Expand Down
72 changes: 68 additions & 4 deletions lightning/src/events/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -32,7 +32,9 @@ use crate::util::ser::{BigSize, FixedLengthReader, Writeable, Writer, MaybeReada
use crate::util::string::UntrustedString;
use crate::routing::router::{RouteHop, RouteParameters};

use bitcoin::{PackedLockTime, Transaction};
use bitcoin::{PackedLockTime, Transaction, OutPoint};
#[cfg(anchors)]
use bitcoin::{Txid, TxIn, TxOut, Witness};
use bitcoin::blockdata::script::Script;
use bitcoin::hashes::Hash;
use bitcoin::hashes::sha256::Hash as Sha256;
Expand DownExpand Up@@ -608,12 +610,39 @@ pub enum Event {
/// The caveat described above the `fee_earned_msat` field applies here as well.
outbound_amount_forwarded_msat: Option<u64>,
},
/// Used to indicate that a channel with the given `channel_id` is being opened and pending
/// confirmation on-chain.
///
/// This event is emitted when the funding transaction has been signed and is broadcast to the

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should we have a note that, in rare cases, this event may be missed if a channel is ultimately confirmed quickly prior to the event being processed and then we restart? Its a stupid rare case but its possible I think?

@tnulltnullMar 30, 2023

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I'm confused, which scenario are you describing exactly? Wouldn't rather be an edge case that we emit the event twice when we decide to rebroadcast on restart?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think we can: (a) fund the channel, with an async monitor update, (b) not have that complete, but persist the manager, then (c) complete the monitor update and broadcast the funding tx (d) then restart, replay the chain and see the funding tx confirm, then (e) re-complete the monitor update completed in c, then we wont get the event again but the user will never have used it.

I think with "full" async monitor updates this becomes more realistic.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we have a note that, in rare cases, this event may be missed if a channel is ultimately confirmed quickly prior to the event being processed and then we restart?

Since we plan to leverage it for #2137, seems we should always regenerate it in cases like this? The channel will actually be ready, but as long as it's queued before the ChannelReady event, it should be fine.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yea, that's my biggest concern here.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I think we can: (a) fund the channel, with an async monitor update, (b) not have that complete, but persist the manager, then (c) complete the monitor update and broadcast the funding tx (d) then restart, replay the chain and see the funding tx confirm, then (e) re-complete the monitor update completed in c, then we wont get the event again but the user will never have used it.

Ugh., I see. In that scenario it would persist the event queue when the event hasn't been emitted yet and wouldn't re-emit it after restart. Mh, instead of leaving a note I went ahead and am now tracking the emission of ChannelPending in the same way as ChannelReady and removed it from internal_funding_created entirely.
This means that we're now only emitting it after CMU completion or reestablish, i.e., would re-emit it if the "emitted" state wasn't persisted before.

/// network. For 0conf channels it will be immediately followed by the corresponding
/// [`Event::ChannelReady`] event.
ChannelPending {
/// The `channel_id` of the channel that is pending confirmation.
channel_id: [u8; 32],
/// The `user_channel_id` value passed in to [`ChannelManager::create_channel`] for outbound
/// channels, or to [`ChannelManager::accept_inbound_channel`] for inbound channels if
/// [`UserConfig::manually_accept_inbound_channels`] config flag is set to true. Otherwise
/// `user_channel_id` will be randomized for an inbound channel.
///
/// [`ChannelManager::create_channel`]: crate::ln::channelmanager::ChannelManager::create_channel
/// [`ChannelManager::accept_inbound_channel`]: crate::ln::channelmanager::ChannelManager::accept_inbound_channel
/// [`UserConfig::manually_accept_inbound_channels`]: crate::util::config::UserConfig::manually_accept_inbound_channels
user_channel_id: u128,
/// The `temporary_channel_id` this channel used to be known by during channel establishment.
///
/// Will be `None` for channels created prior to LDK version 0.0.115.
former_temporary_channel_id: Option<[u8; 32]>,
/// The `node_id` of the channel counterparty.
counterparty_node_id: PublicKey,
/// The outpoint of the channel's funding transaction.
funding_txo: OutPoint,
},
/// Used to indicate that a channel with the given `channel_id` is ready to
/// be used. This event is emitted either when the funding transaction has been confirmed
/// on-chain, or, in case of a 0conf channel, when both parties have confirmed the channel
/// establishment.
ChannelReady {
/// The channel_id of the channel that is ready.
/// The `channel_id` of the channel that is ready.
channel_id: [u8; 32],
/// The `user_channel_id` value passed in to [`ChannelManager::create_channel`] for outbound
/// channels, or to [`ChannelManager::accept_inbound_channel`] for inbound channels if
Expand All@@ -624,15 +653,15 @@ pub enum Event {
/// [`ChannelManager::accept_inbound_channel`]: crate::ln::channelmanager::ChannelManager::accept_inbound_channel
/// [`UserConfig::manually_accept_inbound_channels`]: crate::util::config::UserConfig::manually_accept_inbound_channels
user_channel_id: u128,
/// The node_id of the channel counterparty.
/// The `node_id` of the channel counterparty.
counterparty_node_id: PublicKey,
/// The features that this channel will operate with.
channel_type: ChannelTypeFeatures,
},
/// Used to indicate that a previously opened channel with the given `channel_id` is in the
/// process of closure.
ChannelClosed {
/// The channel_id of the channel which has been closed. Note that on-chain transactions
/// The `channel_id` of the channel which has been closed. Note that on-chain transactions
/// resolving the channel are likely still awaiting confirmation.
channel_id: [u8; 32],
/// The `user_channel_id` value passed in to [`ChannelManager::create_channel`] for outbound
Expand DownExpand Up@@ -931,6 +960,16 @@ impl Writeable for Event {
(6, channel_type, required),
});
},
&Event::ChannelPending { ref channel_id, ref user_channel_id, ref former_temporary_channel_id, ref counterparty_node_id, ref funding_txo } => {
31u8.write(writer)?;
write_tlv_fields!(writer, {
(0, channel_id, required),
(2, user_channel_id, required),
(4, former_temporary_channel_id, required),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: option is a bit more straightforward/saves a few bytes on the wire. It doesn't matter, though, just for future reference.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Yes, it's a tiny bit more efficient but to be honest I don't liking mixing implementation type and serialization type semantics if it can be avoided. In my mind option -> field is optional, not field is Option.

(6, counterparty_node_id, required),
(8, funding_txo, required),
});
},
// Note that, going forward, all new events must only write data inside of
// `write_tlv_fields`. Versions 0.0.101+ will ignore odd-numbered events that write
// data via `write_tlv_fields`.
Expand DownExpand Up@@ -1271,6 +1310,31 @@ impl MaybeReadable for Event {
};
f()
},
31u8 => {
let f = || {
let mut channel_id = [0; 32];
let mut user_channel_id: u128 = 0;
let mut former_temporary_channel_id = None;
let mut counterparty_node_id = RequiredWrapper(None);
let mut funding_txo = RequiredWrapper(None);
read_tlv_fields!(reader, {
(0, channel_id, required),
(2, user_channel_id, required),
(4, former_temporary_channel_id, required),
(6, counterparty_node_id, required),
(8, funding_txo, required),
});

Ok(Some(Event::ChannelPending {
channel_id,
user_channel_id,
former_temporary_channel_id,
counterparty_node_id: counterparty_node_id.0.unwrap(),
funding_txo: funding_txo.0.unwrap()
}))
};
f()
},
// Versions prior to 0.0.100 did not ignore odd types, instead returning InvalidValue.
// Version 0.0.100 failed to properly ignore odd types, possibly resulting in corrupt
// reads.
Expand Down
6 changes: 6 additions & 0 deletions lightning/src/ln/chanmon_update_fail_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1864,6 +1864,7 @@ fn do_during_funding_monitor_fail(confirm_a_first: bool, restore_b_before_conf:
let (outpoint, latest_update, _) = nodes[0].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
nodes[0].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
check_added_monitors!(nodes[0], 0);
expect_channel_pending_event(&nodes[0], &nodes[1].node.get_our_node_id());

let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 0);
Expand All@@ -1885,6 +1886,9 @@ fn do_during_funding_monitor_fail(confirm_a_first: bool, restore_b_before_conf:
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id());
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id());
reconnect_nodes(&nodes[0], &nodes[1], (false, confirm_a_first), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (false, false));

// But we want to re-emit ChannelPending
expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id());
assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());

Expand DownExpand Up@@ -2808,6 +2812,7 @@ fn do_test_outbound_reload_without_init_mon(use_0conf: bool) {
let 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);
expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id());

let bs_signed_locked = nodes[1].node.get_and_clear_pending_msg_events();
assert_eq!(bs_signed_locked.len(), if use_0conf { 2 } else { 1 });
Expand DownExpand Up@@ -2906,6 +2911,7 @@ fn do_test_inbound_reload_without_init_mon(use_0conf: bool, lock_commitment: boo

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());

let as_funding_tx = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
if lock_commitment {
Expand Down
43 changes: 42 additions & 1 deletion lightning/src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -499,6 +499,7 @@ pub(super) struct Channel<Signer: ChannelSigner> {
user_id: u128,

channel_id: [u8; 32],
temporary_channel_id: Option<[u8; 32]>, // Will be `None` for channels created prior to 0.0.115.
channel_state: u32,

// When we reach max(6 blocks, minimum_depth), we need to send an AnnouncementSigs message to
Expand DownExpand Up@@ -729,6 +730,9 @@ pub(super) struct Channel<Signer: ChannelSigner> {
// blinded paths instead of simple scid+node_id aliases.
outbound_scid_alias: u64,

// We track whether we already emitted a `ChannelPending` event.
channel_pending_event_emitted: bool,

// We track whether we already emitted a `ChannelReady` event.
channel_ready_event_emitted: bool,

Expand DownExpand Up@@ -991,6 +995,8 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
}
}

let temporary_channel_id = entropy_source.get_secure_random_bytes();

Ok(Channel {
user_id,

Expand All@@ -1004,7 +1010,8 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {

inbound_handshake_limits_override: Some(config.channel_handshake_limits.clone()),

channel_id: entropy_source.get_secure_random_bytes(),
channel_id: temporary_channel_id,
temporary_channel_id: Some(temporary_channel_id),
channel_state: ChannelState::OurInitSent as u32,
announcement_sigs_state: AnnouncementSigsState::NotSent,
secp_ctx,
Expand DownExpand Up@@ -1103,6 +1110,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
latest_inbound_scid_alias: None,
outbound_scid_alias,

channel_pending_event_emitted: false,
channel_ready_event_emitted: false,

#[cfg(any(test, fuzzing))]
Expand DownExpand Up@@ -1350,6 +1358,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
inbound_handshake_limits_override: None,

channel_id: msg.temporary_channel_id,
temporary_channel_id: Some(msg.temporary_channel_id),
channel_state: (ChannelState::OurInitSent as u32) | (ChannelState::TheirInitSent as u32),
announcement_sigs_state: AnnouncementSigsState::NotSent,
secp_ctx,
Expand DownExpand Up@@ -1451,6 +1460,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
latest_inbound_scid_alias: None,
outbound_scid_alias,

channel_pending_event_emitted: false,
channel_ready_event_emitted: false,

#[cfg(any(test, fuzzing))]
Expand DownExpand Up@@ -4550,6 +4560,13 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
self.channel_id
}

// Return the `temporary_channel_id` used during channel establishment.
//
// Will return `None` for channels created prior to LDK version 0.0.115.
pub fn temporary_channel_id(&self) -> Option<[u8; 32]> {
self.temporary_channel_id
}

pub fn minimum_depth(&self) -> Option<u32> {
self.minimum_depth
}
Expand DownExpand Up@@ -4694,6 +4711,21 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
self.prev_config.map(|prev_config| prev_config.0)
}

// Checks whether we should emit a `ChannelPending` event.
pub(crate) fn should_emit_channel_pending_event(&mut self) -> bool {
self.is_funding_initiated() && !self.channel_pending_event_emitted
}

// Returns whether we already emitted a `ChannelPending` event.
pub(crate) fn channel_pending_event_emitted(&self) -> bool {
self.channel_pending_event_emitted
}

// Remembers that we already emitted a `ChannelPending` event.
pub(crate) fn set_channel_pending_event_emitted(&mut self) {
self.channel_pending_event_emitted = true;
}

// Checks whether we should emit a `ChannelReady` event.
pub(crate) fn should_emit_channel_ready_event(&mut self) -> bool {
self.is_usable() && !self.channel_ready_event_emitted
Expand DownExpand Up@@ -6420,6 +6452,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Writeable for Channel<Signer> {
if self.holder_max_htlc_value_in_flight_msat != Self::get_holder_max_htlc_value_in_flight_msat(self.channel_value_satoshis, &old_max_in_flight_percent_config)
{ Some(self.holder_max_htlc_value_in_flight_msat) } else { None };

let channel_pending_event_emitted = Some(self.channel_pending_event_emitted);
let channel_ready_event_emitted = Some(self.channel_ready_event_emitted);

// `user_id` used to be a single u64 value. In order to remain backwards compatible with
Expand DownExpand Up@@ -6452,6 +6485,8 @@ impl<Signer: WriteableEcdsaChannelSigner> Writeable for Channel<Signer> {
(23, channel_ready_event_emitted, option),
(25, user_id_high_opt, option),
(27, self.channel_keys_id, required),
(29, self.temporary_channel_id, option),
(31, channel_pending_event_emitted, option),
});

Ok(())
Expand DownExpand Up@@ -6719,10 +6754,12 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
let mut announcement_sigs_state = Some(AnnouncementSigsState::NotSent);
let mut latest_inbound_scid_alias = None;
let mut outbound_scid_alias = None;
let mut channel_pending_event_emitted = None;
let mut channel_ready_event_emitted = None;

let mut user_id_high_opt: Option<u64> = None;
let mut channel_keys_id: Option<[u8; 32]> = None;
let mut temporary_channel_id: Option<[u8; 32]> = None;

read_tlv_fields!(reader, {
(0, announcement_sigs, option),
Expand All@@ -6743,6 +6780,8 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
(23, channel_ready_event_emitted, option),
(25, user_id_high_opt, option),
(27, channel_keys_id, option),
(29, temporary_channel_id, option),
(31, channel_pending_event_emitted, option),
});

let (channel_keys_id, holder_signer) = if let Some(channel_keys_id) = channel_keys_id {
Expand DownExpand Up@@ -6807,6 +6846,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
inbound_handshake_limits_override: None,

channel_id,
temporary_channel_id,
channel_state,
announcement_sigs_state: announcement_sigs_state.unwrap(),
secp_ctx,
Expand DownExpand Up@@ -6899,6 +6939,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
// Later in the ChannelManager deserialization phase we scan for channels and assign scid aliases if its missing
outbound_scid_alias: outbound_scid_alias.unwrap_or(0),

channel_pending_event_emitted: channel_pending_event_emitted.unwrap_or(true),
channel_ready_event_emitted: channel_ready_event_emitted.unwrap_or(true),

#[cfg(any(test, fuzzing))]
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
11 changes: 11 additions & 0 deletions fuzz/src/chanmon_consistency.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -526,7 +526,18 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out) {
msg.clone()
} else { panic!("Wrong event type"); }
};
let events = $dest.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
if let events::Event::ChannelPending{ ref counterparty_node_id, .. } = events[0] {
assert_eq!(counterparty_node_id, &$source.get_our_node_id());
} else { panic!("Wrong event type"); }

$source.handle_funding_signed(&$dest.get_our_node_id(), &funding_signed);
let events = $source.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
if let events::Event::ChannelPending{ ref counterparty_node_id, .. } = events[0] {
assert_eq!(counterparty_node_id, &$dest.get_our_node_id());
} else { panic!("Wrong event type"); }

funding_output
} }
Expand Down
5 changes: 4 additions & 1 deletion lightning-background-processor/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -710,7 +710,7 @@ mod tests {
use lightning::chain::keysinterface::{InMemorySigner, KeysManager};
use lightning::chain::transaction::OutPoint;
use lightning::events::{Event, PathFailure, MessageSendEventsProvider, MessageSendEvent};
use lightning::get_event_msg;
use lightning::{get_event_msg, get_event};
use lightning::ln::PaymentHash;
use lightning::ln::channelmanager;
use lightning::ln::channelmanager::{BREAKDOWN_TIMEOUT, ChainParameters, MIN_CLTV_EXPIRY_DELTA, PaymentId};
Expand DownExpand Up@@ -1058,7 +1058,10 @@ mod tests {
($node_a: expr, $node_b: expr, $temporary_channel_id: expr, $tx: expr) => {{
$node_a.node.funding_transaction_generated(&$temporary_channel_id, &$node_b.node.get_our_node_id(), $tx.clone()).unwrap();
$node_b.node.handle_funding_created(&$node_a.node.get_our_node_id(), &get_event_msg!($node_a, MessageSendEvent::SendFundingCreated, $node_b.node.get_our_node_id()));
get_event!($node_b, Event::ChannelPending);

$node_a.node.handle_funding_signed(&$node_b.node.get_our_node_id(), &get_event_msg!($node_b, MessageSendEvent::SendFundingSigned, $node_a.node.get_our_node_id()));
get_event!($node_a, Event::ChannelPending);
}}
}

Expand Down
72 changes: 68 additions & 4 deletions lightning/src/events/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -32,7 +32,9 @@ use crate::util::ser::{BigSize, FixedLengthReader, Writeable, Writer, MaybeReada
use crate::util::string::UntrustedString;
use crate::routing::router::{RouteHop, RouteParameters};

use bitcoin::{PackedLockTime, Transaction};
use bitcoin::{PackedLockTime, Transaction, OutPoint};
#[cfg(anchors)]
use bitcoin::{Txid, TxIn, TxOut, Witness};
use bitcoin::blockdata::script::Script;
use bitcoin::hashes::Hash;
use bitcoin::hashes::sha256::Hash as Sha256;
Expand DownExpand Up@@ -608,12 +610,39 @@ pub enum Event {
/// The caveat described above the `fee_earned_msat` field applies here as well.
outbound_amount_forwarded_msat: Option<u64>,
},
/// Used to indicate that a channel with the given `channel_id` is being opened and pending
/// confirmation on-chain.
///
/// This event is emitted when the funding transaction has been signed and is broadcast to the

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should we have a note that, in rare cases, this event may be missed if a channel is ultimately confirmed quickly prior to the event being processed and then we restart? Its a stupid rare case but its possible I think?

@tnulltnullMar 30, 2023

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I'm confused, which scenario are you describing exactly? Wouldn't rather be an edge case that we emit the event twice when we decide to rebroadcast on restart?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think we can: (a) fund the channel, with an async monitor update, (b) not have that complete, but persist the manager, then (c) complete the monitor update and broadcast the funding tx (d) then restart, replay the chain and see the funding tx confirm, then (e) re-complete the monitor update completed in c, then we wont get the event again but the user will never have used it.

I think with "full" async monitor updates this becomes more realistic.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we have a note that, in rare cases, this event may be missed if a channel is ultimately confirmed quickly prior to the event being processed and then we restart?

Since we plan to leverage it for #2137, seems we should always regenerate it in cases like this? The channel will actually be ready, but as long as it's queued before the ChannelReady event, it should be fine.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yea, that's my biggest concern here.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I think we can: (a) fund the channel, with an async monitor update, (b) not have that complete, but persist the manager, then (c) complete the monitor update and broadcast the funding tx (d) then restart, replay the chain and see the funding tx confirm, then (e) re-complete the monitor update completed in c, then we wont get the event again but the user will never have used it.

Ugh., I see. In that scenario it would persist the event queue when the event hasn't been emitted yet and wouldn't re-emit it after restart. Mh, instead of leaving a note I went ahead and am now tracking the emission of ChannelPending in the same way as ChannelReady and removed it from internal_funding_created entirely.
This means that we're now only emitting it after CMU completion or reestablish, i.e., would re-emit it if the "emitted" state wasn't persisted before.

/// network. For 0conf channels it will be immediately followed by the corresponding
/// [`Event::ChannelReady`] event.
ChannelPending {
/// The `channel_id` of the channel that is pending confirmation.
channel_id: [u8; 32],
/// The `user_channel_id` value passed in to [`ChannelManager::create_channel`] for outbound
/// channels, or to [`ChannelManager::accept_inbound_channel`] for inbound channels if
/// [`UserConfig::manually_accept_inbound_channels`] config flag is set to true. Otherwise
/// `user_channel_id` will be randomized for an inbound channel.
///
/// [`ChannelManager::create_channel`]: crate::ln::channelmanager::ChannelManager::create_channel
/// [`ChannelManager::accept_inbound_channel`]: crate::ln::channelmanager::ChannelManager::accept_inbound_channel
/// [`UserConfig::manually_accept_inbound_channels`]: crate::util::config::UserConfig::manually_accept_inbound_channels
user_channel_id: u128,
/// The `temporary_channel_id` this channel used to be known by during channel establishment.
///
/// Will be `None` for channels created prior to LDK version 0.0.115.
former_temporary_channel_id: Option<[u8; 32]>,
/// The `node_id` of the channel counterparty.
counterparty_node_id: PublicKey,
/// The outpoint of the channel's funding transaction.
funding_txo: OutPoint,
},
/// Used to indicate that a channel with the given `channel_id` is ready to
/// be used. This event is emitted either when the funding transaction has been confirmed
/// on-chain, or, in case of a 0conf channel, when both parties have confirmed the channel
/// establishment.
ChannelReady {
/// The channel_id of the channel that is ready.
/// The `channel_id` of the channel that is ready.
channel_id: [u8; 32],
/// The `user_channel_id` value passed in to [`ChannelManager::create_channel`] for outbound
/// channels, or to [`ChannelManager::accept_inbound_channel`] for inbound channels if
Expand All@@ -624,15 +653,15 @@ pub enum Event {
/// [`ChannelManager::accept_inbound_channel`]: crate::ln::channelmanager::ChannelManager::accept_inbound_channel
/// [`UserConfig::manually_accept_inbound_channels`]: crate::util::config::UserConfig::manually_accept_inbound_channels
user_channel_id: u128,
/// The node_id of the channel counterparty.
/// The `node_id` of the channel counterparty.
counterparty_node_id: PublicKey,
/// The features that this channel will operate with.
channel_type: ChannelTypeFeatures,
},
/// Used to indicate that a previously opened channel with the given `channel_id` is in the
/// process of closure.
ChannelClosed {
/// The channel_id of the channel which has been closed. Note that on-chain transactions
/// The `channel_id` of the channel which has been closed. Note that on-chain transactions
/// resolving the channel are likely still awaiting confirmation.
channel_id: [u8; 32],
/// The `user_channel_id` value passed in to [`ChannelManager::create_channel`] for outbound
Expand DownExpand Up@@ -931,6 +960,16 @@ impl Writeable for Event {
(6, channel_type, required),
});
},
&Event::ChannelPending { ref channel_id, ref user_channel_id, ref former_temporary_channel_id, ref counterparty_node_id, ref funding_txo } => {
31u8.write(writer)?;
write_tlv_fields!(writer, {
(0, channel_id, required),
(2, user_channel_id, required),
(4, former_temporary_channel_id, required),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: option is a bit more straightforward/saves a few bytes on the wire. It doesn't matter, though, just for future reference.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Yes, it's a tiny bit more efficient but to be honest I don't liking mixing implementation type and serialization type semantics if it can be avoided. In my mind option -> field is optional, not field is Option.

(6, counterparty_node_id, required),
(8, funding_txo, required),
});
},
// Note that, going forward, all new events must only write data inside of
// `write_tlv_fields`. Versions 0.0.101+ will ignore odd-numbered events that write
// data via `write_tlv_fields`.
Expand DownExpand Up@@ -1271,6 +1310,31 @@ impl MaybeReadable for Event {
};
f()
},
31u8 => {
let f = || {
let mut channel_id = [0; 32];
let mut user_channel_id: u128 = 0;
let mut former_temporary_channel_id = None;
let mut counterparty_node_id = RequiredWrapper(None);
let mut funding_txo = RequiredWrapper(None);
read_tlv_fields!(reader, {
(0, channel_id, required),
(2, user_channel_id, required),
(4, former_temporary_channel_id, required),
(6, counterparty_node_id, required),
(8, funding_txo, required),
});

Ok(Some(Event::ChannelPending {
channel_id,
user_channel_id,
former_temporary_channel_id,
counterparty_node_id: counterparty_node_id.0.unwrap(),
funding_txo: funding_txo.0.unwrap()
}))
};
f()
},
// Versions prior to 0.0.100 did not ignore odd types, instead returning InvalidValue.
// Version 0.0.100 failed to properly ignore odd types, possibly resulting in corrupt
// reads.
Expand Down
6 changes: 6 additions & 0 deletions lightning/src/ln/chanmon_update_fail_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1864,6 +1864,7 @@ fn do_during_funding_monitor_fail(confirm_a_first: bool, restore_b_before_conf:
let (outpoint, latest_update, _) = nodes[0].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
nodes[0].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
check_added_monitors!(nodes[0], 0);
expect_channel_pending_event(&nodes[0], &nodes[1].node.get_our_node_id());

let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 0);
Expand All@@ -1885,6 +1886,9 @@ fn do_during_funding_monitor_fail(confirm_a_first: bool, restore_b_before_conf:
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id());
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id());
reconnect_nodes(&nodes[0], &nodes[1], (false, confirm_a_first), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (false, false));

// But we want to re-emit ChannelPending
expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id());
assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());

Expand DownExpand Up@@ -2808,6 +2812,7 @@ fn do_test_outbound_reload_without_init_mon(use_0conf: bool) {
let 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);
expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id());

let bs_signed_locked = nodes[1].node.get_and_clear_pending_msg_events();
assert_eq!(bs_signed_locked.len(), if use_0conf { 2 } else { 1 });
Expand DownExpand Up@@ -2906,6 +2911,7 @@ fn do_test_inbound_reload_without_init_mon(use_0conf: bool, lock_commitment: boo

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());

let as_funding_tx = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
if lock_commitment {
Expand Down
43 changes: 42 additions & 1 deletion lightning/src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -499,6 +499,7 @@ pub(super) struct Channel<Signer: ChannelSigner> {
user_id: u128,

channel_id: [u8; 32],
temporary_channel_id: Option<[u8; 32]>, // Will be `None` for channels created prior to 0.0.115.
channel_state: u32,

// When we reach max(6 blocks, minimum_depth), we need to send an AnnouncementSigs message to
Expand DownExpand Up@@ -729,6 +730,9 @@ pub(super) struct Channel<Signer: ChannelSigner> {
// blinded paths instead of simple scid+node_id aliases.
outbound_scid_alias: u64,

// We track whether we already emitted a `ChannelPending` event.
channel_pending_event_emitted: bool,

// We track whether we already emitted a `ChannelReady` event.
channel_ready_event_emitted: bool,

Expand DownExpand Up@@ -991,6 +995,8 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
}
}

let temporary_channel_id = entropy_source.get_secure_random_bytes();

Ok(Channel {
user_id,

Expand All@@ -1004,7 +1010,8 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {

inbound_handshake_limits_override: Some(config.channel_handshake_limits.clone()),

channel_id: entropy_source.get_secure_random_bytes(),
channel_id: temporary_channel_id,
temporary_channel_id: Some(temporary_channel_id),
channel_state: ChannelState::OurInitSent as u32,
announcement_sigs_state: AnnouncementSigsState::NotSent,
secp_ctx,
Expand DownExpand Up@@ -1103,6 +1110,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
latest_inbound_scid_alias: None,
outbound_scid_alias,

channel_pending_event_emitted: false,
channel_ready_event_emitted: false,

#[cfg(any(test, fuzzing))]
Expand DownExpand Up@@ -1350,6 +1358,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
inbound_handshake_limits_override: None,

channel_id: msg.temporary_channel_id,
temporary_channel_id: Some(msg.temporary_channel_id),
channel_state: (ChannelState::OurInitSent as u32) | (ChannelState::TheirInitSent as u32),
announcement_sigs_state: AnnouncementSigsState::NotSent,
secp_ctx,
Expand DownExpand Up@@ -1451,6 +1460,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
latest_inbound_scid_alias: None,
outbound_scid_alias,

channel_pending_event_emitted: false,
channel_ready_event_emitted: false,

#[cfg(any(test, fuzzing))]
Expand DownExpand Up@@ -4550,6 +4560,13 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
self.channel_id
}

// Return the `temporary_channel_id` used during channel establishment.
//
// Will return `None` for channels created prior to LDK version 0.0.115.
pub fn temporary_channel_id(&self) -> Option<[u8; 32]> {
self.temporary_channel_id
}

pub fn minimum_depth(&self) -> Option<u32> {
self.minimum_depth
}
Expand DownExpand Up@@ -4694,6 +4711,21 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
self.prev_config.map(|prev_config| prev_config.0)
}

// Checks whether we should emit a `ChannelPending` event.
pub(crate) fn should_emit_channel_pending_event(&mut self) -> bool {
self.is_funding_initiated() && !self.channel_pending_event_emitted
}

// Returns whether we already emitted a `ChannelPending` event.
pub(crate) fn channel_pending_event_emitted(&self) -> bool {
self.channel_pending_event_emitted
}

// Remembers that we already emitted a `ChannelPending` event.
pub(crate) fn set_channel_pending_event_emitted(&mut self) {
self.channel_pending_event_emitted = true;
}

// Checks whether we should emit a `ChannelReady` event.
pub(crate) fn should_emit_channel_ready_event(&mut self) -> bool {
self.is_usable() && !self.channel_ready_event_emitted
Expand DownExpand Up@@ -6420,6 +6452,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Writeable for Channel<Signer> {
if self.holder_max_htlc_value_in_flight_msat != Self::get_holder_max_htlc_value_in_flight_msat(self.channel_value_satoshis, &old_max_in_flight_percent_config)
{ Some(self.holder_max_htlc_value_in_flight_msat) } else { None };

let channel_pending_event_emitted = Some(self.channel_pending_event_emitted);
let channel_ready_event_emitted = Some(self.channel_ready_event_emitted);

// `user_id` used to be a single u64 value. In order to remain backwards compatible with
Expand DownExpand Up@@ -6452,6 +6485,8 @@ impl<Signer: WriteableEcdsaChannelSigner> Writeable for Channel<Signer> {
(23, channel_ready_event_emitted, option),
(25, user_id_high_opt, option),
(27, self.channel_keys_id, required),
(29, self.temporary_channel_id, option),
(31, channel_pending_event_emitted, option),
});

Ok(())
Expand DownExpand Up@@ -6719,10 +6754,12 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
let mut announcement_sigs_state = Some(AnnouncementSigsState::NotSent);
let mut latest_inbound_scid_alias = None;
let mut outbound_scid_alias = None;
let mut channel_pending_event_emitted = None;
let mut channel_ready_event_emitted = None;

let mut user_id_high_opt: Option<u64> = None;
let mut channel_keys_id: Option<[u8; 32]> = None;
let mut temporary_channel_id: Option<[u8; 32]> = None;

read_tlv_fields!(reader, {
(0, announcement_sigs, option),
Expand All@@ -6743,6 +6780,8 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
(23, channel_ready_event_emitted, option),
(25, user_id_high_opt, option),
(27, channel_keys_id, option),
(29, temporary_channel_id, option),
(31, channel_pending_event_emitted, option),
});

let (channel_keys_id, holder_signer) = if let Some(channel_keys_id) = channel_keys_id {
Expand DownExpand Up@@ -6807,6 +6846,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
inbound_handshake_limits_override: None,

channel_id,
temporary_channel_id,
channel_state,
announcement_sigs_state: announcement_sigs_state.unwrap(),
secp_ctx,
Expand DownExpand Up@@ -6899,6 +6939,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
// Later in the ChannelManager deserialization phase we scan for channels and assign scid aliases if its missing
outbound_scid_alias: outbound_scid_alias.unwrap_or(0),

channel_pending_event_emitted: channel_pending_event_emitted.unwrap_or(true),
channel_ready_event_emitted: channel_ready_event_emitted.unwrap_or(true),

#[cfg(any(test, fuzzing))]
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
11 changes: 11 additions & 0 deletions fuzz/src/chanmon_consistency.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -526,7 +526,18 @@ pub fn do_test<Out: Output>(data: &[u8], underlying_out: Out) {
msg.clone()
} else { panic!("Wrong event type"); }
};
let events = $dest.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
if let events::Event::ChannelPending{ ref counterparty_node_id, .. } = events[0] {
assert_eq!(counterparty_node_id, &$source.get_our_node_id());
} else { panic!("Wrong event type"); }

$source.handle_funding_signed(&$dest.get_our_node_id(), &funding_signed);
let events = $source.get_and_clear_pending_events();
assert_eq!(events.len(), 1);
if let events::Event::ChannelPending{ ref counterparty_node_id, .. } = events[0] {
assert_eq!(counterparty_node_id, &$dest.get_our_node_id());
} else { panic!("Wrong event type"); }

funding_output
} }
Expand Down
5 changes: 4 additions & 1 deletion lightning-background-processor/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -710,7 +710,7 @@ mod tests {
use lightning::chain::keysinterface::{InMemorySigner, KeysManager};
use lightning::chain::transaction::OutPoint;
use lightning::events::{Event, PathFailure, MessageSendEventsProvider, MessageSendEvent};
use lightning::get_event_msg;
use lightning::{get_event_msg, get_event};
use lightning::ln::PaymentHash;
use lightning::ln::channelmanager;
use lightning::ln::channelmanager::{BREAKDOWN_TIMEOUT, ChainParameters, MIN_CLTV_EXPIRY_DELTA, PaymentId};
Expand DownExpand Up@@ -1058,7 +1058,10 @@ mod tests {
($node_a: expr, $node_b: expr, $temporary_channel_id: expr, $tx: expr) => {{
$node_a.node.funding_transaction_generated(&$temporary_channel_id, &$node_b.node.get_our_node_id(), $tx.clone()).unwrap();
$node_b.node.handle_funding_created(&$node_a.node.get_our_node_id(), &get_event_msg!($node_a, MessageSendEvent::SendFundingCreated, $node_b.node.get_our_node_id()));
get_event!($node_b, Event::ChannelPending);

$node_a.node.handle_funding_signed(&$node_b.node.get_our_node_id(), &get_event_msg!($node_b, MessageSendEvent::SendFundingSigned, $node_a.node.get_our_node_id()));
get_event!($node_a, Event::ChannelPending);
}}
}

Expand Down
72 changes: 68 additions & 4 deletions lightning/src/events/mod.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -32,7 +32,9 @@ use crate::util::ser::{BigSize, FixedLengthReader, Writeable, Writer, MaybeReada
use crate::util::string::UntrustedString;
use crate::routing::router::{RouteHop, RouteParameters};

use bitcoin::{PackedLockTime, Transaction};
use bitcoin::{PackedLockTime, Transaction, OutPoint};
#[cfg(anchors)]
use bitcoin::{Txid, TxIn, TxOut, Witness};
use bitcoin::blockdata::script::Script;
use bitcoin::hashes::Hash;
use bitcoin::hashes::sha256::Hash as Sha256;
Expand DownExpand Up@@ -608,12 +610,39 @@ pub enum Event {
/// The caveat described above the `fee_earned_msat` field applies here as well.
outbound_amount_forwarded_msat: Option<u64>,
},
/// Used to indicate that a channel with the given `channel_id` is being opened and pending
/// confirmation on-chain.
///
/// This event is emitted when the funding transaction has been signed and is broadcast to the

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should we have a note that, in rare cases, this event may be missed if a channel is ultimately confirmed quickly prior to the event being processed and then we restart? Its a stupid rare case but its possible I think?

@tnulltnullMar 30, 2023

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I'm confused, which scenario are you describing exactly? Wouldn't rather be an edge case that we emit the event twice when we decide to rebroadcast on restart?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I think we can: (a) fund the channel, with an async monitor update, (b) not have that complete, but persist the manager, then (c) complete the monitor update and broadcast the funding tx (d) then restart, replay the chain and see the funding tx confirm, then (e) re-complete the monitor update completed in c, then we wont get the event again but the user will never have used it.

I think with "full" async monitor updates this becomes more realistic.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should we have a note that, in rare cases, this event may be missed if a channel is ultimately confirmed quickly prior to the event being processed and then we restart?

Since we plan to leverage it for #2137, seems we should always regenerate it in cases like this? The channel will actually be ready, but as long as it's queued before the ChannelReady event, it should be fine.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Yea, that's my biggest concern here.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

I think we can: (a) fund the channel, with an async monitor update, (b) not have that complete, but persist the manager, then (c) complete the monitor update and broadcast the funding tx (d) then restart, replay the chain and see the funding tx confirm, then (e) re-complete the monitor update completed in c, then we wont get the event again but the user will never have used it.

Ugh., I see. In that scenario it would persist the event queue when the event hasn't been emitted yet and wouldn't re-emit it after restart. Mh, instead of leaving a note I went ahead and am now tracking the emission of ChannelPending in the same way as ChannelReady and removed it from internal_funding_created entirely.
This means that we're now only emitting it after CMU completion or reestablish, i.e., would re-emit it if the "emitted" state wasn't persisted before.

/// network. For 0conf channels it will be immediately followed by the corresponding
/// [`Event::ChannelReady`] event.
ChannelPending {
/// The `channel_id` of the channel that is pending confirmation.
channel_id: [u8; 32],
/// The `user_channel_id` value passed in to [`ChannelManager::create_channel`] for outbound
/// channels, or to [`ChannelManager::accept_inbound_channel`] for inbound channels if
/// [`UserConfig::manually_accept_inbound_channels`] config flag is set to true. Otherwise
/// `user_channel_id` will be randomized for an inbound channel.
///
/// [`ChannelManager::create_channel`]: crate::ln::channelmanager::ChannelManager::create_channel
/// [`ChannelManager::accept_inbound_channel`]: crate::ln::channelmanager::ChannelManager::accept_inbound_channel
/// [`UserConfig::manually_accept_inbound_channels`]: crate::util::config::UserConfig::manually_accept_inbound_channels
user_channel_id: u128,
/// The `temporary_channel_id` this channel used to be known by during channel establishment.
///
/// Will be `None` for channels created prior to LDK version 0.0.115.
former_temporary_channel_id: Option<[u8; 32]>,
/// The `node_id` of the channel counterparty.
counterparty_node_id: PublicKey,
/// The outpoint of the channel's funding transaction.
funding_txo: OutPoint,
},
/// Used to indicate that a channel with the given `channel_id` is ready to
/// be used. This event is emitted either when the funding transaction has been confirmed
/// on-chain, or, in case of a 0conf channel, when both parties have confirmed the channel
/// establishment.
ChannelReady {
/// The channel_id of the channel that is ready.
/// The `channel_id` of the channel that is ready.
channel_id: [u8; 32],
/// The `user_channel_id` value passed in to [`ChannelManager::create_channel`] for outbound
/// channels, or to [`ChannelManager::accept_inbound_channel`] for inbound channels if
Expand All@@ -624,15 +653,15 @@ pub enum Event {
/// [`ChannelManager::accept_inbound_channel`]: crate::ln::channelmanager::ChannelManager::accept_inbound_channel
/// [`UserConfig::manually_accept_inbound_channels`]: crate::util::config::UserConfig::manually_accept_inbound_channels
user_channel_id: u128,
/// The node_id of the channel counterparty.
/// The `node_id` of the channel counterparty.
counterparty_node_id: PublicKey,
/// The features that this channel will operate with.
channel_type: ChannelTypeFeatures,
},
/// Used to indicate that a previously opened channel with the given `channel_id` is in the
/// process of closure.
ChannelClosed {
/// The channel_id of the channel which has been closed. Note that on-chain transactions
/// The `channel_id` of the channel which has been closed. Note that on-chain transactions
/// resolving the channel are likely still awaiting confirmation.
channel_id: [u8; 32],
/// The `user_channel_id` value passed in to [`ChannelManager::create_channel`] for outbound
Expand DownExpand Up@@ -931,6 +960,16 @@ impl Writeable for Event {
(6, channel_type, required),
});
},
&Event::ChannelPending { ref channel_id, ref user_channel_id, ref former_temporary_channel_id, ref counterparty_node_id, ref funding_txo } => {
31u8.write(writer)?;
write_tlv_fields!(writer, {
(0, channel_id, required),
(2, user_channel_id, required),
(4, former_temporary_channel_id, required),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: option is a bit more straightforward/saves a few bytes on the wire. It doesn't matter, though, just for future reference.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Yes, it's a tiny bit more efficient but to be honest I don't liking mixing implementation type and serialization type semantics if it can be avoided. In my mind option -> field is optional, not field is Option.

(6, counterparty_node_id, required),
(8, funding_txo, required),
});
},
// Note that, going forward, all new events must only write data inside of
// `write_tlv_fields`. Versions 0.0.101+ will ignore odd-numbered events that write
// data via `write_tlv_fields`.
Expand DownExpand Up@@ -1271,6 +1310,31 @@ impl MaybeReadable for Event {
};
f()
},
31u8 => {
let f = || {
let mut channel_id = [0; 32];
let mut user_channel_id: u128 = 0;
let mut former_temporary_channel_id = None;
let mut counterparty_node_id = RequiredWrapper(None);
let mut funding_txo = RequiredWrapper(None);
read_tlv_fields!(reader, {
(0, channel_id, required),
(2, user_channel_id, required),
(4, former_temporary_channel_id, required),
(6, counterparty_node_id, required),
(8, funding_txo, required),
});

Ok(Some(Event::ChannelPending {
channel_id,
user_channel_id,
former_temporary_channel_id,
counterparty_node_id: counterparty_node_id.0.unwrap(),
funding_txo: funding_txo.0.unwrap()
}))
};
f()
},
// Versions prior to 0.0.100 did not ignore odd types, instead returning InvalidValue.
// Version 0.0.100 failed to properly ignore odd types, possibly resulting in corrupt
// reads.
Expand Down
6 changes: 6 additions & 0 deletions lightning/src/ln/chanmon_update_fail_tests.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1864,6 +1864,7 @@ fn do_during_funding_monitor_fail(confirm_a_first: bool, restore_b_before_conf:
let (outpoint, latest_update, _) = nodes[0].chain_monitor.latest_monitor_update_id.lock().unwrap().get(&channel_id).unwrap().clone();
nodes[0].chain_monitor.chain_monitor.force_channel_monitor_updated(outpoint, latest_update);
check_added_monitors!(nodes[0], 0);
expect_channel_pending_event(&nodes[0], &nodes[1].node.get_our_node_id());

let events = nodes[0].node.get_and_clear_pending_events();
assert_eq!(events.len(), 0);
Expand All@@ -1885,6 +1886,9 @@ fn do_during_funding_monitor_fail(confirm_a_first: bool, restore_b_before_conf:
nodes[0].node.peer_disconnected(&nodes[1].node.get_our_node_id());
nodes[1].node.peer_disconnected(&nodes[0].node.get_our_node_id());
reconnect_nodes(&nodes[0], &nodes[1], (false, confirm_a_first), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (false, false));

// But we want to re-emit ChannelPending
expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id());
assert!(nodes[0].node.get_and_clear_pending_msg_events().is_empty());
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());

Expand DownExpand Up@@ -2808,6 +2812,7 @@ fn do_test_outbound_reload_without_init_mon(use_0conf: bool) {
let 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);
expect_channel_pending_event(&nodes[1], &nodes[0].node.get_our_node_id());

let bs_signed_locked = nodes[1].node.get_and_clear_pending_msg_events();
assert_eq!(bs_signed_locked.len(), if use_0conf { 2 } else { 1 });
Expand DownExpand Up@@ -2906,6 +2911,7 @@ fn do_test_inbound_reload_without_init_mon(use_0conf: bool, lock_commitment: boo

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());

let as_funding_tx = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
if lock_commitment {
Expand Down
43 changes: 42 additions & 1 deletion lightning/src/ln/channel.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -499,6 +499,7 @@ pub(super) struct Channel<Signer: ChannelSigner> {
user_id: u128,

channel_id: [u8; 32],
temporary_channel_id: Option<[u8; 32]>, // Will be `None` for channels created prior to 0.0.115.
channel_state: u32,

// When we reach max(6 blocks, minimum_depth), we need to send an AnnouncementSigs message to
Expand DownExpand Up@@ -729,6 +730,9 @@ pub(super) struct Channel<Signer: ChannelSigner> {
// blinded paths instead of simple scid+node_id aliases.
outbound_scid_alias: u64,

// We track whether we already emitted a `ChannelPending` event.
channel_pending_event_emitted: bool,

// We track whether we already emitted a `ChannelReady` event.
channel_ready_event_emitted: bool,

Expand DownExpand Up@@ -991,6 +995,8 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
}
}

let temporary_channel_id = entropy_source.get_secure_random_bytes();

Ok(Channel {
user_id,

Expand All@@ -1004,7 +1010,8 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {

inbound_handshake_limits_override: Some(config.channel_handshake_limits.clone()),

channel_id: entropy_source.get_secure_random_bytes(),
channel_id: temporary_channel_id,
temporary_channel_id: Some(temporary_channel_id),
channel_state: ChannelState::OurInitSent as u32,
announcement_sigs_state: AnnouncementSigsState::NotSent,
secp_ctx,
Expand DownExpand Up@@ -1103,6 +1110,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
latest_inbound_scid_alias: None,
outbound_scid_alias,

channel_pending_event_emitted: false,
channel_ready_event_emitted: false,

#[cfg(any(test, fuzzing))]
Expand DownExpand Up@@ -1350,6 +1358,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
inbound_handshake_limits_override: None,

channel_id: msg.temporary_channel_id,
temporary_channel_id: Some(msg.temporary_channel_id),
channel_state: (ChannelState::OurInitSent as u32) | (ChannelState::TheirInitSent as u32),
announcement_sigs_state: AnnouncementSigsState::NotSent,
secp_ctx,
Expand DownExpand Up@@ -1451,6 +1460,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
latest_inbound_scid_alias: None,
outbound_scid_alias,

channel_pending_event_emitted: false,
channel_ready_event_emitted: false,

#[cfg(any(test, fuzzing))]
Expand DownExpand Up@@ -4550,6 +4560,13 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
self.channel_id
}

// Return the `temporary_channel_id` used during channel establishment.
//
// Will return `None` for channels created prior to LDK version 0.0.115.
pub fn temporary_channel_id(&self) -> Option<[u8; 32]> {
self.temporary_channel_id
}

pub fn minimum_depth(&self) -> Option<u32> {
self.minimum_depth
}
Expand DownExpand Up@@ -4694,6 +4711,21 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
self.prev_config.map(|prev_config| prev_config.0)
}

// Checks whether we should emit a `ChannelPending` event.
pub(crate) fn should_emit_channel_pending_event(&mut self) -> bool {
self.is_funding_initiated() && !self.channel_pending_event_emitted
}

// Returns whether we already emitted a `ChannelPending` event.
pub(crate) fn channel_pending_event_emitted(&self) -> bool {
self.channel_pending_event_emitted
}

// Remembers that we already emitted a `ChannelPending` event.
pub(crate) fn set_channel_pending_event_emitted(&mut self) {
self.channel_pending_event_emitted = true;
}

// Checks whether we should emit a `ChannelReady` event.
pub(crate) fn should_emit_channel_ready_event(&mut self) -> bool {
self.is_usable() && !self.channel_ready_event_emitted
Expand DownExpand Up@@ -6420,6 +6452,7 @@ impl<Signer: WriteableEcdsaChannelSigner> Writeable for Channel<Signer> {
if self.holder_max_htlc_value_in_flight_msat != Self::get_holder_max_htlc_value_in_flight_msat(self.channel_value_satoshis, &old_max_in_flight_percent_config)
{ Some(self.holder_max_htlc_value_in_flight_msat) } else { None };

let channel_pending_event_emitted = Some(self.channel_pending_event_emitted);
let channel_ready_event_emitted = Some(self.channel_ready_event_emitted);

// `user_id` used to be a single u64 value. In order to remain backwards compatible with
Expand DownExpand Up@@ -6452,6 +6485,8 @@ impl<Signer: WriteableEcdsaChannelSigner> Writeable for Channel<Signer> {
(23, channel_ready_event_emitted, option),
(25, user_id_high_opt, option),
(27, self.channel_keys_id, required),
(29, self.temporary_channel_id, option),
(31, channel_pending_event_emitted, option),
});

Ok(())
Expand DownExpand Up@@ -6719,10 +6754,12 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
let mut announcement_sigs_state = Some(AnnouncementSigsState::NotSent);
let mut latest_inbound_scid_alias = None;
let mut outbound_scid_alias = None;
let mut channel_pending_event_emitted = None;
let mut channel_ready_event_emitted = None;

let mut user_id_high_opt: Option<u64> = None;
let mut channel_keys_id: Option<[u8; 32]> = None;
let mut temporary_channel_id: Option<[u8; 32]> = None;

read_tlv_fields!(reader, {
(0, announcement_sigs, option),
Expand All@@ -6743,6 +6780,8 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
(23, channel_ready_event_emitted, option),
(25, user_id_high_opt, option),
(27, channel_keys_id, option),
(29, temporary_channel_id, option),
(31, channel_pending_event_emitted, option),
});

let (channel_keys_id, holder_signer) = if let Some(channel_keys_id) = channel_keys_id {
Expand DownExpand Up@@ -6807,6 +6846,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
inbound_handshake_limits_override: None,

channel_id,
temporary_channel_id,
channel_state,
announcement_sigs_state: announcement_sigs_state.unwrap(),
secp_ctx,
Expand DownExpand Up@@ -6899,6 +6939,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
// Later in the ChannelManager deserialization phase we scan for channels and assign scid aliases if its missing
outbound_scid_alias: outbound_scid_alias.unwrap_or(0),

channel_pending_event_emitted: channel_pending_event_emitted.unwrap_or(true),
channel_ready_event_emitted: channel_ready_event_emitted.unwrap_or(true),

#[cfg(any(test, fuzzing))]
Expand Down
Loading