Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e8a426c
Use fixed-size Vec allocations for BOLT12 messages
jkczyz Jan 28, 2025
f80d82e
Drop use of reserve_exact in BOLT12 tests
jkczyz Feb 6, 2025
24731d4
fix historical liquidity bucket decay
joostjager Feb 3, 2025
58a7b79
Drop `counterparty_spendable_height` accessor
TheBlueMatt Jan 27, 2025
2e80fbd
Set correct `counterparty_spendable_height` on c.p. revoked HTLCs
TheBlueMatt Jan 27, 2025
174f42e
Mark counterparty revoked offered HTLCs as `Unpinnable`
TheBlueMatt Jan 27, 2025
b5b6429
Call `peer_disconnected` after a handler refuses a connection
TheBlueMatt Jan 30, 2025
8eb3bd7
Avoid parsing `PublicKey`s when applying an unsigned chan update
TheBlueMatt Jan 31, 2025
63fa918
Properly pre-allocate `NetworkGraph` channel/node maps
TheBlueMatt Jan 31, 2025
bed125a
Ignore fees on first-hop channels from the public network graph
TheBlueMatt Feb 3, 2025
fd8eab9
Clean up `long_mpp_route_test` and `mpp_cheaper_route_test`
TheBlueMatt Feb 3, 2025
e46489d
Track `node_counter` in `RouteGraphNode`
TheBlueMatt Feb 3, 2025
b39aef6
Prefer higher-value, shorter equal-cost paths when routing
TheBlueMatt Feb 3, 2025
1976fb8
Move last-hop route handling to the common "normal" hop codepath
TheBlueMatt Feb 3, 2025
1beefb1
Add direct hops to intros after all blinded paths in pathfinding
TheBlueMatt Feb 2, 2025
ae4279d
Add `PathBuildingHop::best_path_from_hop_selected`
TheBlueMatt Feb 3, 2025
1788f80
Replace a few router `expect`s with `debug_assert` + `Err`-returns
TheBlueMatt Feb 10, 2025
bd56ddf
More completely ignore route hints which are for our own channels
TheBlueMatt Feb 10, 2025
fa8d599
Add SemVer compatibility checks to CI
tnull Jan 24, 2025
9d2449a
Fix overflow in historical scoring model point count summation
TheBlueMatt Feb 23, 2025
7c56f21
Cancel claims signed by a remote `ChannelMonitor` when reorging
TheBlueMatt Feb 25, 2025
653c482
Drop return value from `provide_latest_holder_commitment_tx`
TheBlueMatt Feb 25, 2025
6749a07
Only generate a post-close lock ChannelMonitorUpdate if we need one
TheBlueMatt Feb 25, 2025
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
57 changes: 57 additions & 0 deletions .github/workflows/semver.yml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
name: SemVer checks
on:
push:
branches-ignore:
- master
pull_request:
branches-ignore:
- master

jobs:
semver-checks:
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@v4
- name: Install Rust stable toolchain
run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile=minimal --default-toolchain stable
rustup override set stable
- name: Check SemVer with default features
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
feature-group: default-features
- name: Check SemVer *without* default features
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
feature-group: only-explicit-features
- name: Check lightning-background-processor SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
package: lightning-background-processor
feature-group: only-explicit-features
features: futures
- name: Check lightning-block-sync SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
package: lightning-block-sync
feature-group: only-explicit-features
features: rpc-client,rest-client
- name: Check lightning-transaction-sync electrum SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
manifest-path: lightning-transaction-sync/Cargo.toml
feature-group: only-explicit-features
features: electrum
- name: Check lightning-transaction-sync esplora-blocking SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
manifest-path: lightning-transaction-sync/Cargo.toml
feature-group: only-explicit-features
features: esplora-blocking
- name: Check lightning-transaction-sync esplora-async SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
manifest-path: lightning-transaction-sync/Cargo.toml
feature-group: only-explicit-features
features: esplora-async
4 changes: 2 additions & 2 deletions lightning-background-processor/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -2379,8 +2379,8 @@ mod tests {
42,
53,
features,
$nodes[0].node.get_our_node_id(),
$nodes[1].node.get_our_node_id(),
$nodes[0].node.get_our_node_id().into(),
$nodes[1].node.get_our_node_id().into(),
)
.expect("Failed to update channel from partial announcement");
let original_graph_description = $nodes[0].network_graph.to_string();
Expand Down
90 changes: 46 additions & 44 deletions lightning/src/chain/channelmonitor.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1509,8 +1509,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
fn provide_latest_holder_commitment_tx(
&self, holder_commitment_tx: HolderCommitmentTransaction,
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,
) -> Result<(), ()> {
self.inner.lock().unwrap().provide_latest_holder_commitment_tx(holder_commitment_tx, htlc_outputs, &Vec::new(), Vec::new()).map_err(|_| ())
) {
self.inner.lock().unwrap().provide_latest_holder_commitment_tx(holder_commitment_tx, htlc_outputs, &Vec::new(), Vec::new())
}

/// This is used to provide payment preimage(s) out-of-band during startup without updating the
Expand DownExpand Up@@ -1737,10 +1737,14 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
self.inner.lock().unwrap().get_cur_holder_commitment_number()
}

