breez_sdk_core/persist/
send_pays.rs

1use rusqlite::named_params;
2use strum_macros::FromRepr;
3
4use super::{db::SqliteStorage, error::PersistResult};
5
6#[derive(FromRepr)]
7#[repr(i32)]
8pub(crate) enum SendPayStatus {
9    Pending = 0,
10    Failed = 1,
11    Complete = 2,
12}
13
14pub(crate) struct SendPay {
15    pub created_index: u64,
16    pub updated_index: Option<u64>,
17
18    // The cln_grpc groupid (u64) can contain 64 bit values (e.g. > 2^63). This leads to errors when
19    // trying to persist them in an SQLite INTEGER column, which holds signed 64 bit numbers (63 bit
20    // value + 1 bit for the sign).
21    // To map the full range of u64 values in SQLite, we treat this field as String, stored as TEXT.
22    pub groupid: String,
23
24    pub partid: Option<u64>,
25    pub payment_hash: Vec<u8>,
26    pub status: SendPayStatus,
27    pub amount_msat: Option<u64>,
28    pub destination: Option<Vec<u8>>,
29    pub created_at: u64,
30    pub amount_sent_msat: Option<u64>,
31    pub label: Option<String>,
32    pub bolt11: Option<String>,
33    pub description: Option<String>,
34    pub bolt12: Option<String>,
35    pub payment_preimage: Option<Vec<u8>>,
36    pub erroronion: Option<Vec<u8>>,
37}
38
39impl SqliteStorage {
40    pub(crate) fn insert_send_pays(&self, send_pays: &[SendPay]) -> PersistResult<()> {
41        let conn = self.get_connection()?;
42        let mut stmt = conn.prepare(
43            r#"INSERT OR REPLACE INTO send_pays (created_index, updated_index, 
44                groupid, partid, payment_hash, status, amount_msat, 
45                destination, created_at, amount_sent_msat, label, bolt11, 
46                description, bolt12, payment_preimage, erroronion) 
47                VALUES (:created_index, :updated_index, 
48                    :groupid, :partid, :payment_hash, :status, :amount_msat, 
49                    :destination, :created_at, :amount_sent_msat, :label, :bolt11, 
50                    :description, :bolt12, :payment_preimage, :erroronion)"#,
51        )?;
52        for send_pay in send_pays {
53            let status: i32 = match send_pay.status {
54                SendPayStatus::Pending => 0,
55                SendPayStatus::Failed => 1,
56                SendPayStatus::Complete => 2,
57            };
58            stmt.execute(named_params! {
59                ":created_index": send_pay.created_index,
60                ":updated_index": send_pay.updated_index,
61                ":groupid": send_pay.groupid,
62                ":partid": send_pay.partid,
63                ":payment_hash": send_pay.payment_hash,
64                ":status": status,
65                ":amount_msat": send_pay.amount_msat,
66                ":destination": send_pay.destination,
67                ":created_at": send_pay.created_at,
68                ":amount_sent_msat": send_pay.amount_sent_msat,
69                ":label": send_pay.label,
70                ":bolt11": send_pay.bolt11,
71                ":description": send_pay.description,
72                ":bolt12": send_pay.bolt12,
73                ":payment_preimage": send_pay.payment_preimage,
74                ":erroronion": send_pay.erroronion,
75            })?;
76        }
77
78        Ok(())
79    }
80
81    pub(crate) fn list_send_pays(
82        &self,
83        hash_groups: &[(Vec<u8>, String)],
84    ) -> PersistResult<Vec<SendPay>> {
85        let conn = self.get_connection()?;
86        let mut stmt = conn.prepare(
87            r#"SELECT created_index, updated_index, groupid, partid, 
88               payment_hash, status, amount_msat, destination, created_at, 
89               amount_sent_msat, label, bolt11, description, bolt12, 
90               payment_preimage, erroronion
91               FROM send_pays
92               WHERE payment_hash = :payment_hash AND groupid = :groupid
93               ORDER BY created_index"#,
94        )?;
95        let mut send_pays = Vec::new();
96        for hash_group in hash_groups {
97            let rows: Vec<_> = stmt
98                .query_map(
99                    named_params! {
100                        ":payment_hash": hash_group.0,
101                        ":groupid": hash_group.1,
102                    },
103                    |row| {
104                        let status: i32 = row.get("status")?;
105                        Ok(SendPay {
106                            amount_msat: row.get("amount_msat")?,
107                            amount_sent_msat: row.get("amount_sent_msat")?,
108                            created_index: row.get("created_index")?,
109                            updated_index: row.get("updated_index")?,
110                            groupid: row.get("groupid")?,
111                            partid: row.get("partid")?,
112                            payment_hash: row.get("payment_hash")?,
113                            status: SendPayStatus::from_repr(status)
114                                .ok_or(rusqlite::Error::IntegralValueOutOfRange(5, 2))?,
115                            destination: row.get("destination")?,
116                            created_at: row.get("created_at")?,
117                            label: row.get("label")?,
118                            bolt11: row.get("bolt11")?,
119                            description: row.get("description")?,
120                            bolt12: row.get("bolt12")?,
121                            payment_preimage: row.get("payment_preimage")?,
122                            erroronion: row.get("erroronion")?,
123                        })
124                    },
125                )?
126                .collect::<Result<Vec<SendPay>, _>>()?;
127            for row in rows {
128                send_pays.push(row);
129            }
130        }
131        Ok(send_pays)
132    }
133}