1use std::collections::BTreeSet;
14
15use ruma_common::{room_version_rules::RoomVersionRules, CanonicalJsonObject, EventId, OwnedEventId, OwnedRoomId, UserId};
16use ruma_events::{StateEventType, TimelineEventType};
17use ruma_signatures::{hash_and_sign_event, reference_hash, verify_event, PublicKeyMap, Verified};
18use ruma_state_res::{auth_types_for_event, check_state_dependent_auth_rules, check_state_independent_auth_rules, StateMap};
19use serde_json::{json, value::RawValue, Value};
20
21use crate::{resolve::resolve_state_with, Pdu, SignedEvent, Signer};
22
23pub trait DagRead {
25 fn pdu(&self, id: &EventId) -> Option<Pdu>;
27 fn state_after(&self, id: &EventId) -> Option<StateMap<OwnedEventId>>;
29 fn extremities(&self) -> Vec<OwnedEventId>;
31}
32
33#[derive(Debug, thiserror::Error)]
34pub enum Reject {
35 #[error("malformed event: {0}")]
36 Malformed(String),
37 #[error("event already stored")]
38 Duplicate,
39 #[error("content hash does not match (content was altered or already redacted)")]
40 BadContentHash,
41 #[error("signature check failed: {0}")]
42 BadSignature(String),
43 #[error("unknown prev_event {0}")]
44 MissingPrev(String),
45 #[error("unknown auth_event {0}")]
46 MissingAuth(String),
47 #[error("auth rules refuse the event: {0}")]
48 Auth(String),
49 #[error("state resolution failed: {0}")]
50 Resolve(String),
51}
52
53#[derive(Clone, Debug)]
55pub struct Accepted {
56 pub event_id: OwnedEventId,
57 pub pdu: Pdu,
58 pub json: CanonicalJsonObject,
59 pub state_after: StateMap<OwnedEventId>,
61 pub soft_failed: bool,
63 pub extremities: Vec<OwnedEventId>,
65 pub current_state: StateMap<OwnedEventId>,
67 pub forked: bool,
69}
70
71fn skey(kind: &TimelineEventType) -> StateEventType {
72 StateEventType::from(kind.to_string())
73}
74
75fn resolve_over(rules: &RoomVersionRules, db: &dyn DagRead, ids: &[OwnedEventId], extra: Option<(&OwnedEventId, &StateMap<OwnedEventId>)>) -> Result<StateMap<OwnedEventId>, Reject> {
77 let mut forks: Vec<StateMap<OwnedEventId>> = Vec::new();
78 for id in ids {
79 match extra {
80 Some((eid, st)) if eid == id => forks.push(st.clone()),
81 _ => forks.push(db.state_after(id).ok_or_else(|| Reject::MissingPrev(id.to_string()))?),
82 }
83 }
84 match forks.len() {
85 0 => Ok(StateMap::new()),
86 1 => Ok(forks.remove(0)),
87 _ => resolve_state_with(rules, &|id: &EventId| db.pdu(id), &forks).map_err(|e| Reject::Resolve(e.to_string())),
88 }
89}
90
91struct WithNew<'a> {
93 db: &'a dyn DagRead,
94 pdu: &'a Pdu,
95}
96
97impl DagRead for WithNew<'_> {
98 fn pdu(&self, id: &EventId) -> Option<Pdu> {
99 if id == self.pdu.event_id { Some(self.pdu.clone()) } else { self.db.pdu(id) }
100 }
101 fn state_after(&self, id: &EventId) -> Option<StateMap<OwnedEventId>> {
102 self.db.state_after(id)
103 }
104 fn extremities(&self) -> Vec<OwnedEventId> {
105 self.db.extremities()
106 }
107}
108
109pub fn current_state(rules: &RoomVersionRules, db: &dyn DagRead) -> Result<StateMap<OwnedEventId>, Reject> {
111 resolve_over(rules, db, &db.extremities(), None)
112}
113
114pub fn compute_event_id(rules: &RoomVersionRules, json: &CanonicalJsonObject) -> Result<OwnedEventId, Reject> {
116 let h = reference_hash(json, rules).map_err(|e| Reject::Malformed(e.to_string()))?;
117 OwnedEventId::try_from(format!("${h}")).map_err(|e| Reject::Malformed(e.to_string()))
118}
119
120pub fn verify_wire(rules: &RoomVersionRules, json: &CanonicalJsonObject, keys: &PublicKeyMap) -> Result<(), Reject> {
124 match verify_event(keys, json, rules) {
125 Ok(Verified::All) => Ok(()),
126 Ok(Verified::Signatures) if is_skeleton(rules, json) => Ok(()),
127 Ok(Verified::Signatures) => Err(Reject::BadContentHash),
128 Err(e) => Err(Reject::BadSignature(e.to_string())),
129 }
130}
131
132pub fn is_skeleton(rules: &RoomVersionRules, json: &CanonicalJsonObject) -> bool {
134 skeleton(rules, json).map(|s| s == *json).unwrap_or(false)
135}
136
137pub fn skeleton(rules: &RoomVersionRules, json: &CanonicalJsonObject) -> Result<CanonicalJsonObject, Reject> {
140 ruma_common::canonical_json::redact(json.clone(), &rules.redaction, None).map_err(|e| Reject::Malformed(e.to_string()))
141}
142
143pub fn accept(rules: &RoomVersionRules, db: &dyn DagRead, event_id: OwnedEventId, json: CanonicalJsonObject, state_before: Option<StateMap<OwnedEventId>>) -> Result<Accepted, Reject> {
147 if db.pdu(&event_id).is_some() {
148 return Err(Reject::Duplicate);
149 }
150 let pdu = Pdu::from_wire(event_id.clone(), &json).map_err(Reject::Malformed)?;
151 let fetch = |id: &EventId| -> Option<Pdu> { if id == pdu.event_id { Some(pdu.clone()) } else { db.pdu(id) } };
152
153 for a in &pdu.auth_events {
155 if db.pdu(a).is_none() {
156 return Err(Reject::MissingAuth(a.to_string()));
157 }
158 }
159 check_state_independent_auth_rules(&rules.authorization, &pdu, &fetch).map_err(Reject::Auth)?;
160
161 let mut auth_state: std::collections::HashMap<(StateEventType, String), Pdu> = Default::default();
163 for a in &pdu.auth_events {
164 if let Some(p) = db.pdu(a) {
165 if let Some(sk) = &p.state_key {
166 auth_state.insert((skey(&p.kind), sk.clone()), p);
167 }
168 }
169 }
170 check_state_dependent_auth_rules(&rules.authorization, &pdu, |t: &StateEventType, k: &str| auth_state.get(&(t.clone(), k.to_owned())).cloned()).map_err(Reject::Auth)?;
171
172 let before = match state_before {
174 Some(s) => s,
175 None => resolve_over(rules, db, &pdu.prev_events, None)?,
176 };
177 let by_state = |t: &StateEventType, k: &str| before.get(&(t.clone(), k.to_owned())).and_then(|id| db.pdu(id));
178 check_state_dependent_auth_rules(&rules.authorization, &pdu, by_state).map_err(Reject::Auth)?;
179
180 let mut state_after = before;
181 if let Some(sk) = &pdu.state_key {
182 state_after.insert((skey(&pdu.kind), sk.clone()), event_id.clone());
183 }
184
185 let old_ext = db.extremities();
187 let current = resolve_over(rules, db, &old_ext, None)?;
188 let by_current = |t: &StateEventType, k: &str| current.get(&(t.clone(), k.to_owned())).and_then(|id| db.pdu(id));
189 let soft_failed = !old_ext.is_empty() && check_state_dependent_auth_rules(&rules.authorization, &pdu, by_current).is_err();
190
191 if soft_failed {
192 return Ok(Accepted { event_id, pdu, json, state_after, soft_failed: true, extremities: old_ext, current_state: current, forked: false });
193 }
194 let mut ext: Vec<OwnedEventId> = old_ext.into_iter().filter(|e| !pdu.prev_events.contains(e)).collect();
195 ext.push(event_id.clone());
196 ext.sort();
197 let forked = ext.len() > 1;
198 let current_state = resolve_over(rules, &WithNew { db, pdu: &pdu }, &ext, Some((&event_id, &state_after)))?;
199 Ok(Accepted { event_id, pdu, json, state_after, soft_failed: false, extremities: ext, current_state, forked })
200}
201
202pub struct Historic {
206 pub event_id: OwnedEventId,
207 pub pdu: Pdu,
208 pub json: CanonicalJsonObject,
209 pub state_after: StateMap<OwnedEventId>,
210}
211
212pub fn accept_historic(rules: &RoomVersionRules, db: &dyn DagRead, event_id: OwnedEventId, json: CanonicalJsonObject) -> Result<Historic, Reject> {
213 let pdu = Pdu::from_wire(event_id.clone(), &json).map_err(Reject::Malformed)?;
214 let fetch = |id: &EventId| -> Option<Pdu> { if id == pdu.event_id { Some(pdu.clone()) } else { db.pdu(id) } };
215 for a in &pdu.auth_events {
216 if db.pdu(a).is_none() {
217 return Err(Reject::MissingAuth(a.to_string()));
218 }
219 }
220 check_state_independent_auth_rules(&rules.authorization, &pdu, &fetch).map_err(Reject::Auth)?;
221 let mut auth_state: std::collections::HashMap<(StateEventType, String), Pdu> = Default::default();
222 for a in &pdu.auth_events {
223 if let Some(p) = db.pdu(a) {
224 if let Some(sk) = &p.state_key {
225 auth_state.insert((skey(&p.kind), sk.clone()), p);
226 }
227 }
228 }
229 check_state_dependent_auth_rules(&rules.authorization, &pdu, |t: &StateEventType, k: &str| auth_state.get(&(t.clone(), k.to_owned())).cloned()).map_err(Reject::Auth)?;
230 let before = resolve_over(rules, db, &pdu.prev_events, None)?;
231 let by_state = |t: &StateEventType, k: &str| before.get(&(t.clone(), k.to_owned())).and_then(|id| db.pdu(id));
232 check_state_dependent_auth_rules(&rules.authorization, &pdu, by_state).map_err(Reject::Auth)?;
233 let mut state_after = before;
234 if let Some(sk) = &pdu.state_key {
235 state_after.insert((skey(&pdu.kind), sk.clone()), event_id.clone());
236 }
237 Ok(Historic { event_id, pdu, json, state_after })
238}
239
240pub fn verify_auth_chain(rules: &RoomVersionRules, events: &[CanonicalJsonObject], keys: &PublicKeyMap) -> Result<Vec<OwnedEventId>, Reject> {
244 let mut pdus: std::collections::HashMap<OwnedEventId, Pdu> = Default::default();
245 for j in events {
246 verify_wire(rules, j, keys)?;
247 let id = compute_event_id(rules, j)?;
248 let p = Pdu::from_wire(id.clone(), j).map_err(Reject::Malformed)?;
249 pdus.insert(id, p);
250 }
251 let mut order: Vec<&Pdu> = pdus.values().collect();
252 order.sort_by_key(|p| (p.depth, p.event_id.clone()));
253 for p in &order {
254 for a in &p.auth_events {
255 if !pdus.contains_key(a) {
256 return Err(Reject::MissingAuth(a.to_string()));
257 }
258 }
259 let fetch = |id: &EventId| pdus.get(id).cloned();
260 check_state_independent_auth_rules(&rules.authorization, *p, &fetch).map_err(Reject::Auth)?;
261 let mut st: std::collections::HashMap<(StateEventType, String), Pdu> = Default::default();
262 for a in &p.auth_events {
263 let ap = &pdus[a];
264 if let Some(sk) = &ap.state_key {
265 st.insert((skey(&ap.kind), sk.clone()), ap.clone());
266 }
267 }
268 check_state_dependent_auth_rules(&rules.authorization, *p, |t: &StateEventType, k: &str| st.get(&(t.clone(), k.to_owned())).cloned()).map_err(Reject::Auth)?;
269 }
270 Ok(order.iter().map(|p| p.event_id.clone()).collect())
271}
272
273pub fn accept_wire(rules: &RoomVersionRules, db: &dyn DagRead, json: CanonicalJsonObject, keys: &PublicKeyMap, state_before: Option<StateMap<OwnedEventId>>) -> Result<Accepted, Reject> {
275 verify_wire(rules, &json, keys)?;
276 let id = compute_event_id(rules, &json)?;
277 accept(rules, db, id, json, state_before)
278}
279
280pub fn template(rules: &RoomVersionRules, db: &dyn DagRead, room_id: &OwnedRoomId, ts: u64, sender: &str, kind: &str, state_key: Option<&str>, content: Value) -> Result<CanonicalJsonObject, Reject> {
283 let sender_id = UserId::parse(sender).map_err(|e| Reject::Malformed(e.to_string()))?;
284 let raw = RawValue::from_string(content.to_string()).map_err(|e| Reject::Malformed(e.to_string()))?;
285 let tl = TimelineEventType::from(kind.to_owned());
286 let ext = db.extremities();
287 let cur = resolve_over(rules, db, &ext, None)?;
288 let auth: Vec<OwnedEventId> = auth_types_for_event(&tl, &sender_id, state_key, &raw, &rules.authorization)
289 .map_err(Reject::Auth)?
290 .iter()
291 .filter_map(|k| cur.get(k).cloned())
292 .collect();
293 let depth = ext.iter().filter_map(|e| db.pdu(e)).map(|p| p.depth).max().unwrap_or(0) + 1;
294 let mut o = json!({
295 "room_id": room_id, "sender": sender, "type": kind, "content": content,
296 "origin_server_ts": ts, "prev_events": ext, "auth_events": auth, "depth": depth,
297 });
298 if let Some(sk) = state_key {
299 o["state_key"] = json!(sk);
300 }
301 serde_json::from_value(o).map_err(|e| Reject::Malformed(e.to_string()))
302}
303
304pub fn sign_template(rules: &RoomVersionRules, signer: &Signer, mut obj: CanonicalJsonObject) -> Result<SignedEvent, Reject> {
306 hash_and_sign_event(signer.server(), signer, &mut obj, &rules.redaction).map_err(|e| Reject::Malformed(e.to_string()))?;
307 let event_id = compute_event_id(rules, &obj)?;
308 Ok(SignedEvent { event_id, json: obj })
309}
310
311pub fn local_event(rules: &RoomVersionRules, db: &dyn DagRead, signer: &Signer, room_id: &OwnedRoomId, ts: u64, sender: &str, kind: &str, state_key: Option<&str>, content: Value) -> Result<Accepted, Reject> {
313 let t = template(rules, db, room_id, ts, sender, kind, state_key, content)?;
314 let s = sign_template(rules, signer, t)?;
315 accept(rules, db, s.event_id, s.json, None)
316}
317
318pub fn missing(db: &dyn DagRead, ids: &[OwnedEventId]) -> BTreeSet<OwnedEventId> {
320 ids.iter().filter(|i| db.pdu(i).is_none()).cloned().collect()
321}