Skip to content

Commit 055a2e7

Browse files
committed
lsp_plugin: add cleanup on "on_invoice_payment"
The `on_invoice_payment` hook is called when core-lightning successfully collected all parts to an invoice. We'll use this to clean up the the datastore when an invoice completes. Caveat: This will be called on every succesfull invoice payment, we may improve this in the future. Signed-off-by: Peter Neuroth <[email protected]>
1 parent 2ab8119 commit 055a2e7

File tree

3 files changed

+49
-2
lines changed

3 files changed

+49
-2
lines changed

plugins/lsps-plugin/src/client.rs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use anyhow::{anyhow, bail, Context};
2+
use bitcoin::hashes::{hex::FromHex, sha256, Hash};
23
use chrono::{Duration, Utc};
34
use cln_lsps::jsonrpc::client::JsonRpcClient;
45
use cln_lsps::lsps0::primitives::Msat;
@@ -8,7 +9,8 @@ use cln_lsps::lsps0::{
89
};
910
use cln_lsps::lsps2::cln::tlv::encode_tu64;
1011
use cln_lsps::lsps2::cln::{
11-
HtlcAcceptedRequest, HtlcAcceptedResponse, TLV_FORWARD_AMT, TLV_PAYMENT_SECRET,
12+
HtlcAcceptedRequest, HtlcAcceptedResponse, InvoicePaymentRequest, TLV_FORWARD_AMT,
13+
TLV_PAYMENT_SECRET,
1214
};
1315
use cln_lsps::lsps2::model::{
1416
compute_opening_fee, Lsps2BuyRequest, Lsps2BuyResponse, Lsps2GetInfoRequest,
@@ -80,6 +82,7 @@ async fn main() -> Result<(), anyhow::Error> {
8082
"Requests a new jit channel from LSP and returns the matching invoice",
8183
on_lsps_jitchannel,
8284
)
85+
.hook("invoice_payment", on_invoice_payment)
8386
.hook("htlc_accepted", on_htlc_accepted)
8487
.hook("openchannel", on_openchannel)
8588
.configure()
@@ -251,7 +254,7 @@ async fn on_lsps_lsps2_approve(
251254
) -> Result<serde_json::Value, anyhow::Error> {
252255
let req: ClnRpcLsps2Approve = serde_json::from_value(v)?;
253256
let ds_rec = DatastoreRecord {
254-
jit_channel_scid: req.jit_channel_scid,
257+
jit_channel_scid: req.jit_channel_scid.clone(),
255258
client_trusts_lsp: req.client_trusts_lsp.unwrap_or_default(),
256259
};
257260
let ds_rec_json = serde_json::to_string(&ds_rec)?;
@@ -489,6 +492,29 @@ async fn on_lsps_jitchannel(
489492
Ok(serde_json::to_value(out)?)
490493
}
491494

495+
async fn on_invoice_payment(
496+
p: cln_plugin::Plugin<State>,
497+
v: serde_json::Value,
498+
) -> Result<serde_json::Value, anyhow::Error> {
499+
let req: InvoicePaymentRequest = serde_json::from_value(v).context("invalid hook request")?;
500+
let preimage = <[u8; 32]>::from_hex(&req.payment.preimage).context("invalid preimage hex")?;
501+
let hash = payment_hash(&preimage);
502+
503+
// Delete DS-entries.
504+
let dir = p.configuration().lightning_dir;
505+
let rpc_path = Path::new(&dir).join(&p.configuration().rpc_file);
506+
let mut cln_client = cln_rpc::ClnRpc::new(rpc_path.clone()).await?;
507+
cln_client
508+
.call_typed(&DeldatastoreRequest {
509+
key: vec!["lsps".to_string(), "invoice".to_string(), hash.to_string()],
510+
generation: None,
511+
})
512+
.await
513+
.ok();
514+
515+
Ok(serde_json::json!({"result": "continue"}))
516+
}
517+
492518
async fn on_htlc_accepted(
493519
p: cln_plugin::Plugin<State>,
494520
v: serde_json::Value,
@@ -761,6 +787,10 @@ pub fn gen_rand_preimage_hex<R: Rng + CryptoRng>(rng: &mut R) -> String {
761787
hex::encode(&pre)
762788
}
763789

790+
pub fn payment_hash(preimage: &[u8]) -> sha256::Hash {
791+
sha256::Hash::hash(preimage)
792+
}
793+
764794
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
765795
struct LspsBuyJitChannelResponse {
766796
bolt11: String,

plugins/lsps-plugin/src/lsps2/cln.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,17 @@ impl HtlcAcceptedResponse {
117117
}
118118
}
119119

120+
#[derive(Debug, Deserialize)]
121+
pub struct InvoicePaymentRequest {
122+
pub payment: InvoicePaymentRequestPayment,
123+
}
124+
125+
#[derive(Debug, Deserialize)]
126+
pub struct InvoicePaymentRequestPayment {
127+
pub label: String,
128+
pub preimage: String,
129+
pub msat: u64,
130+
}
120131
/// Deserializes a lowercase hex string to a `Vec<u8>`.
121132
pub fn from_hex<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
122133
where

tests/test_cln_lsps.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,8 @@ def test_lsps2_buyjitchannel_no_mpp_var_invoice(node_factory, bitcoind):
191191
chs = l1.rpc.listpeerchannels()["channels"]
192192
assert len(chs) == 1
193193

194+
# Check that the client cleaned up after themselves.
195+
assert l1.rpc.listdatastore(["lsps"]) == {"datastore": []}
194196

195197

196198
def test_lsps2_buyjitchannel_mpp_fixed_invoice(node_factory, bitcoind):
@@ -292,6 +294,10 @@ def test_lsps2_buyjitchannel_mpp_fixed_invoice(node_factory, bitcoind):
292294
assert len(chs) == 1
293295

294296

297+
# Check that the client cleaned up after themselves.
298+
assert l1.rpc.listdatastore("lsps") == {"datastore": []}
299+
300+
295301
def test_lsps2_non_approved_zero_conf(node_factory, bitcoind):
296302
"""Checks that we don't allow zerof_conf channels from an LSP if we did
297303
not approve it first.

0 commit comments

Comments
 (0)