Skip to content

Commit

Permalink
Add FundingTxBroadcastSafe event
Browse files Browse the repository at this point in the history
The `FundingTxBroadcastSafe` event indicates that we have received
`funding_signed` message from our counterparty and that you should
broadcast the funding transaction.

This event is only emitted if upon generating the funding transaction
you call `ChannelManager::unsafe_manual_funding_transaction_generated`
that will emit this event instead of `ChannelPending` event.

`ChannelManager::unsafe_manual_funding_transaction_generated` wont check
if the funding transaction is signed, those its unsafe. It is manual
because you are responsibile on broadcasting the transaction once the
event is received.
  • Loading branch information
jbesraa committed Jun 13, 2024
1 parent f2237a7 commit b06df26
Show file tree
Hide file tree
Showing 5 changed files with 305 additions and 6 deletions.
64 changes: 63 additions & 1 deletion lightning/src/events/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,37 @@ pub enum Event {
/// [`UserConfig::manually_accept_inbound_channels`]: crate::util::config::UserConfig::manually_accept_inbound_channels
user_channel_id: u128,
},
/// Used to indicate that the counterparty node has provided the signature(s) required to
/// recover our funds in case they go offline.
///
/// It is safe (and your responsibility) to broadcast the funding transaction upon receiving this
/// event.
///
/// This event is only emitted if you called
/// [`ChannelManager::unsafe_manual_funding_transaction_generated`] instead of
/// [`ChannelManager::funding_transaction_generated`].
///
/// [`ChannelManager::unsafe_manual_funding_transaction_generated`]: crate::ln::channelmanager::ChannelManager::unsafe_manual_funding_transaction_generated
/// [`ChannelManager::funding_transaction_generated`]: crate::ln::channelmanager::ChannelManager::funding_transaction_generated
FundingTxBroadcastSafe {
/// The `channel_id` indicating which channel has reached this stage.
channel_id: ChannelId,
/// 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,
/// Channel funding transaction
funding_tx: Transaction,
/// The `node_id` of the channel counterparty.
counterparty_node_id: PublicKey,
/// The `temporary_channel_id` this channel used to be known by during channel establishment.
former_temporary_channel_id: ChannelId,
},
/// Indicates that we've been offered a payment and it needs to be claimed via calling
/// [`ChannelManager::claim_funds`] with the preimage given in [`PaymentPurpose`].
///
Expand Down Expand Up @@ -1447,7 +1478,17 @@ impl Writeable for Event {
write_tlv_fields!(writer, {
(0, peer_node_id, required),
});
}
},
&Event::FundingTxBroadcastSafe { ref channel_id, ref user_channel_id, ref funding_tx, ref counterparty_node_id, ref former_temporary_channel_id} => {
41u8.write(writer)?;
write_tlv_fields!(writer, {
(0, channel_id, required),
(2, user_channel_id, required),
(4, funding_tx, required),
(6, counterparty_node_id, required),
(8, former_temporary_channel_id, 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 Down Expand Up @@ -1884,6 +1925,27 @@ impl MaybeReadable for Event {
};
f()
},
41u8 => {
let mut channel_id = RequiredWrapper(None);
let mut user_channel_id = RequiredWrapper(None);
let mut funding_tx = RequiredWrapper(None);
let mut counterparty_node_id = RequiredWrapper(None);
let mut former_temporary_channel_id = RequiredWrapper(None);
read_tlv_fields!(reader, {
(0, channel_id, required),
(2, user_channel_id, required),
(4, funding_tx, required),
(6, counterparty_node_id, required),
(8, former_temporary_channel_id, required)
});
Ok(Some(Event::FundingTxBroadcastSafe {
channel_id: channel_id.0.unwrap(),
user_channel_id: user_channel_id.0.unwrap(),
funding_tx: funding_tx.0.unwrap(),
counterparty_node_id: counterparty_node_id.0.unwrap(),
former_temporary_channel_id: former_temporary_channel_id.0.unwrap(),
}))
},
// 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
51 changes: 51 additions & 0 deletions lightning/src/ln/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1413,6 +1413,9 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
// We track whether we already emitted a `ChannelPending` event.
channel_pending_event_emitted: bool,

// We track whether we already emitted a `FundingTxBroadcastSafe` event.
funding_tx_broadcast_safe_event_emitted: bool,

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

Expand All @@ -1429,6 +1432,14 @@ pub(super) struct ChannelContext<SP: Deref> where SP::Target: SignerProvider {
/// If we can't release a [`ChannelMonitorUpdate`] until some external action completes, we
/// store it here and only release it to the `ChannelManager` once it asks for it.
blocked_monitor_updates: Vec<PendingChannelMonitorUpdate>,

/// Using this flag will prevent the funding transaction from being broadcasted
/// and will allow the user to manually broadcast it.
///
/// The funding transaction can be accessed through the [`Event::FundingTxBroadcastSafe`] event.
///
/// [`Event::FundingTxBroadcastSafe`]: crate::events::Event::FundingTxBroadcastSafe
is_manual_broadcast: bool,
}

impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
Expand Down Expand Up @@ -1752,6 +1763,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
outbound_scid_alias: 0,

channel_pending_event_emitted: false,
funding_tx_broadcast_safe_event_emitted: false,
channel_ready_event_emitted: false,

#[cfg(any(test, fuzzing))]
Expand All @@ -1763,6 +1775,8 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
local_initiated_shutdown: None,

blocked_monitor_updates: Vec::new(),

is_manual_broadcast: false,
};

Ok(channel_context)
Expand Down Expand Up @@ -1976,6 +1990,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
outbound_scid_alias,

channel_pending_event_emitted: false,
funding_tx_broadcast_safe_event_emitted: false,
channel_ready_event_emitted: false,

#[cfg(any(test, fuzzing))]
Expand All @@ -1986,6 +2001,7 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {

blocked_monitor_updates: Vec::new(),
local_initiated_shutdown: None,
is_manual_broadcast: false,
})
}

