Skip to content

Commit f178d17

Browse files
JssDWtcdecker
authored andcommitted
tramp: account for retried payments
Ensure trampoline payments follow the following behavior: - When a payment with the same payment hash had already completed, return a completed response. - When a payment with the same payment hash is pending, return an error (ideally it should wait for payment parts to return) - When a payment for the same payment hash is retried, bump the group id.
1 parent a8b7eff commit f178d17

File tree

1 file changed

+68
-11
lines changed

1 file changed

+68
-11
lines changed

libs/gl-plugin/src/tramp.rs

Lines changed: 68 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,72 @@ pub async fn trampolinepay(
6565
) -> Result<cln_rpc::model::responses::PayResponse> {
6666
let node_id = cln_rpc::primitives::PublicKey::from_slice(&req.trampoline_node_id[..])?;
6767

68+
let mut rpc = ClnRpc::new(&rpc_path).await?;
69+
70+
// Extract the amount from the bolt11 or use the set amount field
71+
// Return an error if there is a mismatch.
72+
let decoded = rpc
73+
.call_typed(&cln_rpc::model::requests::DecodepayRequest {
74+
bolt11: req.bolt11.clone(),
75+
description: None,
76+
})
77+
.await?;
78+
79+
let send_pays = rpc
80+
.call_typed(&cln_rpc::model::requests::ListsendpaysRequest {
81+
payment_hash: Some(decoded.payment_hash.clone()),
82+
bolt11: None,
83+
index: None,
84+
limit: None,
85+
start: None,
86+
status: None,
87+
})
88+
.await?;
89+
if send_pays
90+
.payments
91+
.iter()
92+
.any(|p| p.status != cln_rpc::model::responses::ListsendpaysPaymentsStatus::FAILED)
93+
{
94+
let resp = rpc
95+
.call_typed(&cln_rpc::model::requests::WaitsendpayRequest {
96+
payment_hash: decoded.payment_hash.clone(),
97+
groupid: None,
98+
partid: None,
99+
timeout: None,
100+
})
101+
.await?;
102+
103+
let preimage = match resp.payment_preimage {
104+
Some(preimage) => preimage,
105+
None => return Err(anyhow!("got completed payment part without preimage")),
106+
};
107+
return Ok(cln_rpc::model::responses::PayResponse {
108+
amount_msat: resp.amount_msat.unwrap_or(Amount::from_msat(0)),
109+
amount_sent_msat: resp.amount_sent_msat,
110+
created_at: 0.,
111+
destination: resp.destination,
112+
parts: match resp.partid {
113+
Some(0) => 1,
114+
Some(partid) => partid as u32,
115+
None => 1,
116+
},
117+
payment_hash: resp.payment_hash,
118+
payment_preimage: preimage,
119+
status: match resp.status {
120+
cln_rpc::model::responses::WaitsendpayStatus::COMPLETE => {
121+
cln_rpc::model::responses::PayStatus::COMPLETE
122+
}
123+
},
124+
warning_partial_completion: None,
125+
});
126+
}
127+
128+
let max_group_id = send_pays
129+
.payments
130+
.iter()
131+
.map(|p| p.groupid)
132+
.max()
133+
.unwrap_or(0);
68134
log::debug!(
69135
"New trampoline payment via {}: {} ",
70136
node_id.to_hex(),
@@ -78,7 +144,7 @@ pub async fn trampolinepay(
78144
.await?;
79145

80146
// Check if peer has signaled that they support forward trampoline pays:
81-
let mut rpc = ClnRpc::new(&rpc_path).await?;
147+
82148
let features = rpc
83149
.call_typed(&cln_rpc::model::requests::ListpeersRequest {
84150
id: Some(node_id),
@@ -95,15 +161,6 @@ pub async fn trampolinepay(
95161

96162
feature_guard(features, TRAMPOLINE_FEATURE_BIT)?;
97163

98-
// Extract the amount from the bolt11 or use the set amount field
99-
// Return an error if there is a mismatch.
100-
let decoded = rpc
101-
.call_typed(&cln_rpc::model::requests::DecodepayRequest {
102-
bolt11: req.bolt11.clone(),
103-
description: None,
104-
})
105-
.await?;
106-
107164
let amount_msat = match (as_option(req.amount_msat), decoded.amount_msat) {
108165
(None, None) => {
109166
return Err(anyhow!(
@@ -225,7 +282,7 @@ pub async fn trampolinepay(
225282
let payload_hex = hex::encode(SerializedTlvStream::to_bytes(payload));
226283

227284
let mut part_id = if choosen.len() == 1 { 0 } else { 1 };
228-
let group_id = 1;
285+
let group_id = max_group_id + 1;
229286
let mut handles: Vec<
230287
tokio::task::JoinHandle<
231288
std::result::Result<cln_rpc::model::responses::WaitsendpayResponse, anyhow::Error>,

0 commit comments

Comments
 (0)