/// Gets whether we've been notified that this channel is closed by the `ChannelManager` (i.e.
/// via a [`ChannelMonitorUpdateStep::ChannelForceClosed`]).
pub(crate) fn offchain_closed(&self) -> bool {
self.inner.lock().unwrap().lockdown_from_offchain
/// Fetches whether this monitor has marked the channel as closed and will refuse any further
/// updates to the commitment transactions.
///
/// It can be marked closed in a few different ways, including via a
/// [`ChannelMonitorUpdateStep::ChannelForceClosed`] or if the channel has been closed
/// on-chain.
pub(crate) fn no_further_updates_allowed(&self) -> bool {
self.inner.lock().unwrap().no_further_updates_allowed()
}

/// Gets the `node_id` of the counterparty for this channel.
Expand DownExpand Up@@ -2901,7 +2905,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
/// is important that any clones of this channel monitor (including remote clones) by kept
/// up-to-date as our holder commitment transaction is updated.
/// Panics if set_on_holder_tx_csv has never been called.
fn provide_latest_holder_commitment_tx(&mut self, holder_commitment_tx: HolderCommitmentTransaction, mut htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>, claimed_htlcs: &[(SentHTLCId, PaymentPreimage)], nondust_htlc_sources: Vec<HTLCSource>) -> Result<(), &'static str> {
fn provide_latest_holder_commitment_tx(&mut self, holder_commitment_tx: HolderCommitmentTransaction, mut htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>, claimed_htlcs: &[(SentHTLCId, PaymentPreimage)], nondust_htlc_sources: Vec<HTLCSource>) {
if htlc_outputs.iter().any(|(_, s, _)| s.is_some()) {
// If we have non-dust HTLCs in htlc_outputs, ensure they match the HTLCs in the
// `holder_commitment_tx`. In the future, we'll no longer provide the redundant data
Expand DownExpand Up@@ -2978,10 +2982,6 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
self.counterparty_fulfilled_htlcs.insert(*claimed_htlc_id, *claimed_preimage);
}
if self.holder_tx_signed {
return Err("Latest holder commitment signed has already been signed, update is rejected");
}
Ok(())
}

/// Provides a payment_hash->payment_preimage mapping. Will be automatically pruned when all
Expand DownExpand Up@@ -3202,11 +3202,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { commitment_tx, htlc_outputs, claimed_htlcs, nondust_htlc_sources } => {
log_trace!(logger, "Updating ChannelMonitor with latest holder commitment transaction info");
if self.lockdown_from_offchain { panic!(); }
if let Err(e) = self.provide_latest_holder_commitment_tx(commitment_tx.clone(), htlc_outputs.clone(), &claimed_htlcs, nondust_htlc_sources.clone()) {
log_error!(logger, "Providing latest holder commitment transaction failed/was refused:");
log_error!(logger, " {}", e);
ret = Err(());
}
self.provide_latest_holder_commitment_tx(commitment_tx.clone(), htlc_outputs.clone(), &claimed_htlcs, nondust_htlc_sources.clone());
}
ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { commitment_txid, htlc_outputs, commitment_number, their_per_commitment_point, .. } => {
log_trace!(logger, "Updating ChannelMonitor with latest counterparty commitment transaction info");
Expand DownExpand Up@@ -3286,12 +3282,16 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
}

if ret.is_ok() && (self.funding_spend_seen || self.lockdown_from_offchain || self.holder_tx_signed) && is_pre_close_update {
if ret.is_ok() && self.no_further_updates_allowed() && is_pre_close_update {
log_error!(logger, "Refusing Channel Monitor Update as counterparty attempted to update commitment after funding was spent");
Err(())
} else { ret }
}

fn no_further_updates_allowed(&self) -> bool {
self.funding_spend_seen || self.lockdown_from_offchain || self.holder_tx_signed
}

fn get_latest_update_id(&self) -> u64 {
self.latest_update_id
}
Expand DownExpand Up@@ -3564,11 +3564,16 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
return (claimable_outpoints, to_counterparty_output_info);
}
let revk_htlc_outp = RevokedHTLCOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, htlc.amount_msat / 1000, htlc.clone(), &self.onchain_tx_handler.channel_transaction_parameters.channel_type_features);
let counterparty_spendable_height = if htlc.offered {
htlc.cltv_expiry
} else {
height
};
let justice_package = PackageTemplate::build_package(
commitment_txid,
transaction_output_index,
PackageSolvingData::RevokedHTLCOutput(revk_htlc_outp),
htlc.cltv_expiry,
counterparty_spendable_height,
);
claimable_outpoints.push(justice_package);
}
Expand DownExpand Up@@ -3869,35 +3874,32 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
}
}
if self.holder_tx_signed {
// If we've signed, we may have broadcast either commitment (prev or current), and
// attempted to claim from it immediately without waiting for a confirmation.
if self.current_holder_commitment_tx.txid != *confirmed_commitment_txid {
// Cancel any pending claims for any holder commitments in case they had previously
// confirmed or been signed (in which case we will start attempting to claim without
// waiting for confirmation).
if self.current_holder_commitment_tx.txid != *confirmed_commitment_txid {
log_trace!(logger, "Canceling claims for previously broadcast holder commitment {}",
self.current_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: self.current_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &self.current_holder_commitment_tx.htlc_outputs {
if let Some(vout) = htlc.transaction_output_index {
outpoint.vout = vout;
self.onchain_tx_handler.abandon_claim(&outpoint);
}
}
}
if let Some(prev_holder_commitment_tx) = &self.prev_holder_signed_commitment_tx {
if prev_holder_commitment_tx.txid != *confirmed_commitment_txid {
log_trace!(logger, "Canceling claims for previously broadcast holder commitment {}",
self.current_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: self.current_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &self.current_holder_commitment_tx.htlc_outputs {
prev_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: prev_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &prev_holder_commitment_tx.htlc_outputs {
if let Some(vout) = htlc.transaction_output_index {
outpoint.vout = vout;
self.onchain_tx_handler.abandon_claim(&outpoint);
}
}
}
if let Some(prev_holder_commitment_tx) = &self.prev_holder_signed_commitment_tx {
if prev_holder_commitment_tx.txid != *confirmed_commitment_txid {
log_trace!(logger, "Canceling claims for previously broadcast holder commitment {}",
prev_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: prev_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &prev_holder_commitment_tx.htlc_outputs {
if let Some(vout) = htlc.transaction_output_index {
outpoint.vout = vout;
self.onchain_tx_handler.abandon_claim(&outpoint);
}
}
}
}
} else {
// No previous claim.
}
}

Expand DownExpand Up@@ -4233,7 +4235,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
}

if self.lockdown_from_offchain || self.funding_spend_seen || self.holder_tx_signed {
if self.no_further_updates_allowed() {
// Fail back HTLCs on backwards channels if they expire within
// `LATENCY_GRACE_PERIOD_BLOCKS` blocks and the channel is closed (i.e. we're at a
// point where no further off-chain updates will be accepted). If we haven't seen the
Expand DownExpand Up@@ -5384,7 +5386,7 @@ mod tests {
let dummy_commitment_tx = HolderCommitmentTransaction::dummy(&mut htlcs);

monitor.provide_latest_holder_commitment_tx(dummy_commitment_tx.clone(),
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect()).unwrap();
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect());
monitor.provide_latest_counterparty_commitment_tx(Txid::from_byte_array(Sha256::hash(b"1").to_byte_array()),
preimages_slice_to_htlc_outputs!(preimages[5..15]), 281474976710655, dummy_key, &logger);
monitor.provide_latest_counterparty_commitment_tx(Txid::from_byte_array(Sha256::hash(b"2").to_byte_array()),
Expand DownExpand Up@@ -5422,7 +5424,7 @@ mod tests {
let mut htlcs = preimages_slice_to_htlcs!(preimages[0..5]);
let dummy_commitment_tx = HolderCommitmentTransaction::dummy(&mut htlcs);
monitor.provide_latest_holder_commitment_tx(dummy_commitment_tx.clone(),
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect()).unwrap();
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect());
secret[0..32].clone_from_slice(&<Vec<u8>>::from_hex("2273e227a5b7449b6e70f1fb4652864038b1cbf9cd7c043a7d6456b7fc275ad8").unwrap());
monitor.provide_secret(281474976710653, secret.clone()).unwrap();
assert_eq!(monitor.inner.lock().unwrap().payment_preimages.len(), 12);
Expand All@@ -5433,7 +5435,7 @@ mod tests {
let mut htlcs = preimages_slice_to_htlcs!(preimages[0..3]);
let dummy_commitment_tx = HolderCommitmentTransaction::dummy(&mut htlcs);
monitor.provide_latest_holder_commitment_tx(dummy_commitment_tx,
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect()).unwrap();
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect());
secret[0..32].clone_from_slice(&<Vec<u8>>::from_hex("27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116").unwrap());
monitor.provide_secret(281474976710652, secret.clone()).unwrap();
assert_eq!(monitor.inner.lock().unwrap().payment_preimages.len(), 5);
Expand Down
46 changes: 29 additions & 17 deletions lightning/src/chain/package.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -699,8 +699,13 @@ impl PackageSolvingData {
match self {
PackageSolvingData::RevokedOutput(RevokedOutput { .. }) =>
PackageMalleability::Malleable(AggregationCluster::Unpinnable),
PackageSolvingData::RevokedHTLCOutput(..) =>
PackageMalleability::Malleable(AggregationCluster::Pinnable),
PackageSolvingData::RevokedHTLCOutput(RevokedHTLCOutput { htlc, .. }) => {
if htlc.offered {
PackageMalleability::Malleable(AggregationCluster::Unpinnable)
} else {
PackageMalleability::Malleable(AggregationCluster::Pinnable)
}
},
PackageSolvingData::CounterpartyOfferedHTLCOutput(..) =>
PackageMalleability::Malleable(AggregationCluster::Unpinnable),
PackageSolvingData::CounterpartyReceivedHTLCOutput(..) =>
Expand DownExpand Up@@ -771,10 +776,12 @@ pub struct PackageTemplate {
/// Block height at which our counterparty can potentially claim this output as well (assuming
/// they have the keys or information required to do so).
///
/// This is used primarily by external consumers to decide when an output becomes "pinnable"
/// because the counterparty can potentially spend it. It is also used internally by
/// [`Self::get_height_timer`] to identify when an output must be claimed by, depending on the
/// type of output.
/// This is used primarily to decide when an output becomes "pinnable" because the counterparty
/// can potentially spend it. It is also used internally by [`Self::get_height_timer`] to
/// identify when an output must be claimed by, depending on the type of output.
///
/// Note that for revoked counterparty HTLC outputs the value may be zero in some cases where
/// we upgraded from LDK 0.1 or prior.
counterparty_spendable_height: u32,
// Cache of package feerate committed at previous (re)broadcast. If bumping resources
// (either claimed output value or external utxo), it will keep increasing until holder
Expand DownExpand Up@@ -834,17 +841,17 @@ impl PackageTemplate {
// Now check that we only merge packages if they are both unpinnable or both
// pinnable.
let self_pinnable = self_cluster == AggregationCluster::Pinnable ||
self.counterparty_spendable_height() <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
self.counterparty_spendable_height <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
let other_pinnable = other_cluster == AggregationCluster::Pinnable ||
other.counterparty_spendable_height() <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
other.counterparty_spendable_height <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
if self_pinnable && other_pinnable {
return true;
}

let self_unpinnable = self_cluster == AggregationCluster::Unpinnable &&
self.counterparty_spendable_height() > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
self.counterparty_spendable_height > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
let other_unpinnable = other_cluster == AggregationCluster::Unpinnable &&
other.counterparty_spendable_height() > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
other.counterparty_spendable_height > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
if self_unpinnable && other_unpinnable {
return true;
}
Expand All@@ -855,13 +862,6 @@ impl PackageTemplate {
pub(crate) fn is_malleable(&self) -> bool {
matches!(self.malleability, PackageMalleability::Malleable(..))
}
/// The height at which our counterparty may be able to spend this output.
///
/// This is an important limit for aggregation as after this height our counterparty may be
/// able to pin transactions spending this output in the mempool.
pub(crate) fn counterparty_spendable_height(&self) -> u32 {
self.counterparty_spendable_height
}
pub(crate) fn previous_feerate(&self) -> u64 {
self.feerate_previous
}
Expand DownExpand Up@@ -1225,6 +1225,18 @@ impl Readable for PackageTemplate {
(4, _height_original, option), // Written with a dummy value since 0.1
(6, height_timer, option),
});
for (_, input) in &inputs {
if let PackageSolvingData::RevokedHTLCOutput(RevokedHTLCOutput { htlc, .. }) = input {
// LDK versions through 0.1 set the wrong counterparty_spendable_height for
// non-offered revoked HTLCs (ie HTLCs we sent to our counterparty which they can
// claim with a preimage immediately). Here we detect this and reset the value to
// zero, as the value is unused except for merging decisions which doesn't care
// about any values below the current height.
if !htlc.offered && htlc.cltv_expiry == counterparty_spendable_height {
counterparty_spendable_height = 0;
}
}
}
Ok(PackageTemplate {
inputs,
malleability,
Expand Down
4 changes: 2 additions & 2 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -13364,8 +13364,8 @@ where
// claim.
// Note that a `ChannelMonitor` is created with `update_id` 0 and after we
// provide it with a closure update its `update_id` will be at 1.
if !monitor.offchain_closed() || monitor.get_latest_update_id() > 1 {
should_queue_fc_update = !monitor.offchain_closed();
if !monitor.no_further_updates_allowed() || monitor.get_latest_update_id() > 1 {
should_queue_fc_update = !monitor.no_further_updates_allowed();
let mut latest_update_id = monitor.get_latest_update_id();
if should_queue_fc_update {
latest_update_id += 1;
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
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;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
0.1 Backports for 0.1.2 by TheBlueMatt · Pull Request #3613 · lightningdevkit/rust-lightning · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e8a426c
Use fixed-size Vec allocations for BOLT12 messages
jkczyz Jan 28, 2025
f80d82e
Drop use of reserve_exact in BOLT12 tests
jkczyz Feb 6, 2025
24731d4
fix historical liquidity bucket decay
joostjager Feb 3, 2025
58a7b79
Drop `counterparty_spendable_height` accessor
TheBlueMatt Jan 27, 2025
2e80fbd
Set correct `counterparty_spendable_height` on c.p. revoked HTLCs
TheBlueMatt Jan 27, 2025
174f42e
Mark counterparty revoked offered HTLCs as `Unpinnable`
TheBlueMatt Jan 27, 2025
b5b6429
Call `peer_disconnected` after a handler refuses a connection
TheBlueMatt Jan 30, 2025
8eb3bd7
Avoid parsing `PublicKey`s when applying an unsigned chan update
TheBlueMatt Jan 31, 2025
63fa918
Properly pre-allocate `NetworkGraph` channel/node maps
TheBlueMatt Jan 31, 2025
bed125a
Ignore fees on first-hop channels from the public network graph
TheBlueMatt Feb 3, 2025
fd8eab9
Clean up `long_mpp_route_test` and `mpp_cheaper_route_test`
TheBlueMatt Feb 3, 2025
e46489d
Track `node_counter` in `RouteGraphNode`
TheBlueMatt Feb 3, 2025
b39aef6
Prefer higher-value, shorter equal-cost paths when routing
TheBlueMatt Feb 3, 2025
1976fb8
Move last-hop route handling to the common "normal" hop codepath
TheBlueMatt Feb 3, 2025
1beefb1
Add direct hops to intros after all blinded paths in pathfinding
TheBlueMatt Feb 2, 2025
ae4279d
Add `PathBuildingHop::best_path_from_hop_selected`
TheBlueMatt Feb 3, 2025
1788f80
Replace a few router `expect`s with `debug_assert` + `Err`-returns
TheBlueMatt Feb 10, 2025
bd56ddf
More completely ignore route hints which are for our own channels
TheBlueMatt Feb 10, 2025
fa8d599
Add SemVer compatibility checks to CI
tnull Jan 24, 2025
9d2449a
Fix overflow in historical scoring model point count summation
TheBlueMatt Feb 23, 2025
7c56f21
Cancel claims signed by a remote `ChannelMonitor` when reorging
TheBlueMatt Feb 25, 2025
653c482
Drop return value from `provide_latest_holder_commitment_tx`
TheBlueMatt Feb 25, 2025
6749a07
Only generate a post-close lock ChannelMonitorUpdate if we need one
TheBlueMatt Feb 25, 2025
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
57 changes: 57 additions & 0 deletions .github/workflows/semver.yml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
name: SemVer checks
on:
push:
branches-ignore:
- master
pull_request:
branches-ignore:
- master

jobs:
semver-checks:
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@v4
- name: Install Rust stable toolchain
run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile=minimal --default-toolchain stable
rustup override set stable
- name: Check SemVer with default features
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
feature-group: default-features
- name: Check SemVer *without* default features
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
feature-group: only-explicit-features
- name: Check lightning-background-processor SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
package: lightning-background-processor
feature-group: only-explicit-features
features: futures
- name: Check lightning-block-sync SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
package: lightning-block-sync
feature-group: only-explicit-features
features: rpc-client,rest-client
- name: Check lightning-transaction-sync electrum SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
manifest-path: lightning-transaction-sync/Cargo.toml
feature-group: only-explicit-features
features: electrum
- name: Check lightning-transaction-sync esplora-blocking SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
manifest-path: lightning-transaction-sync/Cargo.toml
feature-group: only-explicit-features
features: esplora-blocking
- name: Check lightning-transaction-sync esplora-async SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
manifest-path: lightning-transaction-sync/Cargo.toml
feature-group: only-explicit-features
features: esplora-async
4 changes: 2 additions & 2 deletions lightning-background-processor/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -2379,8 +2379,8 @@ mod tests {
42,
53,
features,
$nodes[0].node.get_our_node_id(),
$nodes[1].node.get_our_node_id(),
$nodes[0].node.get_our_node_id().into(),
$nodes[1].node.get_our_node_id().into(),
)
.expect("Failed to update channel from partial announcement");
let original_graph_description = $nodes[0].network_graph.to_string();
Expand Down
90 changes: 46 additions & 44 deletions lightning/src/chain/channelmonitor.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1509,8 +1509,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
fn provide_latest_holder_commitment_tx(
&self, holder_commitment_tx: HolderCommitmentTransaction,
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,
) -> Result<(), ()> {
self.inner.lock().unwrap().provide_latest_holder_commitment_tx(holder_commitment_tx, htlc_outputs, &Vec::new(), Vec::new()).map_err(|_| ())
) {
self.inner.lock().unwrap().provide_latest_holder_commitment_tx(holder_commitment_tx, htlc_outputs, &Vec::new(), Vec::new())
}

/// This is used to provide payment preimage(s) out-of-band during startup without updating the
Expand DownExpand Up@@ -1737,10 +1737,14 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
self.inner.lock().unwrap().get_cur_holder_commitment_number()
}

/// Gets whether we've been notified that this channel is closed by the `ChannelManager` (i.e.
/// via a [`ChannelMonitorUpdateStep::ChannelForceClosed`]).
pub(crate) fn offchain_closed(&self) -> bool {
self.inner.lock().unwrap().lockdown_from_offchain
/// Fetches whether this monitor has marked the channel as closed and will refuse any further
/// updates to the commitment transactions.
///
/// It can be marked closed in a few different ways, including via a
/// [`ChannelMonitorUpdateStep::ChannelForceClosed`] or if the channel has been closed
/// on-chain.
pub(crate) fn no_further_updates_allowed(&self) -> bool {
self.inner.lock().unwrap().no_further_updates_allowed()
}

/// Gets the `node_id` of the counterparty for this channel.
Expand DownExpand Up@@ -2901,7 +2905,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
/// is important that any clones of this channel monitor (including remote clones) by kept
/// up-to-date as our holder commitment transaction is updated.
/// Panics if set_on_holder_tx_csv has never been called.
fn provide_latest_holder_commitment_tx(&mut self, holder_commitment_tx: HolderCommitmentTransaction, mut htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>, claimed_htlcs: &[(SentHTLCId, PaymentPreimage)], nondust_htlc_sources: Vec<HTLCSource>) -> Result<(), &'static str> {
fn provide_latest_holder_commitment_tx(&mut self, holder_commitment_tx: HolderCommitmentTransaction, mut htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>, claimed_htlcs: &[(SentHTLCId, PaymentPreimage)], nondust_htlc_sources: Vec<HTLCSource>) {
if htlc_outputs.iter().any(|(_, s, _)| s.is_some()) {
// If we have non-dust HTLCs in htlc_outputs, ensure they match the HTLCs in the
// `holder_commitment_tx`. In the future, we'll no longer provide the redundant data
Expand DownExpand Up@@ -2978,10 +2982,6 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
self.counterparty_fulfilled_htlcs.insert(*claimed_htlc_id, *claimed_preimage);
}
if self.holder_tx_signed {
return Err("Latest holder commitment signed has already been signed, update is rejected");
}
Ok(())
}

/// Provides a payment_hash->payment_preimage mapping. Will be automatically pruned when all
Expand DownExpand Up@@ -3202,11 +3202,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { commitment_tx, htlc_outputs, claimed_htlcs, nondust_htlc_sources } => {
log_trace!(logger, "Updating ChannelMonitor with latest holder commitment transaction info");
if self.lockdown_from_offchain { panic!(); }
if let Err(e) = self.provide_latest_holder_commitment_tx(commitment_tx.clone(), htlc_outputs.clone(), &claimed_htlcs, nondust_htlc_sources.clone()) {
log_error!(logger, "Providing latest holder commitment transaction failed/was refused:");
log_error!(logger, " {}", e);
ret = Err(());
}
self.provide_latest_holder_commitment_tx(commitment_tx.clone(), htlc_outputs.clone(), &claimed_htlcs, nondust_htlc_sources.clone());
}
ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { commitment_txid, htlc_outputs, commitment_number, their_per_commitment_point, .. } => {
log_trace!(logger, "Updating ChannelMonitor with latest counterparty commitment transaction info");
Expand DownExpand Up@@ -3286,12 +3282,16 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
}

if ret.is_ok() && (self.funding_spend_seen || self.lockdown_from_offchain || self.holder_tx_signed) && is_pre_close_update {
if ret.is_ok() && self.no_further_updates_allowed() && is_pre_close_update {
log_error!(logger, "Refusing Channel Monitor Update as counterparty attempted to update commitment after funding was spent");
Err(())
} else { ret }
}

fn no_further_updates_allowed(&self) -> bool {
self.funding_spend_seen || self.lockdown_from_offchain || self.holder_tx_signed
}

fn get_latest_update_id(&self) -> u64 {
self.latest_update_id
}
Expand DownExpand Up@@ -3564,11 +3564,16 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
return (claimable_outpoints, to_counterparty_output_info);
}
let revk_htlc_outp = RevokedHTLCOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, htlc.amount_msat / 1000, htlc.clone(), &self.onchain_tx_handler.channel_transaction_parameters.channel_type_features);
let counterparty_spendable_height = if htlc.offered {
htlc.cltv_expiry
} else {
height
};
let justice_package = PackageTemplate::build_package(
commitment_txid,
transaction_output_index,
PackageSolvingData::RevokedHTLCOutput(revk_htlc_outp),
htlc.cltv_expiry,
counterparty_spendable_height,
);
claimable_outpoints.push(justice_package);
}
Expand DownExpand Up@@ -3869,35 +3874,32 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
}
}
if self.holder_tx_signed {
// If we've signed, we may have broadcast either commitment (prev or current), and
// attempted to claim from it immediately without waiting for a confirmation.
if self.current_holder_commitment_tx.txid != *confirmed_commitment_txid {
// Cancel any pending claims for any holder commitments in case they had previously
// confirmed or been signed (in which case we will start attempting to claim without
// waiting for confirmation).
if self.current_holder_commitment_tx.txid != *confirmed_commitment_txid {
log_trace!(logger, "Canceling claims for previously broadcast holder commitment {}",
self.current_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: self.current_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &self.current_holder_commitment_tx.htlc_outputs {
if let Some(vout) = htlc.transaction_output_index {
outpoint.vout = vout;
self.onchain_tx_handler.abandon_claim(&outpoint);
}
}
}
if let Some(prev_holder_commitment_tx) = &self.prev_holder_signed_commitment_tx {
if prev_holder_commitment_tx.txid != *confirmed_commitment_txid {
log_trace!(logger, "Canceling claims for previously broadcast holder commitment {}",
self.current_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: self.current_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &self.current_holder_commitment_tx.htlc_outputs {
prev_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: prev_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &prev_holder_commitment_tx.htlc_outputs {
if let Some(vout) = htlc.transaction_output_index {
outpoint.vout = vout;
self.onchain_tx_handler.abandon_claim(&outpoint);
}
}
}
if let Some(prev_holder_commitment_tx) = &self.prev_holder_signed_commitment_tx {
if prev_holder_commitment_tx.txid != *confirmed_commitment_txid {
log_trace!(logger, "Canceling claims for previously broadcast holder commitment {}",
prev_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: prev_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &prev_holder_commitment_tx.htlc_outputs {
if let Some(vout) = htlc.transaction_output_index {
outpoint.vout = vout;
self.onchain_tx_handler.abandon_claim(&outpoint);
}
}
}
}
} else {
// No previous claim.
}
}

Expand DownExpand Up@@ -4233,7 +4235,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
}

if self.lockdown_from_offchain || self.funding_spend_seen || self.holder_tx_signed {
if self.no_further_updates_allowed() {
// Fail back HTLCs on backwards channels if they expire within
// `LATENCY_GRACE_PERIOD_BLOCKS` blocks and the channel is closed (i.e. we're at a
// point where no further off-chain updates will be accepted). If we haven't seen the
Expand DownExpand Up@@ -5384,7 +5386,7 @@ mod tests {
let dummy_commitment_tx = HolderCommitmentTransaction::dummy(&mut htlcs);

monitor.provide_latest_holder_commitment_tx(dummy_commitment_tx.clone(),
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect()).unwrap();
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect());
monitor.provide_latest_counterparty_commitment_tx(Txid::from_byte_array(Sha256::hash(b"1").to_byte_array()),
preimages_slice_to_htlc_outputs!(preimages[5..15]), 281474976710655, dummy_key, &logger);
monitor.provide_latest_counterparty_commitment_tx(Txid::from_byte_array(Sha256::hash(b"2").to_byte_array()),
Expand DownExpand Up@@ -5422,7 +5424,7 @@ mod tests {
let mut htlcs = preimages_slice_to_htlcs!(preimages[0..5]);
let dummy_commitment_tx = HolderCommitmentTransaction::dummy(&mut htlcs);
monitor.provide_latest_holder_commitment_tx(dummy_commitment_tx.clone(),
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect()).unwrap();
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect());
secret[0..32].clone_from_slice(&<Vec<u8>>::from_hex("2273e227a5b7449b6e70f1fb4652864038b1cbf9cd7c043a7d6456b7fc275ad8").unwrap());
monitor.provide_secret(281474976710653, secret.clone()).unwrap();
assert_eq!(monitor.inner.lock().unwrap().payment_preimages.len(), 12);
Expand All@@ -5433,7 +5435,7 @@ mod tests {
let mut htlcs = preimages_slice_to_htlcs!(preimages[0..3]);
let dummy_commitment_tx = HolderCommitmentTransaction::dummy(&mut htlcs);
monitor.provide_latest_holder_commitment_tx(dummy_commitment_tx,
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect()).unwrap();
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect());
secret[0..32].clone_from_slice(&<Vec<u8>>::from_hex("27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116").unwrap());
monitor.provide_secret(281474976710652, secret.clone()).unwrap();
assert_eq!(monitor.inner.lock().unwrap().payment_preimages.len(), 5);
Expand Down
46 changes: 29 additions & 17 deletions lightning/src/chain/package.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -699,8 +699,13 @@ impl PackageSolvingData {
match self {
PackageSolvingData::RevokedOutput(RevokedOutput { .. }) =>
PackageMalleability::Malleable(AggregationCluster::Unpinnable),
PackageSolvingData::RevokedHTLCOutput(..) =>
PackageMalleability::Malleable(AggregationCluster::Pinnable),
PackageSolvingData::RevokedHTLCOutput(RevokedHTLCOutput { htlc, .. }) => {
if htlc.offered {
PackageMalleability::Malleable(AggregationCluster::Unpinnable)
} else {
PackageMalleability::Malleable(AggregationCluster::Pinnable)
}
},
PackageSolvingData::CounterpartyOfferedHTLCOutput(..) =>
PackageMalleability::Malleable(AggregationCluster::Unpinnable),
PackageSolvingData::CounterpartyReceivedHTLCOutput(..) =>
Expand DownExpand Up@@ -771,10 +776,12 @@ pub struct PackageTemplate {
/// Block height at which our counterparty can potentially claim this output as well (assuming
/// they have the keys or information required to do so).
///
/// This is used primarily by external consumers to decide when an output becomes "pinnable"
/// because the counterparty can potentially spend it. It is also used internally by
/// [`Self::get_height_timer`] to identify when an output must be claimed by, depending on the
/// type of output.
/// This is used primarily to decide when an output becomes "pinnable" because the counterparty
/// can potentially spend it. It is also used internally by [`Self::get_height_timer`] to
/// identify when an output must be claimed by, depending on the type of output.
///
/// Note that for revoked counterparty HTLC outputs the value may be zero in some cases where
/// we upgraded from LDK 0.1 or prior.
counterparty_spendable_height: u32,
// Cache of package feerate committed at previous (re)broadcast. If bumping resources
// (either claimed output value or external utxo), it will keep increasing until holder
Expand DownExpand Up@@ -834,17 +841,17 @@ impl PackageTemplate {
// Now check that we only merge packages if they are both unpinnable or both
// pinnable.
let self_pinnable = self_cluster == AggregationCluster::Pinnable ||
self.counterparty_spendable_height() <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
self.counterparty_spendable_height <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
let other_pinnable = other_cluster == AggregationCluster::Pinnable ||
other.counterparty_spendable_height() <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
other.counterparty_spendable_height <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
if self_pinnable && other_pinnable {
return true;
}

let self_unpinnable = self_cluster == AggregationCluster::Unpinnable &&
self.counterparty_spendable_height() > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
self.counterparty_spendable_height > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
let other_unpinnable = other_cluster == AggregationCluster::Unpinnable &&
other.counterparty_spendable_height() > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
other.counterparty_spendable_height > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
if self_unpinnable && other_unpinnable {
return true;
}
Expand All@@ -855,13 +862,6 @@ impl PackageTemplate {
pub(crate) fn is_malleable(&self) -> bool {
matches!(self.malleability, PackageMalleability::Malleable(..))
}
/// The height at which our counterparty may be able to spend this output.
///
/// This is an important limit for aggregation as after this height our counterparty may be
/// able to pin transactions spending this output in the mempool.
pub(crate) fn counterparty_spendable_height(&self) -> u32 {
self.counterparty_spendable_height
}
pub(crate) fn previous_feerate(&self) -> u64 {
self.feerate_previous
}
Expand DownExpand Up@@ -1225,6 +1225,18 @@ impl Readable for PackageTemplate {
(4, _height_original, option), // Written with a dummy value since 0.1
(6, height_timer, option),
});
for (_, input) in &inputs {
if let PackageSolvingData::RevokedHTLCOutput(RevokedHTLCOutput { htlc, .. }) = input {
// LDK versions through 0.1 set the wrong counterparty_spendable_height for
// non-offered revoked HTLCs (ie HTLCs we sent to our counterparty which they can
// claim with a preimage immediately). Here we detect this and reset the value to
// zero, as the value is unused except for merging decisions which doesn't care
// about any values below the current height.
if !htlc.offered && htlc.cltv_expiry == counterparty_spendable_height {
counterparty_spendable_height = 0;
}
}
}
Ok(PackageTemplate {
inputs,
malleability,
Expand Down
4 changes: 2 additions & 2 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -13364,8 +13364,8 @@ where
// claim.
// Note that a `ChannelMonitor` is created with `update_id` 0 and after we
// provide it with a closure update its `update_id` will be at 1.
if !monitor.offchain_closed() || monitor.get_latest_update_id() > 1 {
should_queue_fc_update = !monitor.offchain_closed();
if !monitor.no_further_updates_allowed() || monitor.get_latest_update_id() > 1 {
should_queue_fc_update = !monitor.no_further_updates_allowed();
let mut latest_update_id = monitor.get_latest_update_id();
if should_queue_fc_update {
latest_update_id += 1;
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' 0.1 Backports for 0.1.2 by TheBlueMatt · Pull Request #3613 · lightningdevkit/rust-lightning · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e8a426c
Use fixed-size Vec allocations for BOLT12 messages
jkczyz Jan 28, 2025
f80d82e
Drop use of reserve_exact in BOLT12 tests
jkczyz Feb 6, 2025
24731d4
fix historical liquidity bucket decay
joostjager Feb 3, 2025
58a7b79
Drop `counterparty_spendable_height` accessor
TheBlueMatt Jan 27, 2025
2e80fbd
Set correct `counterparty_spendable_height` on c.p. revoked HTLCs
TheBlueMatt Jan 27, 2025
174f42e
Mark counterparty revoked offered HTLCs as `Unpinnable`
TheBlueMatt Jan 27, 2025
b5b6429
Call `peer_disconnected` after a handler refuses a connection
TheBlueMatt Jan 30, 2025
8eb3bd7
Avoid parsing `PublicKey`s when applying an unsigned chan update
TheBlueMatt Jan 31, 2025
63fa918
Properly pre-allocate `NetworkGraph` channel/node maps
TheBlueMatt Jan 31, 2025
bed125a
Ignore fees on first-hop channels from the public network graph
TheBlueMatt Feb 3, 2025
fd8eab9
Clean up `long_mpp_route_test` and `mpp_cheaper_route_test`
TheBlueMatt Feb 3, 2025
e46489d
Track `node_counter` in `RouteGraphNode`
TheBlueMatt Feb 3, 2025
b39aef6
Prefer higher-value, shorter equal-cost paths when routing
TheBlueMatt Feb 3, 2025
1976fb8
Move last-hop route handling to the common "normal" hop codepath
TheBlueMatt Feb 3, 2025
1beefb1
Add direct hops to intros after all blinded paths in pathfinding
TheBlueMatt Feb 2, 2025
ae4279d
Add `PathBuildingHop::best_path_from_hop_selected`
TheBlueMatt Feb 3, 2025
1788f80
Replace a few router `expect`s with `debug_assert` + `Err`-returns
TheBlueMatt Feb 10, 2025
bd56ddf
More completely ignore route hints which are for our own channels
TheBlueMatt Feb 10, 2025
fa8d599
Add SemVer compatibility checks to CI
tnull Jan 24, 2025
9d2449a
Fix overflow in historical scoring model point count summation
TheBlueMatt Feb 23, 2025
7c56f21
Cancel claims signed by a remote `ChannelMonitor` when reorging
TheBlueMatt Feb 25, 2025
653c482
Drop return value from `provide_latest_holder_commitment_tx`
TheBlueMatt Feb 25, 2025
6749a07
Only generate a post-close lock ChannelMonitorUpdate if we need one
TheBlueMatt Feb 25, 2025
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
57 changes: 57 additions & 0 deletions .github/workflows/semver.yml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
name: SemVer checks
on:
push:
branches-ignore:
- master
pull_request:
branches-ignore:
- master

jobs:
semver-checks:
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@v4
- name: Install Rust stable toolchain
run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile=minimal --default-toolchain stable
rustup override set stable
- name: Check SemVer with default features
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
feature-group: default-features
- name: Check SemVer *without* default features
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
feature-group: only-explicit-features
- name: Check lightning-background-processor SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
package: lightning-background-processor
feature-group: only-explicit-features
features: futures
- name: Check lightning-block-sync SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
package: lightning-block-sync
feature-group: only-explicit-features
features: rpc-client,rest-client
- name: Check lightning-transaction-sync electrum SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
manifest-path: lightning-transaction-sync/Cargo.toml
feature-group: only-explicit-features
features: electrum
- name: Check lightning-transaction-sync esplora-blocking SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
manifest-path: lightning-transaction-sync/Cargo.toml
feature-group: only-explicit-features
features: esplora-blocking
- name: Check lightning-transaction-sync esplora-async SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
manifest-path: lightning-transaction-sync/Cargo.toml
feature-group: only-explicit-features
features: esplora-async
4 changes: 2 additions & 2 deletions lightning-background-processor/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -2379,8 +2379,8 @@ mod tests {
42,
53,
features,
$nodes[0].node.get_our_node_id(),
$nodes[1].node.get_our_node_id(),
$nodes[0].node.get_our_node_id().into(),
$nodes[1].node.get_our_node_id().into(),
)
.expect("Failed to update channel from partial announcement");
let original_graph_description = $nodes[0].network_graph.to_string();
Expand Down
90 changes: 46 additions & 44 deletions lightning/src/chain/channelmonitor.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1509,8 +1509,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
fn provide_latest_holder_commitment_tx(
&self, holder_commitment_tx: HolderCommitmentTransaction,
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,
) -> Result<(), ()> {
self.inner.lock().unwrap().provide_latest_holder_commitment_tx(holder_commitment_tx, htlc_outputs, &Vec::new(), Vec::new()).map_err(|_| ())
) {
self.inner.lock().unwrap().provide_latest_holder_commitment_tx(holder_commitment_tx, htlc_outputs, &Vec::new(), Vec::new())
}

/// This is used to provide payment preimage(s) out-of-band during startup without updating the
Expand DownExpand Up@@ -1737,10 +1737,14 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
self.inner.lock().unwrap().get_cur_holder_commitment_number()
}

/// Gets whether we've been notified that this channel is closed by the `ChannelManager` (i.e.
/// via a [`ChannelMonitorUpdateStep::ChannelForceClosed`]).
pub(crate) fn offchain_closed(&self) -> bool {
self.inner.lock().unwrap().lockdown_from_offchain
/// Fetches whether this monitor has marked the channel as closed and will refuse any further
/// updates to the commitment transactions.
///
/// It can be marked closed in a few different ways, including via a
/// [`ChannelMonitorUpdateStep::ChannelForceClosed`] or if the channel has been closed
/// on-chain.
pub(crate) fn no_further_updates_allowed(&self) -> bool {
self.inner.lock().unwrap().no_further_updates_allowed()
}

/// Gets the `node_id` of the counterparty for this channel.
Expand DownExpand Up@@ -2901,7 +2905,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
/// is important that any clones of this channel monitor (including remote clones) by kept
/// up-to-date as our holder commitment transaction is updated.
/// Panics if set_on_holder_tx_csv has never been called.
fn provide_latest_holder_commitment_tx(&mut self, holder_commitment_tx: HolderCommitmentTransaction, mut htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>, claimed_htlcs: &[(SentHTLCId, PaymentPreimage)], nondust_htlc_sources: Vec<HTLCSource>) -> Result<(), &'static str> {
fn provide_latest_holder_commitment_tx(&mut self, holder_commitment_tx: HolderCommitmentTransaction, mut htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>, claimed_htlcs: &[(SentHTLCId, PaymentPreimage)], nondust_htlc_sources: Vec<HTLCSource>) {
if htlc_outputs.iter().any(|(_, s, _)| s.is_some()) {
// If we have non-dust HTLCs in htlc_outputs, ensure they match the HTLCs in the
// `holder_commitment_tx`. In the future, we'll no longer provide the redundant data
Expand DownExpand Up@@ -2978,10 +2982,6 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
self.counterparty_fulfilled_htlcs.insert(*claimed_htlc_id, *claimed_preimage);
}
if self.holder_tx_signed {
return Err("Latest holder commitment signed has already been signed, update is rejected");
}
Ok(())
}

/// Provides a payment_hash->payment_preimage mapping. Will be automatically pruned when all
Expand DownExpand Up@@ -3202,11 +3202,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { commitment_tx, htlc_outputs, claimed_htlcs, nondust_htlc_sources } => {
log_trace!(logger, "Updating ChannelMonitor with latest holder commitment transaction info");
if self.lockdown_from_offchain { panic!(); }
if let Err(e) = self.provide_latest_holder_commitment_tx(commitment_tx.clone(), htlc_outputs.clone(), &claimed_htlcs, nondust_htlc_sources.clone()) {
log_error!(logger, "Providing latest holder commitment transaction failed/was refused:");
log_error!(logger, " {}", e);
ret = Err(());
}
self.provide_latest_holder_commitment_tx(commitment_tx.clone(), htlc_outputs.clone(), &claimed_htlcs, nondust_htlc_sources.clone());
}
ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { commitment_txid, htlc_outputs, commitment_number, their_per_commitment_point, .. } => {
log_trace!(logger, "Updating ChannelMonitor with latest counterparty commitment transaction info");
Expand DownExpand Up@@ -3286,12 +3282,16 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
}

if ret.is_ok() && (self.funding_spend_seen || self.lockdown_from_offchain || self.holder_tx_signed) && is_pre_close_update {
if ret.is_ok() && self.no_further_updates_allowed() && is_pre_close_update {
log_error!(logger, "Refusing Channel Monitor Update as counterparty attempted to update commitment after funding was spent");
Err(())
} else { ret }
}

fn no_further_updates_allowed(&self) -> bool {
self.funding_spend_seen || self.lockdown_from_offchain || self.holder_tx_signed
}

fn get_latest_update_id(&self) -> u64 {
self.latest_update_id
}
Expand DownExpand Up@@ -3564,11 +3564,16 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
return (claimable_outpoints, to_counterparty_output_info);
}
let revk_htlc_outp = RevokedHTLCOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, htlc.amount_msat / 1000, htlc.clone(), &self.onchain_tx_handler.channel_transaction_parameters.channel_type_features);
let counterparty_spendable_height = if htlc.offered {
htlc.cltv_expiry
} else {
height
};
let justice_package = PackageTemplate::build_package(
commitment_txid,
transaction_output_index,
PackageSolvingData::RevokedHTLCOutput(revk_htlc_outp),
htlc.cltv_expiry,
counterparty_spendable_height,
);
claimable_outpoints.push(justice_package);
}
Expand DownExpand Up@@ -3869,35 +3874,32 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
}
}
if self.holder_tx_signed {
// If we've signed, we may have broadcast either commitment (prev or current), and
// attempted to claim from it immediately without waiting for a confirmation.
if self.current_holder_commitment_tx.txid != *confirmed_commitment_txid {
// Cancel any pending claims for any holder commitments in case they had previously
// confirmed or been signed (in which case we will start attempting to claim without
// waiting for confirmation).
if self.current_holder_commitment_tx.txid != *confirmed_commitment_txid {
log_trace!(logger, "Canceling claims for previously broadcast holder commitment {}",
self.current_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: self.current_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &self.current_holder_commitment_tx.htlc_outputs {
if let Some(vout) = htlc.transaction_output_index {
outpoint.vout = vout;
self.onchain_tx_handler.abandon_claim(&outpoint);
}
}
}
if let Some(prev_holder_commitment_tx) = &self.prev_holder_signed_commitment_tx {
if prev_holder_commitment_tx.txid != *confirmed_commitment_txid {
log_trace!(logger, "Canceling claims for previously broadcast holder commitment {}",
self.current_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: self.current_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &self.current_holder_commitment_tx.htlc_outputs {
prev_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: prev_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &prev_holder_commitment_tx.htlc_outputs {
if let Some(vout) = htlc.transaction_output_index {
outpoint.vout = vout;
self.onchain_tx_handler.abandon_claim(&outpoint);
}
}
}
if let Some(prev_holder_commitment_tx) = &self.prev_holder_signed_commitment_tx {
if prev_holder_commitment_tx.txid != *confirmed_commitment_txid {
log_trace!(logger, "Canceling claims for previously broadcast holder commitment {}",
prev_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: prev_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &prev_holder_commitment_tx.htlc_outputs {
if let Some(vout) = htlc.transaction_output_index {
outpoint.vout = vout;
self.onchain_tx_handler.abandon_claim(&outpoint);
}
}
}
}
} else {
// No previous claim.
}
}

Expand DownExpand Up@@ -4233,7 +4235,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
}

if self.lockdown_from_offchain || self.funding_spend_seen || self.holder_tx_signed {
if self.no_further_updates_allowed() {
// Fail back HTLCs on backwards channels if they expire within
// `LATENCY_GRACE_PERIOD_BLOCKS` blocks and the channel is closed (i.e. we're at a
// point where no further off-chain updates will be accepted). If we haven't seen the
Expand DownExpand Up@@ -5384,7 +5386,7 @@ mod tests {
let dummy_commitment_tx = HolderCommitmentTransaction::dummy(&mut htlcs);

monitor.provide_latest_holder_commitment_tx(dummy_commitment_tx.clone(),
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect()).unwrap();
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect());
monitor.provide_latest_counterparty_commitment_tx(Txid::from_byte_array(Sha256::hash(b"1").to_byte_array()),
preimages_slice_to_htlc_outputs!(preimages[5..15]), 281474976710655, dummy_key, &logger);
monitor.provide_latest_counterparty_commitment_tx(Txid::from_byte_array(Sha256::hash(b"2").to_byte_array()),
Expand DownExpand Up@@ -5422,7 +5424,7 @@ mod tests {
let mut htlcs = preimages_slice_to_htlcs!(preimages[0..5]);
let dummy_commitment_tx = HolderCommitmentTransaction::dummy(&mut htlcs);
monitor.provide_latest_holder_commitment_tx(dummy_commitment_tx.clone(),
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect()).unwrap();
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect());
secret[0..32].clone_from_slice(&<Vec<u8>>::from_hex("2273e227a5b7449b6e70f1fb4652864038b1cbf9cd7c043a7d6456b7fc275ad8").unwrap());
monitor.provide_secret(281474976710653, secret.clone()).unwrap();
assert_eq!(monitor.inner.lock().unwrap().payment_preimages.len(), 12);
Expand All@@ -5433,7 +5435,7 @@ mod tests {
let mut htlcs = preimages_slice_to_htlcs!(preimages[0..3]);
let dummy_commitment_tx = HolderCommitmentTransaction::dummy(&mut htlcs);
monitor.provide_latest_holder_commitment_tx(dummy_commitment_tx,
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect()).unwrap();
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect());
secret[0..32].clone_from_slice(&<Vec<u8>>::from_hex("27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116").unwrap());
monitor.provide_secret(281474976710652, secret.clone()).unwrap();
assert_eq!(monitor.inner.lock().unwrap().payment_preimages.len(), 5);
Expand Down
46 changes: 29 additions & 17 deletions lightning/src/chain/package.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -699,8 +699,13 @@ impl PackageSolvingData {
match self {
PackageSolvingData::RevokedOutput(RevokedOutput { .. }) =>
PackageMalleability::Malleable(AggregationCluster::Unpinnable),
PackageSolvingData::RevokedHTLCOutput(..) =>
PackageMalleability::Malleable(AggregationCluster::Pinnable),
PackageSolvingData::RevokedHTLCOutput(RevokedHTLCOutput { htlc, .. }) => {
if htlc.offered {
PackageMalleability::Malleable(AggregationCluster::Unpinnable)
} else {
PackageMalleability::Malleable(AggregationCluster::Pinnable)
}
},
PackageSolvingData::CounterpartyOfferedHTLCOutput(..) =>
PackageMalleability::Malleable(AggregationCluster::Unpinnable),
PackageSolvingData::CounterpartyReceivedHTLCOutput(..) =>
Expand DownExpand Up@@ -771,10 +776,12 @@ pub struct PackageTemplate {
/// Block height at which our counterparty can potentially claim this output as well (assuming
/// they have the keys or information required to do so).
///
/// This is used primarily by external consumers to decide when an output becomes "pinnable"
/// because the counterparty can potentially spend it. It is also used internally by
/// [`Self::get_height_timer`] to identify when an output must be claimed by, depending on the
/// type of output.
/// This is used primarily to decide when an output becomes "pinnable" because the counterparty
/// can potentially spend it. It is also used internally by [`Self::get_height_timer`] to
/// identify when an output must be claimed by, depending on the type of output.
///
/// Note that for revoked counterparty HTLC outputs the value may be zero in some cases where
/// we upgraded from LDK 0.1 or prior.
counterparty_spendable_height: u32,
// Cache of package feerate committed at previous (re)broadcast. If bumping resources
// (either claimed output value or external utxo), it will keep increasing until holder
Expand DownExpand Up@@ -834,17 +841,17 @@ impl PackageTemplate {
// Now check that we only merge packages if they are both unpinnable or both
// pinnable.
let self_pinnable = self_cluster == AggregationCluster::Pinnable ||
self.counterparty_spendable_height() <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
self.counterparty_spendable_height <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
let other_pinnable = other_cluster == AggregationCluster::Pinnable ||
other.counterparty_spendable_height() <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
other.counterparty_spendable_height <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
if self_pinnable && other_pinnable {
return true;
}

let self_unpinnable = self_cluster == AggregationCluster::Unpinnable &&
self.counterparty_spendable_height() > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
self.counterparty_spendable_height > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
let other_unpinnable = other_cluster == AggregationCluster::Unpinnable &&
other.counterparty_spendable_height() > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
other.counterparty_spendable_height > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
if self_unpinnable && other_unpinnable {
return true;
}
Expand All@@ -855,13 +862,6 @@ impl PackageTemplate {
pub(crate) fn is_malleable(&self) -> bool {
matches!(self.malleability, PackageMalleability::Malleable(..))
}
/// The height at which our counterparty may be able to spend this output.
///
/// This is an important limit for aggregation as after this height our counterparty may be
/// able to pin transactions spending this output in the mempool.
pub(crate) fn counterparty_spendable_height(&self) -> u32 {
self.counterparty_spendable_height
}
pub(crate) fn previous_feerate(&self) -> u64 {
self.feerate_previous
}
Expand DownExpand Up@@ -1225,6 +1225,18 @@ impl Readable for PackageTemplate {
(4, _height_original, option), // Written with a dummy value since 0.1
(6, height_timer, option),
});
for (_, input) in &inputs {
if let PackageSolvingData::RevokedHTLCOutput(RevokedHTLCOutput { htlc, .. }) = input {
// LDK versions through 0.1 set the wrong counterparty_spendable_height for
// non-offered revoked HTLCs (ie HTLCs we sent to our counterparty which they can
// claim with a preimage immediately). Here we detect this and reset the value to
// zero, as the value is unused except for merging decisions which doesn't care
// about any values below the current height.
if !htlc.offered && htlc.cltv_expiry == counterparty_spendable_height {
counterparty_spendable_height = 0;
}
}
}
Ok(PackageTemplate {
inputs,
malleability,
Expand Down
4 changes: 2 additions & 2 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -13364,8 +13364,8 @@ where
// claim.
// Note that a `ChannelMonitor` is created with `update_id` 0 and after we
// provide it with a closure update its `update_id` will be at 1.
if !monitor.offchain_closed() || monitor.get_latest_update_id() > 1 {
should_queue_fc_update = !monitor.offchain_closed();
if !monitor.no_further_updates_allowed() || monitor.get_latest_update_id() > 1 {
should_queue_fc_update = !monitor.no_further_updates_allowed();
let mut latest_update_id = monitor.get_latest_update_id();
if should_queue_fc_update {
latest_update_id += 1;
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' 0.1 Backports for 0.1.2 by TheBlueMatt · Pull Request #3613 · lightningdevkit/rust-lightning · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e8a426c
Use fixed-size Vec allocations for BOLT12 messages
jkczyz Jan 28, 2025
f80d82e
Drop use of reserve_exact in BOLT12 tests
jkczyz Feb 6, 2025
24731d4
fix historical liquidity bucket decay
joostjager Feb 3, 2025
58a7b79
Drop `counterparty_spendable_height` accessor
TheBlueMatt Jan 27, 2025
2e80fbd
Set correct `counterparty_spendable_height` on c.p. revoked HTLCs
TheBlueMatt Jan 27, 2025
174f42e
Mark counterparty revoked offered HTLCs as `Unpinnable`
TheBlueMatt Jan 27, 2025
b5b6429
Call `peer_disconnected` after a handler refuses a connection
TheBlueMatt Jan 30, 2025
8eb3bd7
Avoid parsing `PublicKey`s when applying an unsigned chan update
TheBlueMatt Jan 31, 2025
63fa918
Properly pre-allocate `NetworkGraph` channel/node maps
TheBlueMatt Jan 31, 2025
bed125a
Ignore fees on first-hop channels from the public network graph
TheBlueMatt Feb 3, 2025
fd8eab9
Clean up `long_mpp_route_test` and `mpp_cheaper_route_test`
TheBlueMatt Feb 3, 2025
e46489d
Track `node_counter` in `RouteGraphNode`
TheBlueMatt Feb 3, 2025
b39aef6
Prefer higher-value, shorter equal-cost paths when routing
TheBlueMatt Feb 3, 2025
1976fb8
Move last-hop route handling to the common "normal" hop codepath
TheBlueMatt Feb 3, 2025
1beefb1
Add direct hops to intros after all blinded paths in pathfinding
TheBlueMatt Feb 2, 2025
ae4279d
Add `PathBuildingHop::best_path_from_hop_selected`
TheBlueMatt Feb 3, 2025
1788f80
Replace a few router `expect`s with `debug_assert` + `Err`-returns
TheBlueMatt Feb 10, 2025
bd56ddf
More completely ignore route hints which are for our own channels
TheBlueMatt Feb 10, 2025
fa8d599
Add SemVer compatibility checks to CI
tnull Jan 24, 2025
9d2449a
Fix overflow in historical scoring model point count summation
TheBlueMatt Feb 23, 2025
7c56f21
Cancel claims signed by a remote `ChannelMonitor` when reorging
TheBlueMatt Feb 25, 2025
653c482
Drop return value from `provide_latest_holder_commitment_tx`
TheBlueMatt Feb 25, 2025
6749a07
Only generate a post-close lock ChannelMonitorUpdate if we need one
TheBlueMatt Feb 25, 2025
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
57 changes: 57 additions & 0 deletions .github/workflows/semver.yml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
name: SemVer checks
on:
push:
branches-ignore:
- master
pull_request:
branches-ignore:
- master

jobs:
semver-checks:
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@v4
- name: Install Rust stable toolchain
run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile=minimal --default-toolchain stable
rustup override set stable
- name: Check SemVer with default features
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
feature-group: default-features
- name: Check SemVer *without* default features
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
feature-group: only-explicit-features
- name: Check lightning-background-processor SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
package: lightning-background-processor
feature-group: only-explicit-features
features: futures
- name: Check lightning-block-sync SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
package: lightning-block-sync
feature-group: only-explicit-features
features: rpc-client,rest-client
- name: Check lightning-transaction-sync electrum SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
manifest-path: lightning-transaction-sync/Cargo.toml
feature-group: only-explicit-features
features: electrum
- name: Check lightning-transaction-sync esplora-blocking SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
manifest-path: lightning-transaction-sync/Cargo.toml
feature-group: only-explicit-features
features: esplora-blocking
- name: Check lightning-transaction-sync esplora-async SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
manifest-path: lightning-transaction-sync/Cargo.toml
feature-group: only-explicit-features
features: esplora-async
4 changes: 2 additions & 2 deletions lightning-background-processor/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -2379,8 +2379,8 @@ mod tests {
42,
53,
features,
$nodes[0].node.get_our_node_id(),
$nodes[1].node.get_our_node_id(),
$nodes[0].node.get_our_node_id().into(),
$nodes[1].node.get_our_node_id().into(),
)
.expect("Failed to update channel from partial announcement");
let original_graph_description = $nodes[0].network_graph.to_string();
Expand Down
90 changes: 46 additions & 44 deletions lightning/src/chain/channelmonitor.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1509,8 +1509,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
fn provide_latest_holder_commitment_tx(
&self, holder_commitment_tx: HolderCommitmentTransaction,
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,
) -> Result<(), ()> {
self.inner.lock().unwrap().provide_latest_holder_commitment_tx(holder_commitment_tx, htlc_outputs, &Vec::new(), Vec::new()).map_err(|_| ())
) {
self.inner.lock().unwrap().provide_latest_holder_commitment_tx(holder_commitment_tx, htlc_outputs, &Vec::new(), Vec::new())
}

/// This is used to provide payment preimage(s) out-of-band during startup without updating the
Expand DownExpand Up@@ -1737,10 +1737,14 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
self.inner.lock().unwrap().get_cur_holder_commitment_number()
}

/// Gets whether we've been notified that this channel is closed by the `ChannelManager` (i.e.
/// via a [`ChannelMonitorUpdateStep::ChannelForceClosed`]).
pub(crate) fn offchain_closed(&self) -> bool {
self.inner.lock().unwrap().lockdown_from_offchain
/// Fetches whether this monitor has marked the channel as closed and will refuse any further
/// updates to the commitment transactions.
///
/// It can be marked closed in a few different ways, including via a
/// [`ChannelMonitorUpdateStep::ChannelForceClosed`] or if the channel has been closed
/// on-chain.
pub(crate) fn no_further_updates_allowed(&self) -> bool {
self.inner.lock().unwrap().no_further_updates_allowed()
}

/// Gets the `node_id` of the counterparty for this channel.
Expand DownExpand Up@@ -2901,7 +2905,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
/// is important that any clones of this channel monitor (including remote clones) by kept
/// up-to-date as our holder commitment transaction is updated.
/// Panics if set_on_holder_tx_csv has never been called.
fn provide_latest_holder_commitment_tx(&mut self, holder_commitment_tx: HolderCommitmentTransaction, mut htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>, claimed_htlcs: &[(SentHTLCId, PaymentPreimage)], nondust_htlc_sources: Vec<HTLCSource>) -> Result<(), &'static str> {
fn provide_latest_holder_commitment_tx(&mut self, holder_commitment_tx: HolderCommitmentTransaction, mut htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>, claimed_htlcs: &[(SentHTLCId, PaymentPreimage)], nondust_htlc_sources: Vec<HTLCSource>) {
if htlc_outputs.iter().any(|(_, s, _)| s.is_some()) {
// If we have non-dust HTLCs in htlc_outputs, ensure they match the HTLCs in the
// `holder_commitment_tx`. In the future, we'll no longer provide the redundant data
Expand DownExpand Up@@ -2978,10 +2982,6 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
self.counterparty_fulfilled_htlcs.insert(*claimed_htlc_id, *claimed_preimage);
}
if self.holder_tx_signed {
return Err("Latest holder commitment signed has already been signed, update is rejected");
}
Ok(())
}

/// Provides a payment_hash->payment_preimage mapping. Will be automatically pruned when all
Expand DownExpand Up@@ -3202,11 +3202,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { commitment_tx, htlc_outputs, claimed_htlcs, nondust_htlc_sources } => {
log_trace!(logger, "Updating ChannelMonitor with latest holder commitment transaction info");
if self.lockdown_from_offchain { panic!(); }
if let Err(e) = self.provide_latest_holder_commitment_tx(commitment_tx.clone(), htlc_outputs.clone(), &claimed_htlcs, nondust_htlc_sources.clone()) {
log_error!(logger, "Providing latest holder commitment transaction failed/was refused:");
log_error!(logger, " {}", e);
ret = Err(());
}
self.provide_latest_holder_commitment_tx(commitment_tx.clone(), htlc_outputs.clone(), &claimed_htlcs, nondust_htlc_sources.clone());
}
ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { commitment_txid, htlc_outputs, commitment_number, their_per_commitment_point, .. } => {
log_trace!(logger, "Updating ChannelMonitor with latest counterparty commitment transaction info");
Expand DownExpand Up@@ -3286,12 +3282,16 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
}

if ret.is_ok() && (self.funding_spend_seen || self.lockdown_from_offchain || self.holder_tx_signed) && is_pre_close_update {
if ret.is_ok() && self.no_further_updates_allowed() && is_pre_close_update {
log_error!(logger, "Refusing Channel Monitor Update as counterparty attempted to update commitment after funding was spent");
Err(())
} else { ret }
}

fn no_further_updates_allowed(&self) -> bool {
self.funding_spend_seen || self.lockdown_from_offchain || self.holder_tx_signed
}

fn get_latest_update_id(&self) -> u64 {
self.latest_update_id
}
Expand DownExpand Up@@ -3564,11 +3564,16 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
return (claimable_outpoints, to_counterparty_output_info);
}
let revk_htlc_outp = RevokedHTLCOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, htlc.amount_msat / 1000, htlc.clone(), &self.onchain_tx_handler.channel_transaction_parameters.channel_type_features);
let counterparty_spendable_height = if htlc.offered {
htlc.cltv_expiry
} else {
height
};
let justice_package = PackageTemplate::build_package(
commitment_txid,
transaction_output_index,
PackageSolvingData::RevokedHTLCOutput(revk_htlc_outp),
htlc.cltv_expiry,
counterparty_spendable_height,
);
claimable_outpoints.push(justice_package);
}
Expand DownExpand Up@@ -3869,35 +3874,32 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
}
}
if self.holder_tx_signed {
// If we've signed, we may have broadcast either commitment (prev or current), and
// attempted to claim from it immediately without waiting for a confirmation.
if self.current_holder_commitment_tx.txid != *confirmed_commitment_txid {
// Cancel any pending claims for any holder commitments in case they had previously
// confirmed or been signed (in which case we will start attempting to claim without
// waiting for confirmation).
if self.current_holder_commitment_tx.txid != *confirmed_commitment_txid {
log_trace!(logger, "Canceling claims for previously broadcast holder commitment {}",
self.current_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: self.current_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &self.current_holder_commitment_tx.htlc_outputs {
if let Some(vout) = htlc.transaction_output_index {
outpoint.vout = vout;
self.onchain_tx_handler.abandon_claim(&outpoint);
}
}
}
if let Some(prev_holder_commitment_tx) = &self.prev_holder_signed_commitment_tx {
if prev_holder_commitment_tx.txid != *confirmed_commitment_txid {
log_trace!(logger, "Canceling claims for previously broadcast holder commitment {}",
self.current_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: self.current_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &self.current_holder_commitment_tx.htlc_outputs {
prev_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: prev_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &prev_holder_commitment_tx.htlc_outputs {
if let Some(vout) = htlc.transaction_output_index {
outpoint.vout = vout;
self.onchain_tx_handler.abandon_claim(&outpoint);
}
}
}
if let Some(prev_holder_commitment_tx) = &self.prev_holder_signed_commitment_tx {
if prev_holder_commitment_tx.txid != *confirmed_commitment_txid {
log_trace!(logger, "Canceling claims for previously broadcast holder commitment {}",
prev_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: prev_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &prev_holder_commitment_tx.htlc_outputs {
if let Some(vout) = htlc.transaction_output_index {
outpoint.vout = vout;
self.onchain_tx_handler.abandon_claim(&outpoint);
}
}
}
}
} else {
// No previous claim.
}
}

Expand DownExpand Up@@ -4233,7 +4235,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
}

if self.lockdown_from_offchain || self.funding_spend_seen || self.holder_tx_signed {
if self.no_further_updates_allowed() {
// Fail back HTLCs on backwards channels if they expire within
// `LATENCY_GRACE_PERIOD_BLOCKS` blocks and the channel is closed (i.e. we're at a
// point where no further off-chain updates will be accepted). If we haven't seen the
Expand DownExpand Up@@ -5384,7 +5386,7 @@ mod tests {
let dummy_commitment_tx = HolderCommitmentTransaction::dummy(&mut htlcs);

monitor.provide_latest_holder_commitment_tx(dummy_commitment_tx.clone(),
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect()).unwrap();
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect());
monitor.provide_latest_counterparty_commitment_tx(Txid::from_byte_array(Sha256::hash(b"1").to_byte_array()),
preimages_slice_to_htlc_outputs!(preimages[5..15]), 281474976710655, dummy_key, &logger);
monitor.provide_latest_counterparty_commitment_tx(Txid::from_byte_array(Sha256::hash(b"2").to_byte_array()),
Expand DownExpand Up@@ -5422,7 +5424,7 @@ mod tests {
let mut htlcs = preimages_slice_to_htlcs!(preimages[0..5]);
let dummy_commitment_tx = HolderCommitmentTransaction::dummy(&mut htlcs);
monitor.provide_latest_holder_commitment_tx(dummy_commitment_tx.clone(),
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect()).unwrap();
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect());
secret[0..32].clone_from_slice(&<Vec<u8>>::from_hex("2273e227a5b7449b6e70f1fb4652864038b1cbf9cd7c043a7d6456b7fc275ad8").unwrap());
monitor.provide_secret(281474976710653, secret.clone()).unwrap();
assert_eq!(monitor.inner.lock().unwrap().payment_preimages.len(), 12);
Expand All@@ -5433,7 +5435,7 @@ mod tests {
let mut htlcs = preimages_slice_to_htlcs!(preimages[0..3]);
let dummy_commitment_tx = HolderCommitmentTransaction::dummy(&mut htlcs);
monitor.provide_latest_holder_commitment_tx(dummy_commitment_tx,
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect()).unwrap();
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect());
secret[0..32].clone_from_slice(&<Vec<u8>>::from_hex("27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116").unwrap());
monitor.provide_secret(281474976710652, secret.clone()).unwrap();
assert_eq!(monitor.inner.lock().unwrap().payment_preimages.len(), 5);
Expand Down
46 changes: 29 additions & 17 deletions lightning/src/chain/package.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -699,8 +699,13 @@ impl PackageSolvingData {
match self {
PackageSolvingData::RevokedOutput(RevokedOutput { .. }) =>
PackageMalleability::Malleable(AggregationCluster::Unpinnable),
PackageSolvingData::RevokedHTLCOutput(..) =>
PackageMalleability::Malleable(AggregationCluster::Pinnable),
PackageSolvingData::RevokedHTLCOutput(RevokedHTLCOutput { htlc, .. }) => {
if htlc.offered {
PackageMalleability::Malleable(AggregationCluster::Unpinnable)
} else {
PackageMalleability::Malleable(AggregationCluster::Pinnable)
}
},
PackageSolvingData::CounterpartyOfferedHTLCOutput(..) =>
PackageMalleability::Malleable(AggregationCluster::Unpinnable),
PackageSolvingData::CounterpartyReceivedHTLCOutput(..) =>
Expand DownExpand Up@@ -771,10 +776,12 @@ pub struct PackageTemplate {
/// Block height at which our counterparty can potentially claim this output as well (assuming
/// they have the keys or information required to do so).
///
/// This is used primarily by external consumers to decide when an output becomes "pinnable"
/// because the counterparty can potentially spend it. It is also used internally by
/// [`Self::get_height_timer`] to identify when an output must be claimed by, depending on the
/// type of output.
/// This is used primarily to decide when an output becomes "pinnable" because the counterparty
/// can potentially spend it. It is also used internally by [`Self::get_height_timer`] to
/// identify when an output must be claimed by, depending on the type of output.
///
/// Note that for revoked counterparty HTLC outputs the value may be zero in some cases where
/// we upgraded from LDK 0.1 or prior.
counterparty_spendable_height: u32,
// Cache of package feerate committed at previous (re)broadcast. If bumping resources
// (either claimed output value or external utxo), it will keep increasing until holder
Expand DownExpand Up@@ -834,17 +841,17 @@ impl PackageTemplate {
// Now check that we only merge packages if they are both unpinnable or both
// pinnable.
let self_pinnable = self_cluster == AggregationCluster::Pinnable ||
self.counterparty_spendable_height() <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
self.counterparty_spendable_height <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
let other_pinnable = other_cluster == AggregationCluster::Pinnable ||
other.counterparty_spendable_height() <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
other.counterparty_spendable_height <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
if self_pinnable && other_pinnable {
return true;
}

let self_unpinnable = self_cluster == AggregationCluster::Unpinnable &&
self.counterparty_spendable_height() > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
self.counterparty_spendable_height > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
let other_unpinnable = other_cluster == AggregationCluster::Unpinnable &&
other.counterparty_spendable_height() > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
other.counterparty_spendable_height > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
if self_unpinnable && other_unpinnable {
return true;
}
Expand All@@ -855,13 +862,6 @@ impl PackageTemplate {
pub(crate) fn is_malleable(&self) -> bool {
matches!(self.malleability, PackageMalleability::Malleable(..))
}
/// The height at which our counterparty may be able to spend this output.
///
/// This is an important limit for aggregation as after this height our counterparty may be
/// able to pin transactions spending this output in the mempool.
pub(crate) fn counterparty_spendable_height(&self) -> u32 {
self.counterparty_spendable_height
}
pub(crate) fn previous_feerate(&self) -> u64 {
self.feerate_previous
}
Expand DownExpand Up@@ -1225,6 +1225,18 @@ impl Readable for PackageTemplate {
(4, _height_original, option), // Written with a dummy value since 0.1
(6, height_timer, option),
});
for (_, input) in &inputs {
if let PackageSolvingData::RevokedHTLCOutput(RevokedHTLCOutput { htlc, .. }) = input {
// LDK versions through 0.1 set the wrong counterparty_spendable_height for
// non-offered revoked HTLCs (ie HTLCs we sent to our counterparty which they can
// claim with a preimage immediately). Here we detect this and reset the value to
// zero, as the value is unused except for merging decisions which doesn't care
// about any values below the current height.
if !htlc.offered && htlc.cltv_expiry == counterparty_spendable_height {
counterparty_spendable_height = 0;
}
}
}
Ok(PackageTemplate {
inputs,
malleability,
Expand Down
4 changes: 2 additions & 2 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -13364,8 +13364,8 @@ where
// claim.
// Note that a `ChannelMonitor` is created with `update_id` 0 and after we
// provide it with a closure update its `update_id` will be at 1.
if !monitor.offchain_closed() || monitor.get_latest_update_id() > 1 {
should_queue_fc_update = !monitor.offchain_closed();
if !monitor.no_further_updates_allowed() || monitor.get_latest_update_id() > 1 {
should_queue_fc_update = !monitor.no_further_updates_allowed();
let mut latest_update_id = monitor.get_latest_update_id();
if should_queue_fc_update {
latest_update_id += 1;
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' 0.1 Backports for 0.1.2 by TheBlueMatt · Pull Request #3613 · lightningdevkit/rust-lightning · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e8a426c
Use fixed-size Vec allocations for BOLT12 messages
jkczyz Jan 28, 2025
f80d82e
Drop use of reserve_exact in BOLT12 tests
jkczyz Feb 6, 2025
24731d4
fix historical liquidity bucket decay
joostjager Feb 3, 2025
58a7b79
Drop `counterparty_spendable_height` accessor
TheBlueMatt Jan 27, 2025
2e80fbd
Set correct `counterparty_spendable_height` on c.p. revoked HTLCs
TheBlueMatt Jan 27, 2025
174f42e
Mark counterparty revoked offered HTLCs as `Unpinnable`
TheBlueMatt Jan 27, 2025
b5b6429
Call `peer_disconnected` after a handler refuses a connection
TheBlueMatt Jan 30, 2025
8eb3bd7
Avoid parsing `PublicKey`s when applying an unsigned chan update
TheBlueMatt Jan 31, 2025
63fa918
Properly pre-allocate `NetworkGraph` channel/node maps
TheBlueMatt Jan 31, 2025
bed125a
Ignore fees on first-hop channels from the public network graph
TheBlueMatt Feb 3, 2025
fd8eab9
Clean up `long_mpp_route_test` and `mpp_cheaper_route_test`
TheBlueMatt Feb 3, 2025
e46489d
Track `node_counter` in `RouteGraphNode`
TheBlueMatt Feb 3, 2025
b39aef6
Prefer higher-value, shorter equal-cost paths when routing
TheBlueMatt Feb 3, 2025
1976fb8
Move last-hop route handling to the common "normal" hop codepath
TheBlueMatt Feb 3, 2025
1beefb1
Add direct hops to intros after all blinded paths in pathfinding
TheBlueMatt Feb 2, 2025
ae4279d
Add `PathBuildingHop::best_path_from_hop_selected`
TheBlueMatt Feb 3, 2025
1788f80
Replace a few router `expect`s with `debug_assert` + `Err`-returns
TheBlueMatt Feb 10, 2025
bd56ddf
More completely ignore route hints which are for our own channels
TheBlueMatt Feb 10, 2025
fa8d599
Add SemVer compatibility checks to CI
tnull Jan 24, 2025
9d2449a
Fix overflow in historical scoring model point count summation
TheBlueMatt Feb 23, 2025
7c56f21
Cancel claims signed by a remote `ChannelMonitor` when reorging
TheBlueMatt Feb 25, 2025
653c482
Drop return value from `provide_latest_holder_commitment_tx`
TheBlueMatt Feb 25, 2025
6749a07
Only generate a post-close lock ChannelMonitorUpdate if we need one
TheBlueMatt Feb 25, 2025
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
57 changes: 57 additions & 0 deletions .github/workflows/semver.yml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
name: SemVer checks
on:
push:
branches-ignore:
- master
pull_request:
branches-ignore:
- master

jobs:
semver-checks:
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@v4
- name: Install Rust stable toolchain
run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile=minimal --default-toolchain stable
rustup override set stable
- name: Check SemVer with default features
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
feature-group: default-features
- name: Check SemVer *without* default features
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
feature-group: only-explicit-features
- name: Check lightning-background-processor SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
package: lightning-background-processor
feature-group: only-explicit-features
features: futures
- name: Check lightning-block-sync SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
package: lightning-block-sync
feature-group: only-explicit-features
features: rpc-client,rest-client
- name: Check lightning-transaction-sync electrum SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
manifest-path: lightning-transaction-sync/Cargo.toml
feature-group: only-explicit-features
features: electrum
- name: Check lightning-transaction-sync esplora-blocking SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
manifest-path: lightning-transaction-sync/Cargo.toml
feature-group: only-explicit-features
features: esplora-blocking
- name: Check lightning-transaction-sync esplora-async SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
manifest-path: lightning-transaction-sync/Cargo.toml
feature-group: only-explicit-features
features: esplora-async
4 changes: 2 additions & 2 deletions lightning-background-processor/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -2379,8 +2379,8 @@ mod tests {
42,
53,
features,
$nodes[0].node.get_our_node_id(),
$nodes[1].node.get_our_node_id(),
$nodes[0].node.get_our_node_id().into(),
$nodes[1].node.get_our_node_id().into(),
)
.expect("Failed to update channel from partial announcement");
let original_graph_description = $nodes[0].network_graph.to_string();
Expand Down
90 changes: 46 additions & 44 deletions lightning/src/chain/channelmonitor.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1509,8 +1509,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
fn provide_latest_holder_commitment_tx(
&self, holder_commitment_tx: HolderCommitmentTransaction,
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,
) -> Result<(), ()> {
self.inner.lock().unwrap().provide_latest_holder_commitment_tx(holder_commitment_tx, htlc_outputs, &Vec::new(), Vec::new()).map_err(|_| ())
) {
self.inner.lock().unwrap().provide_latest_holder_commitment_tx(holder_commitment_tx, htlc_outputs, &Vec::new(), Vec::new())
}

/// This is used to provide payment preimage(s) out-of-band during startup without updating the
Expand DownExpand Up@@ -1737,10 +1737,14 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
self.inner.lock().unwrap().get_cur_holder_commitment_number()
}

/// Gets whether we've been notified that this channel is closed by the `ChannelManager` (i.e.
/// via a [`ChannelMonitorUpdateStep::ChannelForceClosed`]).
pub(crate) fn offchain_closed(&self) -> bool {
self.inner.lock().unwrap().lockdown_from_offchain
/// Fetches whether this monitor has marked the channel as closed and will refuse any further
/// updates to the commitment transactions.
///
/// It can be marked closed in a few different ways, including via a
/// [`ChannelMonitorUpdateStep::ChannelForceClosed`] or if the channel has been closed
/// on-chain.
pub(crate) fn no_further_updates_allowed(&self) -> bool {
self.inner.lock().unwrap().no_further_updates_allowed()
}

/// Gets the `node_id` of the counterparty for this channel.
Expand DownExpand Up@@ -2901,7 +2905,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
/// is important that any clones of this channel monitor (including remote clones) by kept
/// up-to-date as our holder commitment transaction is updated.
/// Panics if set_on_holder_tx_csv has never been called.
fn provide_latest_holder_commitment_tx(&mut self, holder_commitment_tx: HolderCommitmentTransaction, mut htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>, claimed_htlcs: &[(SentHTLCId, PaymentPreimage)], nondust_htlc_sources: Vec<HTLCSource>) -> Result<(), &'static str> {
fn provide_latest_holder_commitment_tx(&mut self, holder_commitment_tx: HolderCommitmentTransaction, mut htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>, claimed_htlcs: &[(SentHTLCId, PaymentPreimage)], nondust_htlc_sources: Vec<HTLCSource>) {
if htlc_outputs.iter().any(|(_, s, _)| s.is_some()) {
// If we have non-dust HTLCs in htlc_outputs, ensure they match the HTLCs in the
// `holder_commitment_tx`. In the future, we'll no longer provide the redundant data
Expand DownExpand Up@@ -2978,10 +2982,6 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
self.counterparty_fulfilled_htlcs.insert(*claimed_htlc_id, *claimed_preimage);
}
if self.holder_tx_signed {
return Err("Latest holder commitment signed has already been signed, update is rejected");
}
Ok(())
}

/// Provides a payment_hash->payment_preimage mapping. Will be automatically pruned when all
Expand DownExpand Up@@ -3202,11 +3202,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { commitment_tx, htlc_outputs, claimed_htlcs, nondust_htlc_sources } => {
log_trace!(logger, "Updating ChannelMonitor with latest holder commitment transaction info");
if self.lockdown_from_offchain { panic!(); }
if let Err(e) = self.provide_latest_holder_commitment_tx(commitment_tx.clone(), htlc_outputs.clone(), &claimed_htlcs, nondust_htlc_sources.clone()) {
log_error!(logger, "Providing latest holder commitment transaction failed/was refused:");
log_error!(logger, " {}", e);
ret = Err(());
}
self.provide_latest_holder_commitment_tx(commitment_tx.clone(), htlc_outputs.clone(), &claimed_htlcs, nondust_htlc_sources.clone());
}
ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { commitment_txid, htlc_outputs, commitment_number, their_per_commitment_point, .. } => {
log_trace!(logger, "Updating ChannelMonitor with latest counterparty commitment transaction info");
Expand DownExpand Up@@ -3286,12 +3282,16 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
}

if ret.is_ok() && (self.funding_spend_seen || self.lockdown_from_offchain || self.holder_tx_signed) && is_pre_close_update {
if ret.is_ok() && self.no_further_updates_allowed() && is_pre_close_update {
log_error!(logger, "Refusing Channel Monitor Update as counterparty attempted to update commitment after funding was spent");
Err(())
} else { ret }
}

fn no_further_updates_allowed(&self) -> bool {
self.funding_spend_seen || self.lockdown_from_offchain || self.holder_tx_signed
}

fn get_latest_update_id(&self) -> u64 {
self.latest_update_id
}
Expand DownExpand Up@@ -3564,11 +3564,16 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
return (claimable_outpoints, to_counterparty_output_info);
}
let revk_htlc_outp = RevokedHTLCOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, htlc.amount_msat / 1000, htlc.clone(), &self.onchain_tx_handler.channel_transaction_parameters.channel_type_features);
let counterparty_spendable_height = if htlc.offered {
htlc.cltv_expiry
} else {
height
};
let justice_package = PackageTemplate::build_package(
commitment_txid,
transaction_output_index,
PackageSolvingData::RevokedHTLCOutput(revk_htlc_outp),
htlc.cltv_expiry,
counterparty_spendable_height,
);
claimable_outpoints.push(justice_package);
}
Expand DownExpand Up@@ -3869,35 +3874,32 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
}
}
if self.holder_tx_signed {
// If we've signed, we may have broadcast either commitment (prev or current), and
// attempted to claim from it immediately without waiting for a confirmation.
if self.current_holder_commitment_tx.txid != *confirmed_commitment_txid {
// Cancel any pending claims for any holder commitments in case they had previously
// confirmed or been signed (in which case we will start attempting to claim without
// waiting for confirmation).
if self.current_holder_commitment_tx.txid != *confirmed_commitment_txid {
log_trace!(logger, "Canceling claims for previously broadcast holder commitment {}",
self.current_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: self.current_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &self.current_holder_commitment_tx.htlc_outputs {
if let Some(vout) = htlc.transaction_output_index {
outpoint.vout = vout;
self.onchain_tx_handler.abandon_claim(&outpoint);
}
}
}
if let Some(prev_holder_commitment_tx) = &self.prev_holder_signed_commitment_tx {
if prev_holder_commitment_tx.txid != *confirmed_commitment_txid {
log_trace!(logger, "Canceling claims for previously broadcast holder commitment {}",
self.current_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: self.current_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &self.current_holder_commitment_tx.htlc_outputs {
prev_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: prev_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &prev_holder_commitment_tx.htlc_outputs {
if let Some(vout) = htlc.transaction_output_index {
outpoint.vout = vout;
self.onchain_tx_handler.abandon_claim(&outpoint);
}
}
}
if let Some(prev_holder_commitment_tx) = &self.prev_holder_signed_commitment_tx {
if prev_holder_commitment_tx.txid != *confirmed_commitment_txid {
log_trace!(logger, "Canceling claims for previously broadcast holder commitment {}",
prev_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: prev_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &prev_holder_commitment_tx.htlc_outputs {
if let Some(vout) = htlc.transaction_output_index {
outpoint.vout = vout;
self.onchain_tx_handler.abandon_claim(&outpoint);
}
}
}
}
} else {
// No previous claim.
}
}

Expand DownExpand Up@@ -4233,7 +4235,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
}

if self.lockdown_from_offchain || self.funding_spend_seen || self.holder_tx_signed {
if self.no_further_updates_allowed() {
// Fail back HTLCs on backwards channels if they expire within
// `LATENCY_GRACE_PERIOD_BLOCKS` blocks and the channel is closed (i.e. we're at a
// point where no further off-chain updates will be accepted). If we haven't seen the
Expand DownExpand Up@@ -5384,7 +5386,7 @@ mod tests {
let dummy_commitment_tx = HolderCommitmentTransaction::dummy(&mut htlcs);

monitor.provide_latest_holder_commitment_tx(dummy_commitment_tx.clone(),
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect()).unwrap();
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect());
monitor.provide_latest_counterparty_commitment_tx(Txid::from_byte_array(Sha256::hash(b"1").to_byte_array()),
preimages_slice_to_htlc_outputs!(preimages[5..15]), 281474976710655, dummy_key, &logger);
monitor.provide_latest_counterparty_commitment_tx(Txid::from_byte_array(Sha256::hash(b"2").to_byte_array()),
Expand DownExpand Up@@ -5422,7 +5424,7 @@ mod tests {
let mut htlcs = preimages_slice_to_htlcs!(preimages[0..5]);
let dummy_commitment_tx = HolderCommitmentTransaction::dummy(&mut htlcs);
monitor.provide_latest_holder_commitment_tx(dummy_commitment_tx.clone(),
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect()).unwrap();
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect());
secret[0..32].clone_from_slice(&<Vec<u8>>::from_hex("2273e227a5b7449b6e70f1fb4652864038b1cbf9cd7c043a7d6456b7fc275ad8").unwrap());
monitor.provide_secret(281474976710653, secret.clone()).unwrap();
assert_eq!(monitor.inner.lock().unwrap().payment_preimages.len(), 12);
Expand All@@ -5433,7 +5435,7 @@ mod tests {
let mut htlcs = preimages_slice_to_htlcs!(preimages[0..3]);
let dummy_commitment_tx = HolderCommitmentTransaction::dummy(&mut htlcs);
monitor.provide_latest_holder_commitment_tx(dummy_commitment_tx,
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect()).unwrap();
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect());
secret[0..32].clone_from_slice(&<Vec<u8>>::from_hex("27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116").unwrap());
monitor.provide_secret(281474976710652, secret.clone()).unwrap();
assert_eq!(monitor.inner.lock().unwrap().payment_preimages.len(), 5);
Expand Down
46 changes: 29 additions & 17 deletions lightning/src/chain/package.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -699,8 +699,13 @@ impl PackageSolvingData {
match self {
PackageSolvingData::RevokedOutput(RevokedOutput { .. }) =>
PackageMalleability::Malleable(AggregationCluster::Unpinnable),
PackageSolvingData::RevokedHTLCOutput(..) =>
PackageMalleability::Malleable(AggregationCluster::Pinnable),
PackageSolvingData::RevokedHTLCOutput(RevokedHTLCOutput { htlc, .. }) => {
if htlc.offered {
PackageMalleability::Malleable(AggregationCluster::Unpinnable)
} else {
PackageMalleability::Malleable(AggregationCluster::Pinnable)
}
},
PackageSolvingData::CounterpartyOfferedHTLCOutput(..) =>
PackageMalleability::Malleable(AggregationCluster::Unpinnable),
PackageSolvingData::CounterpartyReceivedHTLCOutput(..) =>
Expand DownExpand Up@@ -771,10 +776,12 @@ pub struct PackageTemplate {
/// Block height at which our counterparty can potentially claim this output as well (assuming
/// they have the keys or information required to do so).
///
/// This is used primarily by external consumers to decide when an output becomes "pinnable"
/// because the counterparty can potentially spend it. It is also used internally by
/// [`Self::get_height_timer`] to identify when an output must be claimed by, depending on the
/// type of output.
/// This is used primarily to decide when an output becomes "pinnable" because the counterparty
/// can potentially spend it. It is also used internally by [`Self::get_height_timer`] to
/// identify when an output must be claimed by, depending on the type of output.
///
/// Note that for revoked counterparty HTLC outputs the value may be zero in some cases where
/// we upgraded from LDK 0.1 or prior.
counterparty_spendable_height: u32,
// Cache of package feerate committed at previous (re)broadcast. If bumping resources
// (either claimed output value or external utxo), it will keep increasing until holder
Expand DownExpand Up@@ -834,17 +841,17 @@ impl PackageTemplate {
// Now check that we only merge packages if they are both unpinnable or both
// pinnable.
let self_pinnable = self_cluster == AggregationCluster::Pinnable ||
self.counterparty_spendable_height() <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
self.counterparty_spendable_height <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
let other_pinnable = other_cluster == AggregationCluster::Pinnable ||
other.counterparty_spendable_height() <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
other.counterparty_spendable_height <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
if self_pinnable && other_pinnable {
return true;
}

let self_unpinnable = self_cluster == AggregationCluster::Unpinnable &&
self.counterparty_spendable_height() > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
self.counterparty_spendable_height > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
let other_unpinnable = other_cluster == AggregationCluster::Unpinnable &&
other.counterparty_spendable_height() > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
other.counterparty_spendable_height > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
if self_unpinnable && other_unpinnable {
return true;
}
Expand All@@ -855,13 +862,6 @@ impl PackageTemplate {
pub(crate) fn is_malleable(&self) -> bool {
matches!(self.malleability, PackageMalleability::Malleable(..))
}
/// The height at which our counterparty may be able to spend this output.
///
/// This is an important limit for aggregation as after this height our counterparty may be
/// able to pin transactions spending this output in the mempool.
pub(crate) fn counterparty_spendable_height(&self) -> u32 {
self.counterparty_spendable_height
}
pub(crate) fn previous_feerate(&self) -> u64 {
self.feerate_previous
}
Expand DownExpand Up@@ -1225,6 +1225,18 @@ impl Readable for PackageTemplate {
(4, _height_original, option), // Written with a dummy value since 0.1
(6, height_timer, option),
});
for (_, input) in &inputs {
if let PackageSolvingData::RevokedHTLCOutput(RevokedHTLCOutput { htlc, .. }) = input {
// LDK versions through 0.1 set the wrong counterparty_spendable_height for
// non-offered revoked HTLCs (ie HTLCs we sent to our counterparty which they can
// claim with a preimage immediately). Here we detect this and reset the value to
// zero, as the value is unused except for merging decisions which doesn't care
// about any values below the current height.
if !htlc.offered && htlc.cltv_expiry == counterparty_spendable_height {
counterparty_spendable_height = 0;
}
}
}
Ok(PackageTemplate {
inputs,
malleability,
Expand Down
4 changes: 2 additions & 2 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -13364,8 +13364,8 @@ where
// claim.
// Note that a `ChannelMonitor` is created with `update_id` 0 and after we
// provide it with a closure update its `update_id` will be at 1.
if !monitor.offchain_closed() || monitor.get_latest_update_id() > 1 {
should_queue_fc_update = !monitor.offchain_closed();
if !monitor.no_further_updates_allowed() || monitor.get_latest_update_id() > 1 {
should_queue_fc_update = !monitor.no_further_updates_allowed();
let mut latest_update_id = monitor.get_latest_update_id();
if should_queue_fc_update {
latest_update_id += 1;
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' 0.1 Backports for 0.1.2 by TheBlueMatt · Pull Request #3613 · lightningdevkit/rust-lightning · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e8a426c
Use fixed-size Vec allocations for BOLT12 messages
jkczyz Jan 28, 2025
f80d82e
Drop use of reserve_exact in BOLT12 tests
jkczyz Feb 6, 2025
24731d4
fix historical liquidity bucket decay
joostjager Feb 3, 2025
58a7b79
Drop `counterparty_spendable_height` accessor
TheBlueMatt Jan 27, 2025
2e80fbd
Set correct `counterparty_spendable_height` on c.p. revoked HTLCs
TheBlueMatt Jan 27, 2025
174f42e
Mark counterparty revoked offered HTLCs as `Unpinnable`
TheBlueMatt Jan 27, 2025
b5b6429
Call `peer_disconnected` after a handler refuses a connection
TheBlueMatt Jan 30, 2025
8eb3bd7
Avoid parsing `PublicKey`s when applying an unsigned chan update
TheBlueMatt Jan 31, 2025
63fa918
Properly pre-allocate `NetworkGraph` channel/node maps
TheBlueMatt Jan 31, 2025
bed125a
Ignore fees on first-hop channels from the public network graph
TheBlueMatt Feb 3, 2025
fd8eab9
Clean up `long_mpp_route_test` and `mpp_cheaper_route_test`
TheBlueMatt Feb 3, 2025
e46489d
Track `node_counter` in `RouteGraphNode`
TheBlueMatt Feb 3, 2025
b39aef6
Prefer higher-value, shorter equal-cost paths when routing
TheBlueMatt Feb 3, 2025
1976fb8
Move last-hop route handling to the common "normal" hop codepath
TheBlueMatt Feb 3, 2025
1beefb1
Add direct hops to intros after all blinded paths in pathfinding
TheBlueMatt Feb 2, 2025
ae4279d
Add `PathBuildingHop::best_path_from_hop_selected`
TheBlueMatt Feb 3, 2025
1788f80
Replace a few router `expect`s with `debug_assert` + `Err`-returns
TheBlueMatt Feb 10, 2025
bd56ddf
More completely ignore route hints which are for our own channels
TheBlueMatt Feb 10, 2025
fa8d599
Add SemVer compatibility checks to CI
tnull Jan 24, 2025
9d2449a
Fix overflow in historical scoring model point count summation
TheBlueMatt Feb 23, 2025
7c56f21
Cancel claims signed by a remote `ChannelMonitor` when reorging
TheBlueMatt Feb 25, 2025
653c482
Drop return value from `provide_latest_holder_commitment_tx`
TheBlueMatt Feb 25, 2025
6749a07
Only generate a post-close lock ChannelMonitorUpdate if we need one
TheBlueMatt Feb 25, 2025
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
57 changes: 57 additions & 0 deletions .github/workflows/semver.yml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
name: SemVer checks
on:
push:
branches-ignore:
- master
pull_request:
branches-ignore:
- master

jobs:
semver-checks:
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@v4
- name: Install Rust stable toolchain
run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile=minimal --default-toolchain stable
rustup override set stable
- name: Check SemVer with default features
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
feature-group: default-features
- name: Check SemVer *without* default features
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
feature-group: only-explicit-features
- name: Check lightning-background-processor SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
package: lightning-background-processor
feature-group: only-explicit-features
features: futures
- name: Check lightning-block-sync SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
package: lightning-block-sync
feature-group: only-explicit-features
features: rpc-client,rest-client
- name: Check lightning-transaction-sync electrum SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
manifest-path: lightning-transaction-sync/Cargo.toml
feature-group: only-explicit-features
features: electrum
- name: Check lightning-transaction-sync esplora-blocking SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
manifest-path: lightning-transaction-sync/Cargo.toml
feature-group: only-explicit-features
features: esplora-blocking
- name: Check lightning-transaction-sync esplora-async SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
manifest-path: lightning-transaction-sync/Cargo.toml
feature-group: only-explicit-features
features: esplora-async
4 changes: 2 additions & 2 deletions lightning-background-processor/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -2379,8 +2379,8 @@ mod tests {
42,
53,
features,
$nodes[0].node.get_our_node_id(),
$nodes[1].node.get_our_node_id(),
$nodes[0].node.get_our_node_id().into(),
$nodes[1].node.get_our_node_id().into(),
)
.expect("Failed to update channel from partial announcement");
let original_graph_description = $nodes[0].network_graph.to_string();
Expand Down
90 changes: 46 additions & 44 deletions lightning/src/chain/channelmonitor.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1509,8 +1509,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
fn provide_latest_holder_commitment_tx(
&self, holder_commitment_tx: HolderCommitmentTransaction,
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,
) -> Result<(), ()> {
self.inner.lock().unwrap().provide_latest_holder_commitment_tx(holder_commitment_tx, htlc_outputs, &Vec::new(), Vec::new()).map_err(|_| ())
) {
self.inner.lock().unwrap().provide_latest_holder_commitment_tx(holder_commitment_tx, htlc_outputs, &Vec::new(), Vec::new())
}

/// This is used to provide payment preimage(s) out-of-band during startup without updating the
Expand DownExpand Up@@ -1737,10 +1737,14 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
self.inner.lock().unwrap().get_cur_holder_commitment_number()
}

/// Gets whether we've been notified that this channel is closed by the `ChannelManager` (i.e.
/// via a [`ChannelMonitorUpdateStep::ChannelForceClosed`]).
pub(crate) fn offchain_closed(&self) -> bool {
self.inner.lock().unwrap().lockdown_from_offchain
/// Fetches whether this monitor has marked the channel as closed and will refuse any further
/// updates to the commitment transactions.
///
/// It can be marked closed in a few different ways, including via a
/// [`ChannelMonitorUpdateStep::ChannelForceClosed`] or if the channel has been closed
/// on-chain.
pub(crate) fn no_further_updates_allowed(&self) -> bool {
self.inner.lock().unwrap().no_further_updates_allowed()
}

/// Gets the `node_id` of the counterparty for this channel.
Expand DownExpand Up@@ -2901,7 +2905,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
/// is important that any clones of this channel monitor (including remote clones) by kept
/// up-to-date as our holder commitment transaction is updated.
/// Panics if set_on_holder_tx_csv has never been called.
fn provide_latest_holder_commitment_tx(&mut self, holder_commitment_tx: HolderCommitmentTransaction, mut htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>, claimed_htlcs: &[(SentHTLCId, PaymentPreimage)], nondust_htlc_sources: Vec<HTLCSource>) -> Result<(), &'static str> {
fn provide_latest_holder_commitment_tx(&mut self, holder_commitment_tx: HolderCommitmentTransaction, mut htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>, claimed_htlcs: &[(SentHTLCId, PaymentPreimage)], nondust_htlc_sources: Vec<HTLCSource>) {
if htlc_outputs.iter().any(|(_, s, _)| s.is_some()) {
// If we have non-dust HTLCs in htlc_outputs, ensure they match the HTLCs in the
// `holder_commitment_tx`. In the future, we'll no longer provide the redundant data
Expand DownExpand Up@@ -2978,10 +2982,6 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
self.counterparty_fulfilled_htlcs.insert(*claimed_htlc_id, *claimed_preimage);
}
if self.holder_tx_signed {
return Err("Latest holder commitment signed has already been signed, update is rejected");
}
Ok(())
}

/// Provides a payment_hash->payment_preimage mapping. Will be automatically pruned when all
Expand DownExpand Up@@ -3202,11 +3202,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { commitment_tx, htlc_outputs, claimed_htlcs, nondust_htlc_sources } => {
log_trace!(logger, "Updating ChannelMonitor with latest holder commitment transaction info");
if self.lockdown_from_offchain { panic!(); }
if let Err(e) = self.provide_latest_holder_commitment_tx(commitment_tx.clone(), htlc_outputs.clone(), &claimed_htlcs, nondust_htlc_sources.clone()) {
log_error!(logger, "Providing latest holder commitment transaction failed/was refused:");
log_error!(logger, " {}", e);
ret = Err(());
}
self.provide_latest_holder_commitment_tx(commitment_tx.clone(), htlc_outputs.clone(), &claimed_htlcs, nondust_htlc_sources.clone());
}
ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { commitment_txid, htlc_outputs, commitment_number, their_per_commitment_point, .. } => {
log_trace!(logger, "Updating ChannelMonitor with latest counterparty commitment transaction info");
Expand DownExpand Up@@ -3286,12 +3282,16 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
}

if ret.is_ok() && (self.funding_spend_seen || self.lockdown_from_offchain || self.holder_tx_signed) && is_pre_close_update {
if ret.is_ok() && self.no_further_updates_allowed() && is_pre_close_update {
log_error!(logger, "Refusing Channel Monitor Update as counterparty attempted to update commitment after funding was spent");
Err(())
} else { ret }
}

fn no_further_updates_allowed(&self) -> bool {
self.funding_spend_seen || self.lockdown_from_offchain || self.holder_tx_signed
}

fn get_latest_update_id(&self) -> u64 {
self.latest_update_id
}
Expand DownExpand Up@@ -3564,11 +3564,16 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
return (claimable_outpoints, to_counterparty_output_info);
}
let revk_htlc_outp = RevokedHTLCOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, htlc.amount_msat / 1000, htlc.clone(), &self.onchain_tx_handler.channel_transaction_parameters.channel_type_features);
let counterparty_spendable_height = if htlc.offered {
htlc.cltv_expiry
} else {
height
};
let justice_package = PackageTemplate::build_package(
commitment_txid,
transaction_output_index,
PackageSolvingData::RevokedHTLCOutput(revk_htlc_outp),
htlc.cltv_expiry,
counterparty_spendable_height,
);
claimable_outpoints.push(justice_package);
}
Expand DownExpand Up@@ -3869,35 +3874,32 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
}
}
if self.holder_tx_signed {
// If we've signed, we may have broadcast either commitment (prev or current), and
// attempted to claim from it immediately without waiting for a confirmation.
if self.current_holder_commitment_tx.txid != *confirmed_commitment_txid {
// Cancel any pending claims for any holder commitments in case they had previously
// confirmed or been signed (in which case we will start attempting to claim without
// waiting for confirmation).
if self.current_holder_commitment_tx.txid != *confirmed_commitment_txid {
log_trace!(logger, "Canceling claims for previously broadcast holder commitment {}",
self.current_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: self.current_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &self.current_holder_commitment_tx.htlc_outputs {
if let Some(vout) = htlc.transaction_output_index {
outpoint.vout = vout;
self.onchain_tx_handler.abandon_claim(&outpoint);
}
}
}
if let Some(prev_holder_commitment_tx) = &self.prev_holder_signed_commitment_tx {
if prev_holder_commitment_tx.txid != *confirmed_commitment_txid {
log_trace!(logger, "Canceling claims for previously broadcast holder commitment {}",
self.current_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: self.current_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &self.current_holder_commitment_tx.htlc_outputs {
prev_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: prev_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &prev_holder_commitment_tx.htlc_outputs {
if let Some(vout) = htlc.transaction_output_index {
outpoint.vout = vout;
self.onchain_tx_handler.abandon_claim(&outpoint);
}
}
}
if let Some(prev_holder_commitment_tx) = &self.prev_holder_signed_commitment_tx {
if prev_holder_commitment_tx.txid != *confirmed_commitment_txid {
log_trace!(logger, "Canceling claims for previously broadcast holder commitment {}",
prev_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: prev_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &prev_holder_commitment_tx.htlc_outputs {
if let Some(vout) = htlc.transaction_output_index {
outpoint.vout = vout;
self.onchain_tx_handler.abandon_claim(&outpoint);
}
}
}
}
} else {
// No previous claim.
}
}

Expand DownExpand Up@@ -4233,7 +4235,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
}

if self.lockdown_from_offchain || self.funding_spend_seen || self.holder_tx_signed {
if self.no_further_updates_allowed() {
// Fail back HTLCs on backwards channels if they expire within
// `LATENCY_GRACE_PERIOD_BLOCKS` blocks and the channel is closed (i.e. we're at a
// point where no further off-chain updates will be accepted). If we haven't seen the
Expand DownExpand Up@@ -5384,7 +5386,7 @@ mod tests {
let dummy_commitment_tx = HolderCommitmentTransaction::dummy(&mut htlcs);

monitor.provide_latest_holder_commitment_tx(dummy_commitment_tx.clone(),
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect()).unwrap();
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect());
monitor.provide_latest_counterparty_commitment_tx(Txid::from_byte_array(Sha256::hash(b"1").to_byte_array()),
preimages_slice_to_htlc_outputs!(preimages[5..15]), 281474976710655, dummy_key, &logger);
monitor.provide_latest_counterparty_commitment_tx(Txid::from_byte_array(Sha256::hash(b"2").to_byte_array()),
Expand DownExpand Up@@ -5422,7 +5424,7 @@ mod tests {
let mut htlcs = preimages_slice_to_htlcs!(preimages[0..5]);
let dummy_commitment_tx = HolderCommitmentTransaction::dummy(&mut htlcs);
monitor.provide_latest_holder_commitment_tx(dummy_commitment_tx.clone(),
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect()).unwrap();
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect());
secret[0..32].clone_from_slice(&<Vec<u8>>::from_hex("2273e227a5b7449b6e70f1fb4652864038b1cbf9cd7c043a7d6456b7fc275ad8").unwrap());
monitor.provide_secret(281474976710653, secret.clone()).unwrap();
assert_eq!(monitor.inner.lock().unwrap().payment_preimages.len(), 12);
Expand All@@ -5433,7 +5435,7 @@ mod tests {
let mut htlcs = preimages_slice_to_htlcs!(preimages[0..3]);
let dummy_commitment_tx = HolderCommitmentTransaction::dummy(&mut htlcs);
monitor.provide_latest_holder_commitment_tx(dummy_commitment_tx,
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect()).unwrap();
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect());
secret[0..32].clone_from_slice(&<Vec<u8>>::from_hex("27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116").unwrap());
monitor.provide_secret(281474976710652, secret.clone()).unwrap();
assert_eq!(monitor.inner.lock().unwrap().payment_preimages.len(), 5);
Expand Down
46 changes: 29 additions & 17 deletions lightning/src/chain/package.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -699,8 +699,13 @@ impl PackageSolvingData {
match self {
PackageSolvingData::RevokedOutput(RevokedOutput { .. }) =>
PackageMalleability::Malleable(AggregationCluster::Unpinnable),
PackageSolvingData::RevokedHTLCOutput(..) =>
PackageMalleability::Malleable(AggregationCluster::Pinnable),
PackageSolvingData::RevokedHTLCOutput(RevokedHTLCOutput { htlc, .. }) => {
if htlc.offered {
PackageMalleability::Malleable(AggregationCluster::Unpinnable)
} else {
PackageMalleability::Malleable(AggregationCluster::Pinnable)
}
},
PackageSolvingData::CounterpartyOfferedHTLCOutput(..) =>
PackageMalleability::Malleable(AggregationCluster::Unpinnable),
PackageSolvingData::CounterpartyReceivedHTLCOutput(..) =>
Expand DownExpand Up@@ -771,10 +776,12 @@ pub struct PackageTemplate {
/// Block height at which our counterparty can potentially claim this output as well (assuming
/// they have the keys or information required to do so).
///
/// This is used primarily by external consumers to decide when an output becomes "pinnable"
/// because the counterparty can potentially spend it. It is also used internally by
/// [`Self::get_height_timer`] to identify when an output must be claimed by, depending on the
/// type of output.
/// This is used primarily to decide when an output becomes "pinnable" because the counterparty
/// can potentially spend it. It is also used internally by [`Self::get_height_timer`] to
/// identify when an output must be claimed by, depending on the type of output.
///
/// Note that for revoked counterparty HTLC outputs the value may be zero in some cases where
/// we upgraded from LDK 0.1 or prior.
counterparty_spendable_height: u32,
// Cache of package feerate committed at previous (re)broadcast. If bumping resources
// (either claimed output value or external utxo), it will keep increasing until holder
Expand DownExpand Up@@ -834,17 +841,17 @@ impl PackageTemplate {
// Now check that we only merge packages if they are both unpinnable or both
// pinnable.
let self_pinnable = self_cluster == AggregationCluster::Pinnable ||
self.counterparty_spendable_height() <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
self.counterparty_spendable_height <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
let other_pinnable = other_cluster == AggregationCluster::Pinnable ||
other.counterparty_spendable_height() <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
other.counterparty_spendable_height <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
if self_pinnable && other_pinnable {
return true;
}

let self_unpinnable = self_cluster == AggregationCluster::Unpinnable &&
self.counterparty_spendable_height() > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
self.counterparty_spendable_height > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
let other_unpinnable = other_cluster == AggregationCluster::Unpinnable &&
other.counterparty_spendable_height() > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
other.counterparty_spendable_height > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
if self_unpinnable && other_unpinnable {
return true;
}
Expand All@@ -855,13 +862,6 @@ impl PackageTemplate {
pub(crate) fn is_malleable(&self) -> bool {
matches!(self.malleability, PackageMalleability::Malleable(..))
}
/// The height at which our counterparty may be able to spend this output.
///
/// This is an important limit for aggregation as after this height our counterparty may be
/// able to pin transactions spending this output in the mempool.
pub(crate) fn counterparty_spendable_height(&self) -> u32 {
self.counterparty_spendable_height
}
pub(crate) fn previous_feerate(&self) -> u64 {
self.feerate_previous
}
Expand DownExpand Up@@ -1225,6 +1225,18 @@ impl Readable for PackageTemplate {
(4, _height_original, option), // Written with a dummy value since 0.1
(6, height_timer, option),
});
for (_, input) in &inputs {
if let PackageSolvingData::RevokedHTLCOutput(RevokedHTLCOutput { htlc, .. }) = input {
// LDK versions through 0.1 set the wrong counterparty_spendable_height for
// non-offered revoked HTLCs (ie HTLCs we sent to our counterparty which they can
// claim with a preimage immediately). Here we detect this and reset the value to
// zero, as the value is unused except for merging decisions which doesn't care
// about any values below the current height.
if !htlc.offered && htlc.cltv_expiry == counterparty_spendable_height {
counterparty_spendable_height = 0;
}
}
}
Ok(PackageTemplate {
inputs,
malleability,
Expand Down
4 changes: 2 additions & 2 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -13364,8 +13364,8 @@ where
// claim.
// Note that a `ChannelMonitor` is created with `update_id` 0 and after we
// provide it with a closure update its `update_id` will be at 1.
if !monitor.offchain_closed() || monitor.get_latest_update_id() > 1 {
should_queue_fc_update = !monitor.offchain_closed();
if !monitor.no_further_updates_allowed() || monitor.get_latest_update_id() > 1 {
should_queue_fc_update = !monitor.no_further_updates_allowed();
let mut latest_update_id = monitor.get_latest_update_id();
if should_queue_fc_update {
latest_update_id += 1;
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' 0.1 Backports for 0.1.2 by TheBlueMatt · Pull Request #3613 · lightningdevkit/rust-lightning · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e8a426c
Use fixed-size Vec allocations for BOLT12 messages
jkczyz Jan 28, 2025
f80d82e
Drop use of reserve_exact in BOLT12 tests
jkczyz Feb 6, 2025
24731d4
fix historical liquidity bucket decay
joostjager Feb 3, 2025
58a7b79
Drop `counterparty_spendable_height` accessor
TheBlueMatt Jan 27, 2025
2e80fbd
Set correct `counterparty_spendable_height` on c.p. revoked HTLCs
TheBlueMatt Jan 27, 2025
174f42e
Mark counterparty revoked offered HTLCs as `Unpinnable`
TheBlueMatt Jan 27, 2025
b5b6429
Call `peer_disconnected` after a handler refuses a connection
TheBlueMatt Jan 30, 2025
8eb3bd7
Avoid parsing `PublicKey`s when applying an unsigned chan update
TheBlueMatt Jan 31, 2025
63fa918
Properly pre-allocate `NetworkGraph` channel/node maps
TheBlueMatt Jan 31, 2025
bed125a
Ignore fees on first-hop channels from the public network graph
TheBlueMatt Feb 3, 2025
fd8eab9
Clean up `long_mpp_route_test` and `mpp_cheaper_route_test`
TheBlueMatt Feb 3, 2025
e46489d
Track `node_counter` in `RouteGraphNode`
TheBlueMatt Feb 3, 2025
b39aef6
Prefer higher-value, shorter equal-cost paths when routing
TheBlueMatt Feb 3, 2025
1976fb8
Move last-hop route handling to the common "normal" hop codepath
TheBlueMatt Feb 3, 2025
1beefb1
Add direct hops to intros after all blinded paths in pathfinding
TheBlueMatt Feb 2, 2025
ae4279d
Add `PathBuildingHop::best_path_from_hop_selected`
TheBlueMatt Feb 3, 2025
1788f80
Replace a few router `expect`s with `debug_assert` + `Err`-returns
TheBlueMatt Feb 10, 2025
bd56ddf
More completely ignore route hints which are for our own channels
TheBlueMatt Feb 10, 2025
fa8d599
Add SemVer compatibility checks to CI
tnull Jan 24, 2025
9d2449a
Fix overflow in historical scoring model point count summation
TheBlueMatt Feb 23, 2025
7c56f21
Cancel claims signed by a remote `ChannelMonitor` when reorging
TheBlueMatt Feb 25, 2025
653c482
Drop return value from `provide_latest_holder_commitment_tx`
TheBlueMatt Feb 25, 2025
6749a07
Only generate a post-close lock ChannelMonitorUpdate if we need one
TheBlueMatt Feb 25, 2025
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
57 changes: 57 additions & 0 deletions .github/workflows/semver.yml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
name: SemVer checks
on:
push:
branches-ignore:
- master
pull_request:
branches-ignore:
- master

jobs:
semver-checks:
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@v4
- name: Install Rust stable toolchain
run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile=minimal --default-toolchain stable
rustup override set stable
- name: Check SemVer with default features
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
feature-group: default-features
- name: Check SemVer *without* default features
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
feature-group: only-explicit-features
- name: Check lightning-background-processor SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
package: lightning-background-processor
feature-group: only-explicit-features
features: futures
- name: Check lightning-block-sync SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
package: lightning-block-sync
feature-group: only-explicit-features
features: rpc-client,rest-client
- name: Check lightning-transaction-sync electrum SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
manifest-path: lightning-transaction-sync/Cargo.toml
feature-group: only-explicit-features
features: electrum
- name: Check lightning-transaction-sync esplora-blocking SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
manifest-path: lightning-transaction-sync/Cargo.toml
feature-group: only-explicit-features
features: esplora-blocking
- name: Check lightning-transaction-sync esplora-async SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
manifest-path: lightning-transaction-sync/Cargo.toml
feature-group: only-explicit-features
features: esplora-async
4 changes: 2 additions & 2 deletions lightning-background-processor/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -2379,8 +2379,8 @@ mod tests {
42,
53,
features,
$nodes[0].node.get_our_node_id(),
$nodes[1].node.get_our_node_id(),
$nodes[0].node.get_our_node_id().into(),
$nodes[1].node.get_our_node_id().into(),
)
.expect("Failed to update channel from partial announcement");
let original_graph_description = $nodes[0].network_graph.to_string();
Expand Down
90 changes: 46 additions & 44 deletions lightning/src/chain/channelmonitor.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1509,8 +1509,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
fn provide_latest_holder_commitment_tx(
&self, holder_commitment_tx: HolderCommitmentTransaction,
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,
) -> Result<(), ()> {
self.inner.lock().unwrap().provide_latest_holder_commitment_tx(holder_commitment_tx, htlc_outputs, &Vec::new(), Vec::new()).map_err(|_| ())
) {
self.inner.lock().unwrap().provide_latest_holder_commitment_tx(holder_commitment_tx, htlc_outputs, &Vec::new(), Vec::new())
}

/// This is used to provide payment preimage(s) out-of-band during startup without updating the
Expand DownExpand Up@@ -1737,10 +1737,14 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
self.inner.lock().unwrap().get_cur_holder_commitment_number()
}

/// Gets whether we've been notified that this channel is closed by the `ChannelManager` (i.e.
/// via a [`ChannelMonitorUpdateStep::ChannelForceClosed`]).
pub(crate) fn offchain_closed(&self) -> bool {
self.inner.lock().unwrap().lockdown_from_offchain
/// Fetches whether this monitor has marked the channel as closed and will refuse any further
/// updates to the commitment transactions.
///
/// It can be marked closed in a few different ways, including via a
/// [`ChannelMonitorUpdateStep::ChannelForceClosed`] or if the channel has been closed
/// on-chain.
pub(crate) fn no_further_updates_allowed(&self) -> bool {
self.inner.lock().unwrap().no_further_updates_allowed()
}

/// Gets the `node_id` of the counterparty for this channel.
Expand DownExpand Up@@ -2901,7 +2905,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
/// is important that any clones of this channel monitor (including remote clones) by kept
/// up-to-date as our holder commitment transaction is updated.
/// Panics if set_on_holder_tx_csv has never been called.
fn provide_latest_holder_commitment_tx(&mut self, holder_commitment_tx: HolderCommitmentTransaction, mut htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>, claimed_htlcs: &[(SentHTLCId, PaymentPreimage)], nondust_htlc_sources: Vec<HTLCSource>) -> Result<(), &'static str> {
fn provide_latest_holder_commitment_tx(&mut self, holder_commitment_tx: HolderCommitmentTransaction, mut htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>, claimed_htlcs: &[(SentHTLCId, PaymentPreimage)], nondust_htlc_sources: Vec<HTLCSource>) {
if htlc_outputs.iter().any(|(_, s, _)| s.is_some()) {
// If we have non-dust HTLCs in htlc_outputs, ensure they match the HTLCs in the
// `holder_commitment_tx`. In the future, we'll no longer provide the redundant data
Expand DownExpand Up@@ -2978,10 +2982,6 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
self.counterparty_fulfilled_htlcs.insert(*claimed_htlc_id, *claimed_preimage);
}
if self.holder_tx_signed {
return Err("Latest holder commitment signed has already been signed, update is rejected");
}
Ok(())
}

/// Provides a payment_hash->payment_preimage mapping. Will be automatically pruned when all
Expand DownExpand Up@@ -3202,11 +3202,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { commitment_tx, htlc_outputs, claimed_htlcs, nondust_htlc_sources } => {
log_trace!(logger, "Updating ChannelMonitor with latest holder commitment transaction info");
if self.lockdown_from_offchain { panic!(); }
if let Err(e) = self.provide_latest_holder_commitment_tx(commitment_tx.clone(), htlc_outputs.clone(), &claimed_htlcs, nondust_htlc_sources.clone()) {
log_error!(logger, "Providing latest holder commitment transaction failed/was refused:");
log_error!(logger, " {}", e);
ret = Err(());
}
self.provide_latest_holder_commitment_tx(commitment_tx.clone(), htlc_outputs.clone(), &claimed_htlcs, nondust_htlc_sources.clone());
}
ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { commitment_txid, htlc_outputs, commitment_number, their_per_commitment_point, .. } => {
log_trace!(logger, "Updating ChannelMonitor with latest counterparty commitment transaction info");
Expand DownExpand Up@@ -3286,12 +3282,16 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
}

if ret.is_ok() && (self.funding_spend_seen || self.lockdown_from_offchain || self.holder_tx_signed) && is_pre_close_update {
if ret.is_ok() && self.no_further_updates_allowed() && is_pre_close_update {
log_error!(logger, "Refusing Channel Monitor Update as counterparty attempted to update commitment after funding was spent");
Err(())
} else { ret }
}

fn no_further_updates_allowed(&self) -> bool {
self.funding_spend_seen || self.lockdown_from_offchain || self.holder_tx_signed
}

fn get_latest_update_id(&self) -> u64 {
self.latest_update_id
}
Expand DownExpand Up@@ -3564,11 +3564,16 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
return (claimable_outpoints, to_counterparty_output_info);
}
let revk_htlc_outp = RevokedHTLCOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, htlc.amount_msat / 1000, htlc.clone(), &self.onchain_tx_handler.channel_transaction_parameters.channel_type_features);
let counterparty_spendable_height = if htlc.offered {
htlc.cltv_expiry
} else {
height
};
let justice_package = PackageTemplate::build_package(
commitment_txid,
transaction_output_index,
PackageSolvingData::RevokedHTLCOutput(revk_htlc_outp),
htlc.cltv_expiry,
counterparty_spendable_height,
);
claimable_outpoints.push(justice_package);
}
Expand DownExpand Up@@ -3869,35 +3874,32 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
}
}
if self.holder_tx_signed {
// If we've signed, we may have broadcast either commitment (prev or current), and
// attempted to claim from it immediately without waiting for a confirmation.
if self.current_holder_commitment_tx.txid != *confirmed_commitment_txid {
// Cancel any pending claims for any holder commitments in case they had previously
// confirmed or been signed (in which case we will start attempting to claim without
// waiting for confirmation).
if self.current_holder_commitment_tx.txid != *confirmed_commitment_txid {
log_trace!(logger, "Canceling claims for previously broadcast holder commitment {}",
self.current_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: self.current_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &self.current_holder_commitment_tx.htlc_outputs {
if let Some(vout) = htlc.transaction_output_index {
outpoint.vout = vout;
self.onchain_tx_handler.abandon_claim(&outpoint);
}
}
}
if let Some(prev_holder_commitment_tx) = &self.prev_holder_signed_commitment_tx {
if prev_holder_commitment_tx.txid != *confirmed_commitment_txid {
log_trace!(logger, "Canceling claims for previously broadcast holder commitment {}",
self.current_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: self.current_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &self.current_holder_commitment_tx.htlc_outputs {
prev_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: prev_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &prev_holder_commitment_tx.htlc_outputs {
if let Some(vout) = htlc.transaction_output_index {
outpoint.vout = vout;
self.onchain_tx_handler.abandon_claim(&outpoint);
}
}
}
if let Some(prev_holder_commitment_tx) = &self.prev_holder_signed_commitment_tx {
if prev_holder_commitment_tx.txid != *confirmed_commitment_txid {
log_trace!(logger, "Canceling claims for previously broadcast holder commitment {}",
prev_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: prev_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &prev_holder_commitment_tx.htlc_outputs {
if let Some(vout) = htlc.transaction_output_index {
outpoint.vout = vout;
self.onchain_tx_handler.abandon_claim(&outpoint);
}
}
}
}
} else {
// No previous claim.
}
}

Expand DownExpand Up@@ -4233,7 +4235,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
}

if self.lockdown_from_offchain || self.funding_spend_seen || self.holder_tx_signed {
if self.no_further_updates_allowed() {
// Fail back HTLCs on backwards channels if they expire within
// `LATENCY_GRACE_PERIOD_BLOCKS` blocks and the channel is closed (i.e. we're at a
// point where no further off-chain updates will be accepted). If we haven't seen the
Expand DownExpand Up@@ -5384,7 +5386,7 @@ mod tests {
let dummy_commitment_tx = HolderCommitmentTransaction::dummy(&mut htlcs);

monitor.provide_latest_holder_commitment_tx(dummy_commitment_tx.clone(),
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect()).unwrap();
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect());
monitor.provide_latest_counterparty_commitment_tx(Txid::from_byte_array(Sha256::hash(b"1").to_byte_array()),
preimages_slice_to_htlc_outputs!(preimages[5..15]), 281474976710655, dummy_key, &logger);
monitor.provide_latest_counterparty_commitment_tx(Txid::from_byte_array(Sha256::hash(b"2").to_byte_array()),
Expand DownExpand Up@@ -5422,7 +5424,7 @@ mod tests {
let mut htlcs = preimages_slice_to_htlcs!(preimages[0..5]);
let dummy_commitment_tx = HolderCommitmentTransaction::dummy(&mut htlcs);
monitor.provide_latest_holder_commitment_tx(dummy_commitment_tx.clone(),
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect()).unwrap();
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect());
secret[0..32].clone_from_slice(&<Vec<u8>>::from_hex("2273e227a5b7449b6e70f1fb4652864038b1cbf9cd7c043a7d6456b7fc275ad8").unwrap());
monitor.provide_secret(281474976710653, secret.clone()).unwrap();
assert_eq!(monitor.inner.lock().unwrap().payment_preimages.len(), 12);
Expand All@@ -5433,7 +5435,7 @@ mod tests {
let mut htlcs = preimages_slice_to_htlcs!(preimages[0..3]);
let dummy_commitment_tx = HolderCommitmentTransaction::dummy(&mut htlcs);
monitor.provide_latest_holder_commitment_tx(dummy_commitment_tx,
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect()).unwrap();
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect());
secret[0..32].clone_from_slice(&<Vec<u8>>::from_hex("27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116").unwrap());
monitor.provide_secret(281474976710652, secret.clone()).unwrap();
assert_eq!(monitor.inner.lock().unwrap().payment_preimages.len(), 5);
Expand Down
46 changes: 29 additions & 17 deletions lightning/src/chain/package.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -699,8 +699,13 @@ impl PackageSolvingData {
match self {
PackageSolvingData::RevokedOutput(RevokedOutput { .. }) =>
PackageMalleability::Malleable(AggregationCluster::Unpinnable),
PackageSolvingData::RevokedHTLCOutput(..) =>
PackageMalleability::Malleable(AggregationCluster::Pinnable),
PackageSolvingData::RevokedHTLCOutput(RevokedHTLCOutput { htlc, .. }) => {
if htlc.offered {
PackageMalleability::Malleable(AggregationCluster::Unpinnable)
} else {
PackageMalleability::Malleable(AggregationCluster::Pinnable)
}
},
PackageSolvingData::CounterpartyOfferedHTLCOutput(..) =>
PackageMalleability::Malleable(AggregationCluster::Unpinnable),
PackageSolvingData::CounterpartyReceivedHTLCOutput(..) =>
Expand DownExpand Up@@ -771,10 +776,12 @@ pub struct PackageTemplate {
/// Block height at which our counterparty can potentially claim this output as well (assuming
/// they have the keys or information required to do so).
///
/// This is used primarily by external consumers to decide when an output becomes "pinnable"
/// because the counterparty can potentially spend it. It is also used internally by
/// [`Self::get_height_timer`] to identify when an output must be claimed by, depending on the
/// type of output.
/// This is used primarily to decide when an output becomes "pinnable" because the counterparty
/// can potentially spend it. It is also used internally by [`Self::get_height_timer`] to
/// identify when an output must be claimed by, depending on the type of output.
///
/// Note that for revoked counterparty HTLC outputs the value may be zero in some cases where
/// we upgraded from LDK 0.1 or prior.
counterparty_spendable_height: u32,
// Cache of package feerate committed at previous (re)broadcast. If bumping resources
// (either claimed output value or external utxo), it will keep increasing until holder
Expand DownExpand Up@@ -834,17 +841,17 @@ impl PackageTemplate {
// Now check that we only merge packages if they are both unpinnable or both
// pinnable.
let self_pinnable = self_cluster == AggregationCluster::Pinnable ||
self.counterparty_spendable_height() <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
self.counterparty_spendable_height <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
let other_pinnable = other_cluster == AggregationCluster::Pinnable ||
other.counterparty_spendable_height() <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
other.counterparty_spendable_height <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
if self_pinnable && other_pinnable {
return true;
}

let self_unpinnable = self_cluster == AggregationCluster::Unpinnable &&
self.counterparty_spendable_height() > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
self.counterparty_spendable_height > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
let other_unpinnable = other_cluster == AggregationCluster::Unpinnable &&
other.counterparty_spendable_height() > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
other.counterparty_spendable_height > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
if self_unpinnable && other_unpinnable {
return true;
}
Expand All@@ -855,13 +862,6 @@ impl PackageTemplate {
pub(crate) fn is_malleable(&self) -> bool {
matches!(self.malleability, PackageMalleability::Malleable(..))
}
/// The height at which our counterparty may be able to spend this output.
///
/// This is an important limit for aggregation as after this height our counterparty may be
/// able to pin transactions spending this output in the mempool.
pub(crate) fn counterparty_spendable_height(&self) -> u32 {
self.counterparty_spendable_height
}
pub(crate) fn previous_feerate(&self) -> u64 {
self.feerate_previous
}
Expand DownExpand Up@@ -1225,6 +1225,18 @@ impl Readable for PackageTemplate {
(4, _height_original, option), // Written with a dummy value since 0.1
(6, height_timer, option),
});
for (_, input) in &inputs {
if let PackageSolvingData::RevokedHTLCOutput(RevokedHTLCOutput { htlc, .. }) = input {
// LDK versions through 0.1 set the wrong counterparty_spendable_height for
// non-offered revoked HTLCs (ie HTLCs we sent to our counterparty which they can
// claim with a preimage immediately). Here we detect this and reset the value to
// zero, as the value is unused except for merging decisions which doesn't care
// about any values below the current height.
if !htlc.offered && htlc.cltv_expiry == counterparty_spendable_height {
counterparty_spendable_height = 0;
}
}
}
Ok(PackageTemplate {
inputs,
malleability,
Expand Down
4 changes: 2 additions & 2 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -13364,8 +13364,8 @@ where
// claim.
// Note that a `ChannelMonitor` is created with `update_id` 0 and after we
// provide it with a closure update its `update_id` will be at 1.
if !monitor.offchain_closed() || monitor.get_latest_update_id() > 1 {
should_queue_fc_update = !monitor.offchain_closed();
if !monitor.no_further_updates_allowed() || monitor.get_latest_update_id() > 1 {
should_queue_fc_update = !monitor.no_further_updates_allowed();
let mut latest_update_id = monitor.get_latest_update_id();
if should_queue_fc_update {
latest_update_id += 1;
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); 0.1 Backports for 0.1.2 by TheBlueMatt · Pull Request #3613 · lightningdevkit/rust-lightning · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e8a426c
Use fixed-size Vec allocations for BOLT12 messages
jkczyz Jan 28, 2025
f80d82e
Drop use of reserve_exact in BOLT12 tests
jkczyz Feb 6, 2025
24731d4
fix historical liquidity bucket decay
joostjager Feb 3, 2025
58a7b79
Drop `counterparty_spendable_height` accessor
TheBlueMatt Jan 27, 2025
2e80fbd
Set correct `counterparty_spendable_height` on c.p. revoked HTLCs
TheBlueMatt Jan 27, 2025
174f42e
Mark counterparty revoked offered HTLCs as `Unpinnable`
TheBlueMatt Jan 27, 2025
b5b6429
Call `peer_disconnected` after a handler refuses a connection
TheBlueMatt Jan 30, 2025
8eb3bd7
Avoid parsing `PublicKey`s when applying an unsigned chan update
TheBlueMatt Jan 31, 2025
63fa918
Properly pre-allocate `NetworkGraph` channel/node maps
TheBlueMatt Jan 31, 2025
bed125a
Ignore fees on first-hop channels from the public network graph
TheBlueMatt Feb 3, 2025
fd8eab9
Clean up `long_mpp_route_test` and `mpp_cheaper_route_test`
TheBlueMatt Feb 3, 2025
e46489d
Track `node_counter` in `RouteGraphNode`
TheBlueMatt Feb 3, 2025
b39aef6
Prefer higher-value, shorter equal-cost paths when routing
TheBlueMatt Feb 3, 2025
1976fb8
Move last-hop route handling to the common "normal" hop codepath
TheBlueMatt Feb 3, 2025
1beefb1
Add direct hops to intros after all blinded paths in pathfinding
TheBlueMatt Feb 2, 2025
ae4279d
Add `PathBuildingHop::best_path_from_hop_selected`
TheBlueMatt Feb 3, 2025
1788f80
Replace a few router `expect`s with `debug_assert` + `Err`-returns
TheBlueMatt Feb 10, 2025
bd56ddf
More completely ignore route hints which are for our own channels
TheBlueMatt Feb 10, 2025
fa8d599
Add SemVer compatibility checks to CI
tnull Jan 24, 2025
9d2449a
Fix overflow in historical scoring model point count summation
TheBlueMatt Feb 23, 2025
7c56f21
Cancel claims signed by a remote `ChannelMonitor` when reorging
TheBlueMatt Feb 25, 2025
653c482
Drop return value from `provide_latest_holder_commitment_tx`
TheBlueMatt Feb 25, 2025
6749a07
Only generate a post-close lock ChannelMonitorUpdate if we need one
TheBlueMatt Feb 25, 2025
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
57 changes: 57 additions & 0 deletions .github/workflows/semver.yml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
name: SemVer checks
on:
push:
branches-ignore:
- master
pull_request:
branches-ignore:
- master

jobs:
semver-checks:
runs-on: ubuntu-latest
steps:
- name: Checkout source code
uses: actions/checkout@v4
- name: Install Rust stable toolchain
run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile=minimal --default-toolchain stable
rustup override set stable
- name: Check SemVer with default features
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
feature-group: default-features
- name: Check SemVer *without* default features
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
feature-group: only-explicit-features
- name: Check lightning-background-processor SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
package: lightning-background-processor
feature-group: only-explicit-features
features: futures
- name: Check lightning-block-sync SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
package: lightning-block-sync
feature-group: only-explicit-features
features: rpc-client,rest-client
- name: Check lightning-transaction-sync electrum SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
manifest-path: lightning-transaction-sync/Cargo.toml
feature-group: only-explicit-features
features: electrum
- name: Check lightning-transaction-sync esplora-blocking SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
manifest-path: lightning-transaction-sync/Cargo.toml
feature-group: only-explicit-features
features: esplora-blocking
- name: Check lightning-transaction-sync esplora-async SemVer
uses: obi1kenobi/cargo-semver-checks-action@v2
with:
manifest-path: lightning-transaction-sync/Cargo.toml
feature-group: only-explicit-features
features: esplora-async
4 changes: 2 additions & 2 deletions lightning-background-processor/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -2379,8 +2379,8 @@ mod tests {
42,
53,
features,
$nodes[0].node.get_our_node_id(),
$nodes[1].node.get_our_node_id(),
$nodes[0].node.get_our_node_id().into(),
$nodes[1].node.get_our_node_id().into(),
)
.expect("Failed to update channel from partial announcement");
let original_graph_description = $nodes[0].network_graph.to_string();
Expand Down
90 changes: 46 additions & 44 deletions lightning/src/chain/channelmonitor.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -1509,8 +1509,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
fn provide_latest_holder_commitment_tx(
&self, holder_commitment_tx: HolderCommitmentTransaction,
htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>,
) -> Result<(), ()> {
self.inner.lock().unwrap().provide_latest_holder_commitment_tx(holder_commitment_tx, htlc_outputs, &Vec::new(), Vec::new()).map_err(|_| ())
) {
self.inner.lock().unwrap().provide_latest_holder_commitment_tx(holder_commitment_tx, htlc_outputs, &Vec::new(), Vec::new())
}

/// This is used to provide payment preimage(s) out-of-band during startup without updating the
Expand DownExpand Up@@ -1737,10 +1737,14 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
self.inner.lock().unwrap().get_cur_holder_commitment_number()
}

/// Gets whether we've been notified that this channel is closed by the `ChannelManager` (i.e.
/// via a [`ChannelMonitorUpdateStep::ChannelForceClosed`]).
pub(crate) fn offchain_closed(&self) -> bool {
self.inner.lock().unwrap().lockdown_from_offchain
/// Fetches whether this monitor has marked the channel as closed and will refuse any further
/// updates to the commitment transactions.
///
/// It can be marked closed in a few different ways, including via a
/// [`ChannelMonitorUpdateStep::ChannelForceClosed`] or if the channel has been closed
/// on-chain.
pub(crate) fn no_further_updates_allowed(&self) -> bool {
self.inner.lock().unwrap().no_further_updates_allowed()
}

/// Gets the `node_id` of the counterparty for this channel.
Expand DownExpand Up@@ -2901,7 +2905,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
/// is important that any clones of this channel monitor (including remote clones) by kept
/// up-to-date as our holder commitment transaction is updated.
/// Panics if set_on_holder_tx_csv has never been called.
fn provide_latest_holder_commitment_tx(&mut self, holder_commitment_tx: HolderCommitmentTransaction, mut htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>, claimed_htlcs: &[(SentHTLCId, PaymentPreimage)], nondust_htlc_sources: Vec<HTLCSource>) -> Result<(), &'static str> {
fn provide_latest_holder_commitment_tx(&mut self, holder_commitment_tx: HolderCommitmentTransaction, mut htlc_outputs: Vec<(HTLCOutputInCommitment, Option<Signature>, Option<HTLCSource>)>, claimed_htlcs: &[(SentHTLCId, PaymentPreimage)], nondust_htlc_sources: Vec<HTLCSource>) {
if htlc_outputs.iter().any(|(_, s, _)| s.is_some()) {
// If we have non-dust HTLCs in htlc_outputs, ensure they match the HTLCs in the
// `holder_commitment_tx`. In the future, we'll no longer provide the redundant data
Expand DownExpand Up@@ -2978,10 +2982,6 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
self.counterparty_fulfilled_htlcs.insert(*claimed_htlc_id, *claimed_preimage);
}
if self.holder_tx_signed {
return Err("Latest holder commitment signed has already been signed, update is rejected");
}
Ok(())
}

/// Provides a payment_hash->payment_preimage mapping. Will be automatically pruned when all
Expand DownExpand Up@@ -3202,11 +3202,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
ChannelMonitorUpdateStep::LatestHolderCommitmentTXInfo { commitment_tx, htlc_outputs, claimed_htlcs, nondust_htlc_sources } => {
log_trace!(logger, "Updating ChannelMonitor with latest holder commitment transaction info");
if self.lockdown_from_offchain { panic!(); }
if let Err(e) = self.provide_latest_holder_commitment_tx(commitment_tx.clone(), htlc_outputs.clone(), &claimed_htlcs, nondust_htlc_sources.clone()) {
log_error!(logger, "Providing latest holder commitment transaction failed/was refused:");
log_error!(logger, " {}", e);
ret = Err(());
}
self.provide_latest_holder_commitment_tx(commitment_tx.clone(), htlc_outputs.clone(), &claimed_htlcs, nondust_htlc_sources.clone());
}
ChannelMonitorUpdateStep::LatestCounterpartyCommitmentTXInfo { commitment_txid, htlc_outputs, commitment_number, their_per_commitment_point, .. } => {
log_trace!(logger, "Updating ChannelMonitor with latest counterparty commitment transaction info");
Expand DownExpand Up@@ -3286,12 +3282,16 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
}

if ret.is_ok() && (self.funding_spend_seen || self.lockdown_from_offchain || self.holder_tx_signed) && is_pre_close_update {
if ret.is_ok() && self.no_further_updates_allowed() && is_pre_close_update {
log_error!(logger, "Refusing Channel Monitor Update as counterparty attempted to update commitment after funding was spent");
Err(())
} else { ret }
}

fn no_further_updates_allowed(&self) -> bool {
self.funding_spend_seen || self.lockdown_from_offchain || self.holder_tx_signed
}

fn get_latest_update_id(&self) -> u64 {
self.latest_update_id
}
Expand DownExpand Up@@ -3564,11 +3564,16 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
return (claimable_outpoints, to_counterparty_output_info);
}
let revk_htlc_outp = RevokedHTLCOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, htlc.amount_msat / 1000, htlc.clone(), &self.onchain_tx_handler.channel_transaction_parameters.channel_type_features);
let counterparty_spendable_height = if htlc.offered {
htlc.cltv_expiry
} else {
height
};
let justice_package = PackageTemplate::build_package(
commitment_txid,
transaction_output_index,
PackageSolvingData::RevokedHTLCOutput(revk_htlc_outp),
htlc.cltv_expiry,
counterparty_spendable_height,
);
claimable_outpoints.push(justice_package);
}
Expand DownExpand Up@@ -3869,35 +3874,32 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
}
}
if self.holder_tx_signed {
// If we've signed, we may have broadcast either commitment (prev or current), and
// attempted to claim from it immediately without waiting for a confirmation.
if self.current_holder_commitment_tx.txid != *confirmed_commitment_txid {
// Cancel any pending claims for any holder commitments in case they had previously
// confirmed or been signed (in which case we will start attempting to claim without
// waiting for confirmation).
if self.current_holder_commitment_tx.txid != *confirmed_commitment_txid {
log_trace!(logger, "Canceling claims for previously broadcast holder commitment {}",
self.current_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: self.current_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &self.current_holder_commitment_tx.htlc_outputs {
if let Some(vout) = htlc.transaction_output_index {
outpoint.vout = vout;
self.onchain_tx_handler.abandon_claim(&outpoint);
}
}
}
if let Some(prev_holder_commitment_tx) = &self.prev_holder_signed_commitment_tx {
if prev_holder_commitment_tx.txid != *confirmed_commitment_txid {
log_trace!(logger, "Canceling claims for previously broadcast holder commitment {}",
self.current_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: self.current_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &self.current_holder_commitment_tx.htlc_outputs {
prev_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: prev_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &prev_holder_commitment_tx.htlc_outputs {
if let Some(vout) = htlc.transaction_output_index {
outpoint.vout = vout;
self.onchain_tx_handler.abandon_claim(&outpoint);
}
}
}
if let Some(prev_holder_commitment_tx) = &self.prev_holder_signed_commitment_tx {
if prev_holder_commitment_tx.txid != *confirmed_commitment_txid {
log_trace!(logger, "Canceling claims for previously broadcast holder commitment {}",
prev_holder_commitment_tx.txid);
let mut outpoint = BitcoinOutPoint { txid: prev_holder_commitment_tx.txid, vout: 0 };
for (htlc, _, _) in &prev_holder_commitment_tx.htlc_outputs {
if let Some(vout) = htlc.transaction_output_index {
outpoint.vout = vout;
self.onchain_tx_handler.abandon_claim(&outpoint);
}
}
}
}
} else {
// No previous claim.
}
}

Expand DownExpand Up@@ -4233,7 +4235,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
}
}

if self.lockdown_from_offchain || self.funding_spend_seen || self.holder_tx_signed {
if self.no_further_updates_allowed() {
// Fail back HTLCs on backwards channels if they expire within
// `LATENCY_GRACE_PERIOD_BLOCKS` blocks and the channel is closed (i.e. we're at a
// point where no further off-chain updates will be accepted). If we haven't seen the
Expand DownExpand Up@@ -5384,7 +5386,7 @@ mod tests {
let dummy_commitment_tx = HolderCommitmentTransaction::dummy(&mut htlcs);

monitor.provide_latest_holder_commitment_tx(dummy_commitment_tx.clone(),
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect()).unwrap();
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect());
monitor.provide_latest_counterparty_commitment_tx(Txid::from_byte_array(Sha256::hash(b"1").to_byte_array()),
preimages_slice_to_htlc_outputs!(preimages[5..15]), 281474976710655, dummy_key, &logger);
monitor.provide_latest_counterparty_commitment_tx(Txid::from_byte_array(Sha256::hash(b"2").to_byte_array()),
Expand DownExpand Up@@ -5422,7 +5424,7 @@ mod tests {
let mut htlcs = preimages_slice_to_htlcs!(preimages[0..5]);
let dummy_commitment_tx = HolderCommitmentTransaction::dummy(&mut htlcs);
monitor.provide_latest_holder_commitment_tx(dummy_commitment_tx.clone(),
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect()).unwrap();
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect());
secret[0..32].clone_from_slice(&<Vec<u8>>::from_hex("2273e227a5b7449b6e70f1fb4652864038b1cbf9cd7c043a7d6456b7fc275ad8").unwrap());
monitor.provide_secret(281474976710653, secret.clone()).unwrap();
assert_eq!(monitor.inner.lock().unwrap().payment_preimages.len(), 12);
Expand All@@ -5433,7 +5435,7 @@ mod tests {
let mut htlcs = preimages_slice_to_htlcs!(preimages[0..3]);
let dummy_commitment_tx = HolderCommitmentTransaction::dummy(&mut htlcs);
monitor.provide_latest_holder_commitment_tx(dummy_commitment_tx,
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect()).unwrap();
htlcs.into_iter().map(|(htlc, _)| (htlc, Some(dummy_sig), None)).collect());
secret[0..32].clone_from_slice(&<Vec<u8>>::from_hex("27cddaa5624534cb6cb9d7da077cf2b22ab21e9b506fd4998a51d54502e99116").unwrap());
monitor.provide_secret(281474976710652, secret.clone()).unwrap();
assert_eq!(monitor.inner.lock().unwrap().payment_preimages.len(), 5);
Expand Down
46 changes: 29 additions & 17 deletions lightning/src/chain/package.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -699,8 +699,13 @@ impl PackageSolvingData {
match self {
PackageSolvingData::RevokedOutput(RevokedOutput { .. }) =>
PackageMalleability::Malleable(AggregationCluster::Unpinnable),
PackageSolvingData::RevokedHTLCOutput(..) =>
PackageMalleability::Malleable(AggregationCluster::Pinnable),
PackageSolvingData::RevokedHTLCOutput(RevokedHTLCOutput { htlc, .. }) => {
if htlc.offered {
PackageMalleability::Malleable(AggregationCluster::Unpinnable)
} else {
PackageMalleability::Malleable(AggregationCluster::Pinnable)
}
},
PackageSolvingData::CounterpartyOfferedHTLCOutput(..) =>
PackageMalleability::Malleable(AggregationCluster::Unpinnable),
PackageSolvingData::CounterpartyReceivedHTLCOutput(..) =>
Expand DownExpand Up@@ -771,10 +776,12 @@ pub struct PackageTemplate {
/// Block height at which our counterparty can potentially claim this output as well (assuming
/// they have the keys or information required to do so).
///
/// This is used primarily by external consumers to decide when an output becomes "pinnable"
/// because the counterparty can potentially spend it. It is also used internally by
/// [`Self::get_height_timer`] to identify when an output must be claimed by, depending on the
/// type of output.
/// This is used primarily to decide when an output becomes "pinnable" because the counterparty
/// can potentially spend it. It is also used internally by [`Self::get_height_timer`] to
/// identify when an output must be claimed by, depending on the type of output.
///
/// Note that for revoked counterparty HTLC outputs the value may be zero in some cases where
/// we upgraded from LDK 0.1 or prior.
counterparty_spendable_height: u32,
// Cache of package feerate committed at previous (re)broadcast. If bumping resources
// (either claimed output value or external utxo), it will keep increasing until holder
Expand DownExpand Up@@ -834,17 +841,17 @@ impl PackageTemplate {
// Now check that we only merge packages if they are both unpinnable or both
// pinnable.
let self_pinnable = self_cluster == AggregationCluster::Pinnable ||
self.counterparty_spendable_height() <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
self.counterparty_spendable_height <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
let other_pinnable = other_cluster == AggregationCluster::Pinnable ||
other.counterparty_spendable_height() <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
other.counterparty_spendable_height <= cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
if self_pinnable && other_pinnable {
return true;
}

let self_unpinnable = self_cluster == AggregationCluster::Unpinnable &&
self.counterparty_spendable_height() > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
self.counterparty_spendable_height > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
let other_unpinnable = other_cluster == AggregationCluster::Unpinnable &&
other.counterparty_spendable_height() > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
other.counterparty_spendable_height > cur_height + COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE;
if self_unpinnable && other_unpinnable {
return true;
}
Expand All@@ -855,13 +862,6 @@ impl PackageTemplate {
pub(crate) fn is_malleable(&self) -> bool {
matches!(self.malleability, PackageMalleability::Malleable(..))
}
/// The height at which our counterparty may be able to spend this output.
///
/// This is an important limit for aggregation as after this height our counterparty may be
/// able to pin transactions spending this output in the mempool.
pub(crate) fn counterparty_spendable_height(&self) -> u32 {
self.counterparty_spendable_height
}
pub(crate) fn previous_feerate(&self) -> u64 {
self.feerate_previous
}
Expand DownExpand Up@@ -1225,6 +1225,18 @@ impl Readable for PackageTemplate {
(4, _height_original, option), // Written with a dummy value since 0.1
(6, height_timer, option),
});
for (_, input) in &inputs {
if let PackageSolvingData::RevokedHTLCOutput(RevokedHTLCOutput { htlc, .. }) = input {
// LDK versions through 0.1 set the wrong counterparty_spendable_height for
// non-offered revoked HTLCs (ie HTLCs we sent to our counterparty which they can
// claim with a preimage immediately). Here we detect this and reset the value to
// zero, as the value is unused except for merging decisions which doesn't care
// about any values below the current height.
if !htlc.offered && htlc.cltv_expiry == counterparty_spendable_height {
counterparty_spendable_height = 0;
}
}
}
Ok(PackageTemplate {
inputs,
malleability,
Expand Down
4 changes: 2 additions & 2 deletions lightning/src/ln/channelmanager.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -13364,8 +13364,8 @@ where
// claim.
// Note that a `ChannelMonitor` is created with `update_id` 0 and after we
// provide it with a closure update its `update_id` will be at 1.
if !monitor.offchain_closed() || monitor.get_latest_update_id() > 1 {
should_queue_fc_update = !monitor.offchain_closed();
if !monitor.no_further_updates_allowed() || monitor.get_latest_update_id() > 1 {
should_queue_fc_update = !monitor.no_further_updates_allowed();
let mut latest_update_id = monitor.get_latest_update_id();
if should_queue_fc_update {
latest_update_id += 1;
Expand Down
Loading