Skip to content

Commit 181e09d

Browse files
committed
Avoid reusing on-chain rebalance triggers
Repeated on-chain rebalance events could promote metadata that was already marked as a rebalance trigger. Skip those stale candidates and treat a failed splice metadata update as non-fatal so duplicate events cannot abort the wallet process.
1 parent 75dfcc0 commit 181e09d

1 file changed

Lines changed: 41 additions & 36 deletions

File tree

orange-sdk/src/rebalancer.rs

Lines changed: 41 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -204,47 +204,52 @@ impl RebalanceTrigger for OrangeTrigger {
204204
let txs = self.ln_wallet.list_payments();
205205
let new = txs
206206
.into_iter()
207-
.filter(|t| {
208-
t.status == PaymentStatus::Succeeded
209-
&& t.direction == PaymentDirection::Inbound
210-
&& matches!(t.kind, PaymentKind::Onchain { .. })
211-
&& t.latest_update_timestamp >= onchain_sync_time
207+
.filter_map(|t| {
208+
if t.status != PaymentStatus::Succeeded
209+
|| t.direction != PaymentDirection::Inbound
210+
|| t.latest_update_timestamp <= onchain_sync_time
211+
{
212+
return None;
213+
}
214+
let PaymentKind::Onchain { txid, .. } = t.kind else {
215+
return None;
216+
};
217+
let trigger = PaymentId::SelfCustodial(txid.to_byte_array());
218+
// Only payments can be promoted into rebalance triggers. If this
219+
// metadata was already promoted by a previous rebalance, selecting it
220+
// again would make the event handler reject the duplicate promotion.
221+
let can_mark_as_trigger =
222+
self.tx_metadata.read().get(&trigger).is_none_or(|metadata| {
223+
matches!(metadata.ty, TxType::Payment { .. })
224+
});
225+
if can_mark_as_trigger { Some((t, txid, trigger)) } else { None }
212226
})
213-
.max_by_key(|t| t.amount_msat);
227+
.max_by_key(|(t, _, _)| t.amount_msat);
214228
match new {
215-
Some(new) => {
216-
if let PaymentKind::Onchain { txid, .. } = new.kind {
217-
// make sure we have a metadata entry for the triggering transaction
218-
let trigger = PaymentId::SelfCustodial(txid.to_byte_array());
219-
if self.tx_metadata.read().get(&trigger).is_none() {
220-
self.tx_metadata
221-
.insert(
222-
trigger,
223-
TxMetadata {
224-
ty: TxType::Payment {
225-
ty: PaymentType::IncomingOnChain {
226-
txid: Some(txid),
227-
},
229+
Some((_, txid, trigger)) => {
230+
// make sure we have a metadata entry for the triggering transaction
231+
if self.tx_metadata.read().get(&trigger).is_none() {
232+
self.tx_metadata
233+
.insert(
234+
trigger,
235+
TxMetadata {
236+
ty: TxType::Payment {
237+
ty: PaymentType::IncomingOnChain {
238+
txid: Some(txid),
228239
},
229-
time: SystemTime::now()
230-
.duration_since(SystemTime::UNIX_EPOCH)
231-
.unwrap(),
232240
},
233-
)
234-
.await;
235-
}
236-
237-
Some(TriggerParams {
238-
amount: Amount::from_sats(spendable).expect("valid amount"),
239-
id: txid.to_byte_array(),
240-
})
241-
} else {
242-
debug_assert!(
243-
false,
244-
"PaymentKind::Onchain should always be present for onchain payments"
245-
);
246-
None
241+
time: SystemTime::now()
242+
.duration_since(SystemTime::UNIX_EPOCH)
243+
.unwrap(),
244+
},
245+
)
246+
.await;
247247
}
248+
249+
Some(TriggerParams {
250+
amount: Amount::from_sats(spendable).expect("valid amount"),
251+
id: txid.to_byte_array(),
252+
})
248253
},
249254
None => {
250255
log_warn!(

0 commit comments

Comments
 (0)