nostr_types/types/
delegation.rs

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/// Delegation information for an Event
11#[derive(Clone, Debug, PartialEq, Eq)]
12pub enum EventDelegation {
13    /// The event was not delegated
14    NotDelegated,
15
16    /// The delegation was invalid (with reason)
17    InvalidDelegation(String),
18
19    /// The event was delegated and is valid (with pubkey of delegator)
20    DelegatedBy(PublicKey),
21}
22
23/// Conditions of delegation
24#[derive(Clone, Debug, Default, PartialEq, Eq)]
25#[cfg_attr(feature = "speedy", derive(Readable, Writable))]
26pub struct DelegationConditions {
27    /// If the delegation is only for a given event kind
28    pub kind: Option<EventKind>,
29
30    /// If the delegation is only for events created after a certain time
31    pub created_after: Option<Unixtime>,
32
33    /// If the delegation is only for events created before a certain time
34    pub created_before: Option<Unixtime>,
35
36    /// Optional full string form, in case it was parsed from string
37    pub full_string: Option<String>,
38}
39
40impl DelegationConditions {
41    /// Return in conmpiled string form. If full form is stored, it is returned, otherwise it is compiled from parts.
42    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    /// Compile full string from parts.
50    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    /// Convert from string from
70    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        // store orignal string
90        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    /// Verify the signature part of a Delegation tag
108    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        // Clauses in the condition string are not in the canonical order, but this should not matter
226        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        // Even with alternative order, as_string() should return the same
262        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}