Expand Down Expand Up @@ -2227,6 +2243,10 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
self.config.options.forwarding_fee_proportional_millionths
}

pub fn is_manual_broadcast(&self) -> bool {
self.is_manual_broadcast
}

pub fn get_cltv_expiry_delta(&self) -> u16 {
cmp::max(self.config.options.cltv_expiry_delta, MIN_CLTV_EXPIRY_DELTA)
}
Expand Down Expand Up @@ -2261,6 +2281,11 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
self.channel_pending_event_emitted
}

// Returns whether we already emitted a `FundingTxBroadcastSafe` event.
pub(crate) fn funding_tx_broadcast_safe_event_emitted(&self) -> bool {
self.funding_tx_broadcast_safe_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;
Expand All @@ -2276,6 +2301,11 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
self.channel_ready_event_emitted = true;
}

// Remembers that we already emitted a `FundingTxBroadcastSafe` event.
pub(crate) fn set_funding_tx_broadcast_safe_event_emitted(&mut self) {
self.funding_tx_broadcast_safe_event_emitted = true;
}

/// Tracks the number of ticks elapsed since the previous [`ChannelConfig`] was updated. Once
/// [`EXPIRE_PREV_CONFIG_TICKS`] is reached, the previous config is considered expired and will
/// no longer be considered when forwarding HTLCs.
Expand Down Expand Up @@ -2312,6 +2342,17 @@ impl<SP: Deref> ChannelContext<SP> where SP::Target: SignerProvider {
did_channel_update
}

/// Marking the channel as manual broadcast is used in order to prevent LDK from automatically
/// broadcasting the funding transaction.
///
/// This is useful if you wish to get hold of the funding transaction before it is broadcasted
/// via [`Event::FundingTxBroadcastSafe`] event.
///
/// [`Event::FundingTxBroadcastSafe`]: crate::events::Event::FundingTxBroadcastSafe
pub fn set_manual_broadcast(&mut self) {
self.is_manual_broadcast = true;
}

