hashtree_cli/webrtc/
root_events.rs1use nostr::{nips::nip19::FromBech32, Alphabet, Event, Filter, Kind, PublicKey, SingleLetterTag};
2
3#[derive(Debug, Clone)]
4pub struct PeerRootEvent {
5 pub hash: String,
6 pub key: Option<String>,
7 pub encrypted_key: Option<String>,
8 pub self_encrypted_key: Option<String>,
9 pub event_id: String,
10 pub created_at: u64,
11 pub peer_id: String,
12}
13
14pub const HASHTREE_KIND: u16 = 30078;
15pub const HASHTREE_LABEL: &str = "hashtree";
16
17pub fn build_root_filter(owner_pubkey: &str, tree_name: &str) -> Option<Filter> {
18 let author = PublicKey::from_hex(owner_pubkey)
19 .or_else(|_| PublicKey::from_bech32(owner_pubkey))
20 .ok()?;
21 Some(
22 Filter::new()
23 .kind(Kind::Custom(HASHTREE_KIND))
24 .author(author)
25 .custom_tag(
26 SingleLetterTag::lowercase(Alphabet::D),
27 vec![tree_name.to_string()],
28 )
29 .custom_tag(
30 SingleLetterTag::lowercase(Alphabet::L),
31 vec![HASHTREE_LABEL.to_string()],
32 )
33 .limit(50),
34 )
35}
36
37pub fn hashtree_event_identifier(event: &Event) -> Option<String> {
38 event.tags.iter().find_map(|tag| {
39 let slice = tag.as_slice();
40 if slice.len() >= 2 && slice[0].as_str() == "d" {
41 Some(slice[1].to_string())
42 } else {
43 None
44 }
45 })
46}
47
48pub fn is_hashtree_labeled_event(event: &Event) -> bool {
49 event.tags.iter().any(|tag| {
50 let slice = tag.as_slice();
51 slice.len() >= 2 && slice[0].as_str() == "l" && slice[1].as_str() == HASHTREE_LABEL
52 })
53}
54
55pub fn pick_latest_event<'a, I>(events: I) -> Option<&'a Event>
56where
57 I: IntoIterator<Item = &'a Event>,
58{
59 events.into_iter().max_by(|a, b| {
60 let ordering = a.created_at.cmp(&b.created_at);
61 if ordering == std::cmp::Ordering::Equal {
62 a.id.cmp(&b.id)
63 } else {
64 ordering
65 }
66 })
67}
68
69pub fn root_event_from_peer(
70 event: &Event,
71 peer_id: &str,
72 tree_name: &str,
73) -> Option<PeerRootEvent> {
74 if hashtree_event_identifier(event).as_deref() != Some(tree_name)
75 || !is_hashtree_labeled_event(event)
76 {
77 return None;
78 }
79
80 let mut key = None;
81 let mut encrypted_key = None;
82 let mut self_encrypted_key = None;
83 let mut hash_tag = None;
84
85 for tag in &event.tags {
86 let slice = tag.as_slice();
87 if slice.len() < 2 {
88 continue;
89 }
90 match slice[0].as_str() {
91 "hash" => hash_tag = Some(slice[1].to_string()),
92 "key" => key = Some(slice[1].to_string()),
93 "encryptedKey" => encrypted_key = Some(slice[1].to_string()),
94 "selfEncryptedKey" => self_encrypted_key = Some(slice[1].to_string()),
95 _ => {}
96 }
97 }
98
99 let hash = hash_tag.or_else(|| {
100 if event.content.is_empty() {
101 None
102 } else {
103 Some(event.content.clone())
104 }
105 })?;
106
107 Some(PeerRootEvent {
108 hash,
109 key,
110 encrypted_key,
111 self_encrypted_key,
112 event_id: event.id.to_hex(),
113 created_at: event.created_at.as_u64(),
114 peer_id: peer_id.to_string(),
115 })
116}