1use nostr_sdk::prelude::*;
14use rusqlite::params;
15
16#[repr(i64)]
20#[derive(Copy, Clone, Debug, PartialEq, Eq)]
21pub enum WrapRole {
22 Recipient = 0,
24 SelfSend = 1,
26 Retry = 2,
29}
30
31impl WrapRole {
32 fn from_i64(v: i64) -> Self {
33 match v {
34 1 => Self::SelfSend,
35 2 => Self::Retry,
36 _ => Self::Recipient,
37 }
38 }
39}
40
41#[derive(Clone, Debug)]
42pub struct StoredWrapKey {
43 pub wrap_event_id: EventId,
44 pub rumor_id: EventId,
45 pub recipient_pubkey: PublicKey,
46 pub role: WrapRole,
47 pub secret: SecretKey,
48 pub relay_urls: Vec<String>,
51}
52
53pub fn store_wrap_key(
56 wrap_event_id: &EventId,
57 rumor_id: &EventId,
58 recipient_pubkey: &PublicKey,
59 role: WrapRole,
60 secret: &SecretKey,
61 relay_urls: &[String],
62) -> Result<(), String> {
63 let conn = super::get_write_connection_guard_static()?;
64 let now = std::time::SystemTime::now()
65 .duration_since(std::time::UNIX_EPOCH)
66 .unwrap()
67 .as_secs() as i64;
68 let relays_json = serde_json::to_string(relay_urls)
69 .map_err(|e| format!("Failed to encode relay urls: {}", e))?;
70 conn.execute(
71 "INSERT OR REPLACE INTO nip17_wrap_keys
72 (wrap_event_id, rumor_id, recipient_pubkey, role, secret, relay_urls, created_at)
73 VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
74 params![
75 wrap_event_id.to_hex(),
76 rumor_id.to_hex(),
77 recipient_pubkey.to_hex(),
78 role as i64,
79 secret.as_secret_bytes(),
80 relays_json,
81 now,
82 ],
83 )
84 .map_err(|e| format!("Failed to insert wrap key: {}", e))?;
85 crate::log_info!(
86 "[NIP-17 keys] stored {:?} key for rumor {} (wrap {})",
87 role,
88 rumor_id.to_hex(),
89 wrap_event_id.to_hex()
90 );
91 Ok(())
92}
93
94pub fn get_wrap_keys_for_rumor(rumor_id: &EventId) -> Result<Vec<StoredWrapKey>, String> {
97 let conn = super::get_db_connection_guard_static()?;
98 let mut stmt = conn
99 .prepare(
100 "SELECT wrap_event_id, rumor_id, recipient_pubkey, role, secret, relay_urls
101 FROM nip17_wrap_keys WHERE rumor_id = ?1",
102 )
103 .map_err(|e| format!("Failed to prepare query: {}", e))?;
104
105 let rows = stmt
106 .query_map(params![rumor_id.to_hex()], |row| {
107 let wrap_hex: String = row.get(0)?;
108 let rumor_hex: String = row.get(1)?;
109 let recipient_hex: String = row.get(2)?;
110 let role_i: i64 = row.get(3)?;
111 let secret_blob: Vec<u8> = row.get(4)?;
112 let relays_json: String = row.get(5)?;
113 Ok((wrap_hex, rumor_hex, recipient_hex, role_i, secret_blob, relays_json))
114 })
115 .map_err(|e| format!("Failed to query wrap keys: {}", e))?;
116
117 let mut out = Vec::new();
118 for row_res in rows {
119 let (wrap_hex, rumor_hex, recipient_hex, role_i, secret_blob, relays_json) =
120 row_res.map_err(|e| format!("Row read error: {}", e))?;
121 let wrap_event_id =
122 EventId::from_hex(&wrap_hex).map_err(|e| format!("Bad wrap id: {}", e))?;
123 let rumor_id_parsed =
124 EventId::from_hex(&rumor_hex).map_err(|e| format!("Bad rumor id: {}", e))?;
125 let recipient_pubkey =
126 PublicKey::from_hex(&recipient_hex).map_err(|e| format!("Bad pubkey: {}", e))?;
127 let secret =
128 SecretKey::from_slice(&secret_blob).map_err(|e| format!("Bad secret: {}", e))?;
129 let relay_urls: Vec<String> = serde_json::from_str(&relays_json)
130 .map_err(|e| format!("Bad relay urls: {}", e))?;
131 out.push(StoredWrapKey {
132 wrap_event_id,
133 rumor_id: rumor_id_parsed,
134 recipient_pubkey,
135 role: WrapRole::from_i64(role_i),
136 secret,
137 relay_urls,
138 });
139 }
140 Ok(out)
141}
142
143pub fn has_wrap_keys_for_rumor(rumor_id: &EventId) -> Result<bool, String> {
147 let conn = super::get_db_connection_guard_static()?;
148 conn.query_row(
149 "SELECT EXISTS(SELECT 1 FROM nip17_wrap_keys WHERE rumor_id = ?1)",
150 params![rumor_id.to_hex()],
151 |row| row.get::<_, bool>(0),
152 )
153 .map_err(|e| format!("Failed to check wrap keys: {}", e))
154}
155
156pub fn purge_wrap_keys(wrap_event_ids: &[EventId]) -> Result<(), String> {
161 if wrap_event_ids.is_empty() {
162 return Ok(());
163 }
164 let conn = super::get_write_connection_guard_static()?;
165 let placeholders = std::iter::repeat("?")
166 .take(wrap_event_ids.len())
167 .collect::<Vec<_>>()
168 .join(",");
169 let sql = format!(
170 "DELETE FROM nip17_wrap_keys WHERE wrap_event_id IN ({})",
171 placeholders,
172 );
173 let hex_strings: Vec<String> = wrap_event_ids.iter().map(|id| id.to_hex()).collect();
174 let params_dyn: Vec<&dyn rusqlite::ToSql> = hex_strings
175 .iter()
176 .map(|s| s as &dyn rusqlite::ToSql)
177 .collect();
178 conn.execute(&sql, params_dyn.as_slice())
179 .map_err(|e| format!("Failed to purge wrap keys: {}", e))?;
180 Ok(())
181}