/// Returns true if funding_signed was sent/received and the
/// funding transaction has been broadcast if necessary.
pub fn is_funding_broadcast(&self) -> bool {
Expand Down Expand Up @@ -8662,6 +8703,7 @@ impl<SP: Deref> Writeable for Channel<SP> where SP::Target: SignerProvider {

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

// `user_id` used to be a single u64 value. In order to remain backwards compatible with
// versions prior to 0.0.113, the u128 is serialized as two separate u64 values. Therefore,
Expand All @@ -8674,6 +8716,7 @@ impl<SP: Deref> Writeable for Channel<SP> where SP::Target: SignerProvider {
if !self.context.monitor_pending_update_adds.is_empty() {
monitor_pending_update_adds = Some(&self.context.monitor_pending_update_adds);
}
let is_manual_broadcast = Some(self.context.is_manual_broadcast);

// `current_point` will become optional when async signing is implemented.
let cur_holder_commitment_point = Some(self.context.holder_commitment_point.current_point());
Expand Down Expand Up @@ -8718,6 +8761,8 @@ impl<SP: Deref> Writeable for Channel<SP> where SP::Target: SignerProvider {
(45, cur_holder_commitment_point, option),
(47, next_holder_commitment_point, option),
(49, self.context.local_initiated_shutdown, option), // Added in 0.0.122
(51, is_manual_broadcast, option),
(53, funding_tx_broadcast_safe_event_emitted, option)
});

Ok(())
Expand Down Expand Up @@ -9006,6 +9051,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
let mut outbound_scid_alias = None;
let mut channel_pending_event_emitted = None;
let mut channel_ready_event_emitted = None;
let mut funding_tx_broadcast_safe_event_emitted = None;

let mut user_id_high_opt: Option<u64> = None;
let mut channel_keys_id: Option<[u8; 32]> = None;
Expand All @@ -9029,6 +9075,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch

let mut cur_holder_commitment_point_opt: Option<PublicKey> = None;
let mut next_holder_commitment_point_opt: Option<PublicKey> = None;
let mut is_manual_broadcast = None;

read_tlv_fields!(reader, {
(0, announcement_sigs, option),
Expand Down Expand Up @@ -9063,6 +9110,8 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
(45, cur_holder_commitment_point_opt, option),
(47, next_holder_commitment_point_opt, option),
(49, local_initiated_shutdown, option),
(51, is_manual_broadcast, option),
(53, funding_tx_broadcast_safe_event_emitted, option),
});

let (channel_keys_id, holder_signer) = if let Some(channel_keys_id) = channel_keys_id {
Expand Down Expand Up @@ -9303,6 +9352,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),

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

Expand All @@ -9315,6 +9365,7 @@ impl<'a, 'b, 'c, ES: Deref, SP: Deref> ReadableArgs<(&'a ES, &'b SP, u32, &'c Ch
local_initiated_shutdown,

blocked_monitor_updates: blocked_monitor_updates.unwrap(),
is_manual_broadcast: is_manual_broadcast.unwrap_or(false),
},
#[cfg(any(dual_funding, splicing))]
dual_funding_channel_context: None,
Expand Down
79 changes: 74 additions & 5 deletions lightning/src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2548,6 +2548,20 @@ macro_rules! send_channel_ready {
}
}}
}
macro_rules! emit_funding_tx_broadcast_safe_event {
($locked_events: expr, $channel: expr, $funding_tx: expr) => {
if !$channel.context.funding_tx_broadcast_safe_event_emitted() {
$locked_events.push_back((events::Event::FundingTxBroadcastSafe {
channel_id: $channel.context.channel_id(),
user_channel_id: $channel.context.get_user_id(),
funding_tx: $funding_tx,
counterparty_node_id: $channel.context.get_counterparty_node_id(),
former_temporary_channel_id: $channel.context.temporary_channel_id().expect("Unreachable: FundingTxBroadcastSafe event feature added to channel establishment process in LDK v0.124.0 where this should never be None."),
}, None));
$channel.context.set_funding_tx_broadcast_safe_event_emitted();
}
}
}

macro_rules! emit_channel_pending_event {
($locked_events: expr, $channel: expr) => {
Expand Down Expand Up @@ -4188,7 +4202,7 @@ where
/// which checks the correctness of the funding transaction given the associated channel.
fn funding_transaction_generated_intern<FundingOutput: FnMut(&OutboundV1Channel<SP>, &Transaction) -> Result<OutPoint, &'static str>>(
&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, funding_transaction: Transaction, is_batch_funding: bool,
mut find_funding_output: FundingOutput,
mut find_funding_output: FundingOutput, is_manual_broadcast: bool,
) -> Result<(), APIError> {
let per_peer_state = self.per_peer_state.read().unwrap();
let peer_state_mutex = per_peer_state.get(counterparty_node_id)
Expand Down Expand Up @@ -4253,6 +4267,9 @@ where
msg,
});
}
if is_manual_broadcast {
chan.context.set_manual_broadcast();
}
match peer_state.channel_by_id.entry(chan.context.channel_id()) {
hash_map::Entry::Occupied(_) => {
panic!("Generated duplicate funding txid?");
Expand Down Expand Up @@ -4284,7 +4301,7 @@ where
pub(crate) fn funding_transaction_generated_unchecked(&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, funding_transaction: Transaction, output_index: u16) -> Result<(), APIError> {
self.funding_transaction_generated_intern(temporary_channel_id, counterparty_node_id, funding_transaction, false, |_, tx| {
Ok(OutPoint { txid: tx.txid(), index: output_index })
})
}, false)
}

