1use super::{EventKind, PublicKey, Signature, Unixtime};
2use crate::Error;
3use serde::de::Error as DeError;
4use serde::de::{Deserialize, Deserializer, Visitor};
5use serde::ser::{Serialize, Serializer};
6#[cfg(feature = "speedy")]
7use speedy::{Readable, Writable};
8use std::fmt;
9
10#[derive(Clone, Debug, PartialEq, Eq)]
12pub enum EventDelegation {
13 NotDelegated,
15
16 InvalidDelegation(String),
18
19 DelegatedBy(PublicKey),
21}
22
23#[derive(Clone, Debug, Default, PartialEq, Eq)]
25#[cfg_attr(feature = "speedy", derive(Readable, Writable))]
26pub struct DelegationConditions {
27 pub kind: Option<EventKind>,
29
30 pub created_after: Option<Unixtime>,
32
33 pub created_before: Option<Unixtime>,
35
36 pub full_string: Option<String>,
38}
39
40impl DelegationConditions {
41 pub fn as_string(&self) -> String {
43 match &self.full_string {
44 Some(fs) => fs.clone(),
45 None => self.compile_full_string(),
46 }
47 }
48
49 fn compile_full_string(&self) -> String {
51 let mut parts: Vec<String> = Vec::new();
52 if let Some(kind) = self.kind {
53 parts.push(format!("kind={}", u32::from(kind)));
54 }
55 if let Some(created_after) = self.created_after {
56 parts.push(format!("created_at>{}", created_after.0));
57 }
58 if let Some(created_before) = self.created_before {
59 parts.push(format!("created_at<{}", created_before.0));
60 }
61 parts.join("&")
62 }
63
64 #[allow(dead_code)]
65 fn update_full_string(&mut self) {
66 self.full_string = Some(self.compile_full_string())
67 }
68
69 pub fn try_from_str(s: &str) -> Result<DelegationConditions, Error> {
71 let mut output: DelegationConditions = Default::default();
72
73 let parts = s.split('&');
74 for part in parts {
75 if let Some(kindstr) = part.strip_prefix("kind=") {
76 let event_num = kindstr.parse::<u32>()?;
77 let event_kind: EventKind = From::from(event_num);
78 output.kind = Some(event_kind);
79 }
80 if let Some(timestr) = part.strip_prefix("created_at>") {
81 let time = timestr.parse::<i64>()?;
82 output.created_after = Some(Unixtime(time));
83 }
84 if let Some(timestr) = part.strip_prefix("created_at<") {
85 let time = timestr.parse::<i64>()?;
86 output.created_before = Some(Unixtime(time));
87 }
88 }
89 output.full_string = Some(s.to_string());
91
92 Ok(output)
93 }
94
95 #[allow(dead_code)]
96 pub(crate) fn mock() -> DelegationConditions {
97 let mut dc = DelegationConditions {
98 kind: Some(EventKind::Repost),
99 created_after: Some(Unixtime(1677700000)),
100 created_before: None,
101 full_string: None,
102 };
103 dc.update_full_string();
104 dc
105 }
106
107 pub fn verify_signature(
109 &self,
110 pubkey_delegater: &PublicKey,
111 pubkey_delegatee: &PublicKey,
112 signature: &Signature,
113 ) -> Result<(), Error> {
114 let input = format!(
115 "nostr:delegation:{}:{}",
116 pubkey_delegatee.as_hex_string(),
117 self.as_string()
118 );
119 pubkey_delegater.verify(input.as_bytes(), signature)
120 }
121}
122
123impl Serialize for DelegationConditions {
124 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
125 where
126 S: Serializer,
127 {
128 serializer.serialize_str(&self.as_string())
129 }
130}
131
132impl<'de> Deserialize<'de> for DelegationConditions {
133 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
134 where
135 D: Deserializer<'de>,
136 {
137 deserializer.deserialize_str(DelegationConditionsVisitor)
138 }
139}
140
141struct DelegationConditionsVisitor;
142
143impl Visitor<'_> for DelegationConditionsVisitor {
144 type Value = DelegationConditions;
145
146 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
147 write!(f, "A string")
148 }
149
150 fn visit_str<E>(self, v: &str) -> Result<DelegationConditions, E>
151 where
152 E: DeError,
153 {
154 DelegationConditions::try_from_str(v).map_err(|e| E::custom(format!("{e}")))
155 }
156}
157
158#[cfg(test)]
159mod test {
160 use super::*;
161 use crate::{KeySigner, ParsedTag, PrivateKey, SignerExt, Tag};
162
163 test_serde! {DelegationConditions, test_delegation_conditions_serde}
164
165 #[tokio::test]
166 async fn test_sign_delegation_verify_delegation_signature() {
167 let delegator_private_key = PrivateKey::try_from_hex_string(
168 "ee35e8bb71131c02c1d7e73231daa48e9953d329a4b701f7133c8f46dd21139c",
169 )
170 .unwrap();
171 let delegator_public_key = delegator_private_key.public_key();
172
173 let signer = KeySigner::from_private_key(delegator_private_key, "lockme", 16).unwrap();
174
175 let delegatee_public_key = PublicKey::try_from_hex_string(
176 "477318cfb5427b9cfc66a9fa376150c1ddbc62115ae27cef72417eb959691396",
177 true,
178 )
179 .unwrap();
180
181 let dc = DelegationConditions::try_from_str(
182 "kind=1&created_at>1674834236&created_at<1677426236",
183 )
184 .unwrap();
185
186 let sig = signer
187 .generate_delegation_signature(delegatee_public_key, &dc)
188 .await
189 .unwrap();
190
191 let verify_result = dc.verify_signature(&delegator_public_key, &delegatee_public_key, &sig);
192 assert!(verify_result.is_ok());
193 }
194
195 #[test]
196 fn test_delegation_tag_parse_and_verify() {
197 let tag_str = "[\"delegation\",\"1a459a8a6aa6441d480ba665fb8fb21a4cfe8bcacb7d87300f8046a558a3fce4\",\"kind=1&created_at>1676067553&created_at<1678659553\",\"369aed09c1ad52fceb77ecd6c16f2433eac4a3803fc41c58876a5b60f4f36b9493d5115e5ec5a0ce6c3668ffe5b58d47f2cbc97233833bb7e908f66dbbbd9d36\"]";
198 let dt = serde_json::from_str::<Tag>(tag_str).unwrap();
199 if let Ok(ParsedTag::Delegation {
200 pubkey,
201 conditions,
202 sig,
203 }) = dt.parse()
204 {
205 assert_eq!(
206 conditions.as_string(),
207 "kind=1&created_at>1676067553&created_at<1678659553"
208 );
209
210 let delegatee_public_key = PublicKey::try_from_hex_string(
211 "bea8aeb6c1657e33db5ac75a83910f77e8ec6145157e476b5b88c6e85b1fab34",
212 true,
213 )
214 .unwrap();
215
216 let verify_result = conditions.verify_signature(&pubkey, &delegatee_public_key, &sig);
217 assert!(verify_result.is_ok());
218 } else {
219 panic!("Incorrect tag type")
220 }
221 }
222
223 #[test]
224 fn test_delegation_tag_parse_and_verify_alt_order() {
225 let tag_str = "[\"delegation\",\"05bc52a6117c57f99b73f5315f3105b21cecdcd2c6825dee8d508bd7d972ad6a\",\"kind=1&created_at<1686078180&created_at>1680807780\",\"1016d2f4284cdb4e6dc6eaa4e61dff87b9f4138786154d070d36e9434f817bd623abed2133bb62b9dcfb2fbf54b42e16bcd44cfc23907f8eb5b45c011caaa47c\"]";
227 let dt = serde_json::from_str::<Tag>(tag_str).unwrap();
228 if let Ok(ParsedTag::Delegation {
229 pubkey,
230 conditions,
231 sig,
232 }) = dt.parse()
233 {
234 assert_eq!(
235 conditions.as_string(),
236 "kind=1&created_at<1686078180&created_at>1680807780"
237 );
238
239 let delegatee_public_key = PublicKey::try_from_hex_string(
240 "111c02821806b046068dffc4d8e4de4a56bc99d3015c335b8929d900928fa317",
241 true,
242 )
243 .unwrap();
244
245 let verify_result = conditions.verify_signature(&pubkey, &delegatee_public_key, &sig);
246 assert!(verify_result.is_ok());
247 } else {
248 panic!("Incorrect tag type")
249 }
250 }
251
252 #[test]
253 fn test_from_str() {
254 let str = "kind=1&created_at>1000000&created_at<2000000";
255 let dc = DelegationConditions::try_from_str(str).unwrap();
256 assert_eq!(dc.as_string(), str);
257 }
258
259 #[test]
260 fn test_from_str_alt_order() {
261 let str = "created_at<2000000&created_at>1000000&kind=1";
263 let dc = DelegationConditions::try_from_str(str).unwrap();
264 assert_eq!(dc.as_string(), str);
265 }
266
267 #[test]
268 fn test_as_string() {
269 let dc = DelegationConditions {
270 kind: Some(EventKind::TextNote),
271 created_before: Some(Unixtime(2000000)),
272 created_after: Some(Unixtime(1000000)),
273 full_string: None,
274 };
275 assert_eq!(
276 dc.as_string(),
277 "kind=1&created_at>1000000&created_at<2000000"
278 );
279 }
280}