Skip to content

Commit cd32333

Browse files
committed
Implement LSPS1ServiceHandler persistence and state pruning
We follow the model already employed in LSPS2/LSPS5 and implement state pruning and persistence for `LSPS1ServiceHandler` state.
1 parent a06b91e commit cd32333

File tree

5 files changed

+363
-25
lines changed

5 files changed

+363
-25
lines changed

lightning-liquidity/src/lsps1/peer_state.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use core::fmt;
2626
pub(super) struct PeerState {
2727
outbound_channels_by_order_id: HashMap<LSPS1OrderId, ChannelOrder>,
2828
pending_requests: HashMap<LSPSRequestId, LSPS1Request>,
29+
needs_persist: bool,
2930
}
3031

3132
impl PeerState {
@@ -43,6 +44,7 @@ impl PeerState {
4344
channel_details,
4445
};
4546
self.outbound_channels_by_order_id.insert(order_id, channel_order.clone());
47+
self.needs_persist |= true;
4648
channel_order
4749
}
4850

@@ -66,6 +68,7 @@ impl PeerState {
6668
.ok_or(PeerStateError::UnknownOrderId)?;
6769
order.order_state = order_state;
6870
order.channel_details = channel_details;
71+
self.needs_persist |= true;
6972
Ok(())
7073
}
7174

@@ -88,11 +91,39 @@ impl PeerState {
8891
pub(super) fn has_active_requests(&self) -> bool {
8992
!self.outbound_channels_by_order_id.is_empty()
9093
}
94+
95+
pub(super) fn needs_persist(&self) -> bool {
96+
self.needs_persist
97+
}
98+
99+
pub(super) fn set_needs_persist(&mut self, needs_persist: bool) {
100+
self.needs_persist = needs_persist;
101+
}
102+
103+
pub(super) fn is_prunable(&self) -> bool {
104+
// Return whether the entire state is empty.
105+
self.pending_requests.is_empty() && self.outbound_channels_by_order_id.is_empty()
106+
}
107+
108+
pub(super) fn prune_pending_requests(&mut self) {
109+
self.pending_requests.clear()
110+
}
111+
112+
pub(super) fn prune_expired_request_state(&mut self) {
113+
self.outbound_channels_by_order_id.retain(|_order_id, entry| {
114+
if entry.is_prunable() {
115+
self.needs_persist |= true;
116+
return false;
117+
}
118+
true
119+
});
120+
}
91121
}
92122

93123
impl_writeable_tlv_based!(PeerState, {
94124
(0, outbound_channels_by_order_id, required),
95125
(_unused, pending_requests, (static_value, new_hash_map())),
126+
(_unused, needs_persist, (static_value, false)),
96127
});
97128

98129
#[derive(Debug, Copy, Clone)]
@@ -121,6 +152,29 @@ pub(super) struct ChannelOrder {
121152
pub(super) channel_details: Option<LSPS1ChannelInfo>,
122153
}
123154

155+
impl ChannelOrder {
156+
fn is_prunable(&self) -> bool {
157+
let all_payment_details_expired;
158+
#[cfg(feature = "time")]
159+
{
160+
all_payment_details_expired =
161+
self.payment_details.bolt11.as_ref().is_none_or(|d| d.expires_at.is_past())
162+
&& self.payment_details.bolt12.as_ref().is_none_or(|d| d.expires_at.is_past())
163+
&& self.payment_details.onchain.as_ref().is_none_or(|d| d.expires_at.is_past());
164+
}
165+
#[cfg(not(feature = "time"))]
166+
{
167+
// TODO: We need to find a way to check expiry times in no-std builds.
168+
all_payment_details_expired = false;
169+
}
170+
171+
let created_or_failed =
172+
matches!(self.order_state, LSPS1OrderState::Created | LSPS1OrderState::Failed);
173+
174+
all_payment_details_expired && created_or_failed
175+
}
176+
}
177+
124178
impl_writeable_tlv_based!(ChannelOrder, {
125179
(0, order_params, required),
126180
(2, order_state, required),

0 commit comments

Comments
 (0)