/// Call this upon creation of a funding transaction for the given channel.
Expand Down Expand Up @@ -4321,6 +4338,46 @@ where
self.batch_funding_transaction_generated(&[(temporary_channel_id, counterparty_node_id)], funding_transaction)
}


/// Unsafe: This method does not check if the funding transaction is signed, i.e., if the
/// witness data is empty or not. It is the caller's responsibility to ensure that the funding
/// transaction is final.
///
/// If you wish to use a safer method, use [`ChannelManager::funding_transaction_generated`].
///
/// Call this in response to a [`Event::FundingGenerationReady`] event.
///
/// Note that if this method is called successfully, the funding transaction won't be
/// broadcasted and you are expected to broadcast it manually when receiving the
/// [`Event::FundingTxBroadcastSafe`] event.
///
/// Returns an [`APIError::APIMisuseError`] if no output was found which matches the parameters
/// in [`Event::FundingGenerationReady`].
///
/// Returns [`APIError::ChannelUnavailable`] if a funding transaction has already been provided
/// for the channel or if the channel has been closed as indicated by [`Event::ChannelClosed`].
///
/// May panic if the output found in the funding transaction is duplicative with some other
/// channel (note that this should be trivially prevented by using unique funding transaction
/// keys per-channel).
///
/// Note to keep the miner incentives aligned in moving the blockchain forward, we recommend
/// the wallet software generating the funding transaction to apply anti-fee sniping as
/// implemented by Bitcoin Core wallet. See <https://bitcoinops.org/en/topics/fee-sniping/> for
/// more details.
///
/// [`Event::FundingGenerationReady`]: crate::events::Event::FundingGenerationReady
/// [`Event::FundingTxBroadcastSafe`]: crate::events::Event::FundingTxBroadcastSafe
/// [`Event::ChannelClosed`]: crate::events::Event::ChannelClosed
/// [`ChannelManager::funding_transaction_generated`]: crate::ln::channelmanager::ChannelManager::funding_transaction_generated
pub fn unsafe_manual_funding_transaction_generated(&self, temporary_channel_id: &ChannelId, counterparty_node_id: &PublicKey, funding_transaction: Transaction) -> Result<(), APIError> {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);

let temporary_channels = &[(temporary_channel_id, counterparty_node_id)];
return self.batch_funding_transaction_generated_intern(temporary_channels, funding_transaction, true);

}

/// Call this upon creation of a batch funding transaction for the given channels.
///
/// Return values are identical to [`Self::funding_transaction_generated`], respective to
Expand All @@ -4344,6 +4401,11 @@ where
}
}
}
result.and(self.batch_funding_transaction_generated_intern(temporary_channels, funding_transaction, false))
}

fn batch_funding_transaction_generated_intern(&self, temporary_channels: &[(&ChannelId, &PublicKey)], funding_transaction: Transaction, is_manual_broadcast: bool) -> Result<(), APIError> {
let mut result = Ok(());
if funding_transaction.output.len() > u16::max_value() as usize {
result = result.and(Err(APIError::APIMisuseError {
err: "Transaction had more than 2^16 outputs, which is not supported".to_owned()
Expand Down Expand Up @@ -4411,7 +4473,8 @@ where
funding_batch_state.push((ChannelId::v1_from_funding_outpoint(outpoint), *counterparty_node_id, false));
}
Ok(outpoint)
})
},
is_manual_broadcast)
);
}
if let Err(ref e) = result {
Expand Down Expand Up @@ -6559,8 +6622,14 @@ where
}

if let Some(tx) = funding_broadcastable {
log_info!(logger, "Broadcasting funding transaction with txid {}", tx.txid());
self.tx_broadcaster.broadcast_transactions(&[&tx]);
if channel.context.is_manual_broadcast() {
log_info!(logger, "Not broadcasting funding transaction with txid {} as it is manually managed", tx.txid());
let mut pending_events = self.pending_events.lock().unwrap();
emit_funding_tx_broadcast_safe_event!(pending_events, channel, tx);
} else {
log_info!(logger, "Broadcasting funding transaction with txid {}", tx.txid());
self.tx_broadcaster.broadcast_transactions(&[&tx]);
}
}

{
Expand Down
Loading

0 comments on commit b06df26

Please sign in to comment.