Skip to content

Commit ed6589e

Browse files
committed
Maintain order of yielded claim events
Since the claim events are stored internally within a HashMap, they will be yielded in a random order once dispatched. Claim events may be invalidated if a conflicting claim has confirmed on-chain and we need to generate a new claim event; the randomized order could result in the new claim event being handled prior to the previous. To maintain the order in which the claim events are generated, we track them in a Vec instead and ensure only one instance of a PackageId only ever exists within it. This would have certain performance implications, but since we're bounded by the total number of HTLCs in a commitment anyway, we're comfortable with taking the cost.
1 parent fa2ced3 commit ed6589e

File tree

1 file changed

+33
-13
lines changed

1 file changed

+33
-13
lines changed

lightning/src/chain/onchaintx.rs

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -248,8 +248,10 @@ pub struct OnchainTxHandler<ChannelSigner: WriteableEcdsaChannelSigner> {
248248
pub(crate) pending_claim_requests: HashMap<PackageID, PackageTemplate>,
249249
#[cfg(not(test))]
250250
pending_claim_requests: HashMap<PackageID, PackageTemplate>,
251+
252+
// Used to track external events that need to be forwarded to the `ChainMonitor`.
251253
#[cfg(anchors)]
252-
pending_claim_events: HashMap<PackageID, ClaimEvent>,
254+
pending_claim_events: Vec<(PackageID, ClaimEvent)>,
253255

254256
// Used to link outpoints claimed in a connected block to a pending claim request. The keys
255257
// represent the outpoints that our `ChannelMonitor` has detected we have keys/scripts to
@@ -426,7 +428,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
426428
pending_claim_requests,
427429
onchain_events_awaiting_threshold_conf,
428430
#[cfg(anchors)]
429-
pending_claim_events: HashMap::new(),
431+
pending_claim_events: Vec::new(),
430432
secp_ctx,
431433
})
432434
}
@@ -447,8 +449,7 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
447449
locktimed_packages: BTreeMap::new(),
448450
onchain_events_awaiting_threshold_conf: Vec::new(),
449451
#[cfg(anchors)]
450-
pending_claim_events: HashMap::new(),
451-
452+
pending_claim_events: Vec::new(),
452453
secp_ctx,
453454
}
454455
}
@@ -463,9 +464,9 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
463464

464465
#[cfg(anchors)]
465466
pub(crate) fn get_and_clear_pending_claim_events(&mut self) -> Vec<ClaimEvent> {
466-
let mut ret = HashMap::new();
467-
swap(&mut ret, &mut self.pending_claim_events);
468-
ret.into_iter().map(|(_, event)| event).collect::<Vec<_>>()
467+
let mut events = Vec::new();
468+
swap(&mut events, &mut self.pending_claim_events);
469+
events.into_iter().map(|(_, event)| event).collect()
469470
}
470471

471472
/// Lightning security model (i.e being able to redeem/timeout HTLC or penalize counterparty
@@ -709,7 +710,8 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
709710
package_id
710711
},
711712
};
712-
self.pending_claim_events.insert(package_id, claim_event);
713+
debug_assert_eq!(self.pending_claim_events.iter().filter(|entry| entry.0 == package_id).count(), 0);
714+
self.pending_claim_events.push((package_id, claim_event));
713715
package_id
714716
},
715717
};
@@ -794,6 +796,14 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
794796
//TODO: recompute soonest_timelock to avoid wasting a bit on fees
795797
if at_least_one_drop {
796798
bump_candidates.insert(*package_id, request.clone());
799+
// If we have any pending claim events for the request being updated
800+
// that have yet to be consumed, we'll remove them since they will
801+
// end up producing an invalid transaction by double spending
802+
// input(s) that already have a confirmed spend. If such spend is
803+
// reorged out of the chain, then we'll attempt to re-spend the
804+
// inputs once we see it.
805+
#[cfg(anchors)]
806+
self.pending_claim_events.retain(|entry| entry.0 != *package_id);
797807
}
798808
}
799809
break; //No need to iterate further, either tx is our or their
@@ -829,9 +839,9 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
829839
log_debug!(logger, "Removing claim tracking for {} due to maturation of claim package {}.",
830840
outpoint, log_bytes!(package_id));
831841
self.claimable_outpoints.remove(outpoint);
832-
#[cfg(anchors)]
833-
self.pending_claim_events.remove(&package_id);
834842
}
843+
#[cfg(anchors)]
844+
self.pending_claim_events.retain(|(id, _)| *id != package_id);
835845
}
836846
},
837847
OnchainEvent::ContentiousOutpoint { package } => {
@@ -866,7 +876,12 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
866876
#[cfg(anchors)]
867877
OnchainClaim::Event(claim_event) => {
868878
log_info!(logger, "Yielding RBF-bumped onchain event to spend inputs {:?}", request.outpoints());
869-
self.pending_claim_events.insert(*package_id, claim_event);
879+
if let Some((existing_claim_idx, _)) = self.pending_claim_events.iter().enumerate()
880+
.find(|(_, entry)| entry.0 == *package_id)
881+
{
882+
self.pending_claim_events.remove(existing_claim_idx);
883+
}
884+
self.pending_claim_events.push((*package_id, claim_event));
870885
},
871886
}
872887
if let Some(request) = self.pending_claim_requests.get_mut(package_id) {
@@ -930,7 +945,7 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
930945
self.onchain_events_awaiting_threshold_conf.push(entry);
931946
}
932947
}
933-
for ((_package_id, _), request) in bump_candidates.iter_mut() {
948+
for ((_package_id, _), ref mut request) in bump_candidates.iter_mut() {
934949
if let Some((new_timer, new_feerate, bump_claim)) = self.generate_claim(height, &request, fee_estimator, &&*logger) {
935950
request.set_timer(new_timer);
936951
request.set_feerate(new_feerate);
@@ -942,7 +957,12 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
942957
#[cfg(anchors)]
943958
OnchainClaim::Event(claim_event) => {
944959
log_info!(logger, "Yielding onchain event after reorg to spend inputs {:?}", request.outpoints());
945-
self.pending_claim_events.insert(_package_id, claim_event);
960+
if let Some((existing_claim_idx, _)) = self.pending_claim_events.iter().enumerate()
961+
.find(|(_, entry)| entry.0 == *_package_id)
962+
{
963+
self.pending_claim_events.remove(existing_claim_idx);
964+
}
965+
self.pending_claim_events.push((*_package_id, claim_event));
946966
},
947967
}
948968
}

0 commit comments

Comments
 (0)