diff --git a/src/lib.rs b/src/lib.rs index c524b051..aefd24d6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -140,13 +140,15 @@ impl StateResolution { // TODO make sure each conflicting event is in event_map?? // synapse says `full_set = {eid for eid in full_conflicted_set if eid in event_map}` all_conflicted.retain(|id| event_map.contains_key(id)); - println!( + + tracing::debug!( "ALL {:?}", all_conflicted .iter() .map(ToString::to_string) .collect::>() ); + // get only the power events with a state_key: "" or ban/kick event (sender != state_key) let power_events = all_conflicted .iter() @@ -154,7 +156,7 @@ impl StateResolution { .cloned() .collect::>(); - println!( + tracing::debug!( "POWER {:?}", power_events .iter() @@ -171,7 +173,7 @@ impl StateResolution { &all_conflicted, ); - println!( + tracing::debug!( "SRTD {:?}", sorted_power_levels .iter() @@ -209,7 +211,7 @@ impl StateResolution { .cloned() .collect::>(); - println!( + tracing::debug!( "LEFT {:?}", events_to_resolve .iter() @@ -224,7 +226,7 @@ impl StateResolution { let sorted_left_events = self.mainline_sort(room_id, &events_to_resolve, power_event, &event_map, store); - println!( + tracing::debug!( "SORTED LEFT {:?}", sorted_left_events .iter() @@ -330,7 +332,7 @@ impl StateResolution { let mut event_to_pl = BTreeMap::new(); for (idx, event_id) in graph.keys().enumerate() { let pl = self.get_power_level_for_sender(room_id, &event_id, event_map, store); - println!("{} power level {}", event_id.to_string(), pl); + tracing::info!("{} power level {}", event_id.to_string(), pl); event_to_pl.insert(event_id.clone(), pl); @@ -345,10 +347,20 @@ impl StateResolution { // tracing::debug!("{:?}", event_map.get(event_id).unwrap().origin_server_ts()); let ev = event_map.get(event_id).unwrap(); let pl = event_to_pl.get(event_id).unwrap(); + + tracing::debug!( + "{:?}", + ( + -*pl, + ev.origin_server_ts().clone(), + ev.event_id().unwrap().to_string() + ) + ); + // This return value is the key used for sorting events, // events are then sorted by power level, time, // and lexically by event_id. - (*pl, ev.origin_server_ts().clone(), ev.event_id().cloned()) + (-*pl, ev.origin_server_ts().clone(), ev.event_id().cloned()) }) } @@ -419,16 +431,12 @@ impl StateResolution { sorted.push(node.clone()); } - // tracing::debug!( - // "{:#?}", - // sorted.iter().map(ToString::to_string).collect::>() - // ); sorted } fn get_power_level_for_sender( &self, - room_id: &RoomId, + _room_id: &RoomId, event_id: &EventId, event_map: &EventMap, // TODO use event_map over store ?? store: &dyn StateStore, @@ -438,9 +446,8 @@ impl StateResolution { let mut pl = None; // TODO store.auth_event_ids returns "self" with the event ids is this ok // event.auth_event_ids does not include its own event id ? - for aid in event_map.get(event_id).unwrap().auth_event_ids() { - println!("aid {}", aid.to_string()); - if let Some(aev) = event_map.get(&aid) { + for aid in store.get_event(event_id).unwrap().auth_event_ids() { + if let Ok(aev) = store.get_event(&aid) { if aev.is_type_and_key(EventType::RoomPowerLevels, "") { pl = Some(aev); break; @@ -449,10 +456,8 @@ impl StateResolution { } if pl.is_none() { - for aid in event_map.get(event_id).unwrap().auth_event_ids() { - println!("aid NONE {}", aid.to_string()); - - if let Some(aev) = event_map.get(&aid) { + for aid in store.get_event(event_id).unwrap().auth_event_ids() { + if let Ok(aev) = store.get_event(&aid) { if aev.is_type_and_key(EventType::RoomCreate, "") { if let Ok(content) = aev .deserialize_content::() @@ -478,10 +483,11 @@ impl StateResolution { { if let Some(ev) = event { if let Some(user) = content.users.get(ev.sender()) { + tracing::debug!("found {} at power_level {}", ev.sender().to_string(), user); return (*user).into(); } } - content.state_default.into() + content.users_default.into() } else { 0 } @@ -549,7 +555,7 @@ impl StateResolution { /// the `resolved_power_level`. fn mainline_sort( &mut self, - room_id: &RoomId, + _room_id: &RoomId, to_sort: &[EventId], resolved_power_level: Option<&EventId>, event_map: &EventMap, @@ -571,7 +577,7 @@ impl StateResolution { while let Some(p) = pl { mainline.push(p.clone()); // We don't need the actual pl_ev here since we delegate to the store - let auth_events = store.auth_event_ids(room_id, &[p]).unwrap(); + let auth_events = store.get_event(&p).unwrap().auth_event_ids(); pl = None; for aid in auth_events { let ev = store.get_event(&aid).unwrap(); @@ -664,7 +670,7 @@ impl StateResolution { fn add_event_and_auth_chain_to_graph( &self, - room_id: &RoomId, + _room_id: &RoomId, graph: &mut BTreeMap>, event_id: &EventId, store: &dyn StateStore, @@ -677,7 +683,7 @@ impl StateResolution { graph.entry(eid.clone()).or_insert(vec![]); // prefer the store to event as the store filters dedups the events // otherwise it seems we can loop forever - for aid in store.auth_event_ids(room_id, &[eid.clone()]).unwrap() { + for aid in store.get_event(&eid).unwrap().auth_event_ids() { if auth_diff.contains(&aid) { if !graph.contains_key(&aid) { state.push(aid.clone()); diff --git a/src/state_event.rs b/src/state_event.rs index 1a2d0e78..f15a72cc 100644 --- a/src/state_event.rs +++ b/src/state_event.rs @@ -44,9 +44,32 @@ impl StateEvent { } _ => false, }, - Pdu::RoomV3Pdu(event) => panic!(), + Pdu::RoomV3Pdu(event) => event.state_key == Some("".into()), + }, + Self::Sync(any_event) => match any_event { + PduStub::RoomV1PduStub(event) => match event.kind { + EventType::RoomPowerLevels + | EventType::RoomJoinRules + | EventType::RoomCreate => event.state_key == Some("".into()), + EventType::RoomMember => { + if let Ok(content) = + serde_json::from_value::(event.content.clone()) + { + if [MembershipState::Leave, MembershipState::Ban] + .contains(&content.membership) + { + return event.sender.as_str() + // TODO does None here mean the same as state_key = "" + != event.state_key.as_deref().unwrap_or(""); + } + } + + false + } + _ => false, + }, + PduStub::RoomV3PduStub(event) => event.state_key == Some("".into()), }, - Self::Sync(any_event) => panic!(), } } pub fn deserialize_content( diff --git a/tests/state_res.rs b/tests/state_res.rs index cdfd656d..221839d7 100644 --- a/tests/state_res.rs +++ b/tests/state_res.rs @@ -32,11 +32,11 @@ fn id(id: &str) -> EventId { fn alice() -> UserId { UserId::try_from("@alice:foo").unwrap() } -fn bobo() -> UserId { - UserId::try_from("@bobo:foo").unwrap() +fn bob() -> UserId { + UserId::try_from("@bob:foo").unwrap() } -fn devin() -> UserId { - UserId::try_from("@devin:foo").unwrap() +fn charlie() -> UserId { + UserId::try_from("@charlie:foo").unwrap() } fn zera() -> UserId { UserId::try_from("@zera:foo").unwrap() @@ -244,16 +244,16 @@ fn INITIAL_EVENTS() -> BTreeMap { ), to_init_pdu_event( "IMB", - bobo(), + bob(), EventType::RoomMember, - Some(bobo().to_string().as_str()), + Some(bob().to_string().as_str()), member_content_join(), ), to_init_pdu_event( "IMC", - devin(), + charlie(), EventType::RoomMember, - Some(devin().to_string().as_str()), + Some(charlie().to_string().as_str()), member_content_join(), ), to_init_pdu_event( @@ -292,7 +292,7 @@ fn do_check(events: &[StateEvent], edges: Vec>, expected_state_ids: // .init(); let mut resolver = StateResolution::default(); - // TODO what do we fill this with, everything ?? + let store = TestStore(RefCell::new( INITIAL_EVENTS() .values() @@ -354,16 +354,16 @@ fn do_check(events: &[StateEvent], edges: Vec>, expected_state_ids: .cloned() .collect::>(); - // println!( - // "RESOLVING {:?}", - // state_sets - // .iter() - // .map(|map| map - // .iter() - // .map(|((t, s), id)| (t, s, id.to_string())) - // .collect::>()) - // .collect::>() - // ); + tracing::debug!( + "RESOLVING {:?}", + state_sets + .iter() + .map(|map| map + .iter() + .map(|((t, s), id)| (t, s, id.to_string())) + .collect::>()) + .collect::>() + ); let resolved = resolver.resolve( &room_id(), @@ -397,10 +397,6 @@ fn do_check(events: &[StateEvent], edges: Vec>, expected_state_ids: } let auth_types = state_res::auth_types_for_event(fake_event); - // println!( - // "AUTH TYPES {:?}", - // auth_types.iter().map(|(t, id)| (t, id)).collect::>() - // ); let mut auth_events = vec![]; for key in auth_types { @@ -469,7 +465,7 @@ fn ban_vs_power_level() { alice(), EventType::RoomPowerLevels, Some(""), - json!({"users": {alice(): 100, bobo(): 50}}), + json!({"users": {alice(): 100, bob(): 50}}), ), to_init_pdu_event( "MA", @@ -482,15 +478,15 @@ fn ban_vs_power_level() { "MB", alice(), EventType::RoomMember, - Some(bobo().to_string().as_str()), + Some(bob().to_string().as_str()), member_content_ban(), ), to_init_pdu_event( "PB", - bobo(), + bob(), EventType::RoomPowerLevels, Some(""), - json!({"users": {alice(): 100, bobo(): 50}}), + json!({"users": {alice(): 100, bob(): 50}}), ), ]; @@ -518,7 +514,7 @@ fn ban_vs_power_level() { do_check(events, edges, expected_state_ids) } -// #[test] +#[test] fn topic_basic() { let events = &[ to_init_pdu_event("T1", alice(), EventType::RoomTopic, Some(""), json!({})), @@ -527,7 +523,7 @@ fn topic_basic() { alice(), EventType::RoomPowerLevels, Some(""), - json!({"users": {alice(): 100, bobo(): 50}}), + json!({"users": {alice(): 100, bob(): 50}}), ), to_init_pdu_event("T2", alice(), EventType::RoomTopic, Some(""), json!({})), to_init_pdu_event( @@ -535,16 +531,16 @@ fn topic_basic() { alice(), EventType::RoomPowerLevels, Some(""), - json!({"users": {alice(): 100, bobo(): 0}}), + json!({"users": {alice(): 100, bob(): 0}}), ), to_init_pdu_event( - "PAB", - bobo(), + "PB", + bob(), EventType::RoomPowerLevels, Some(""), - json!({"users": {alice(): 100, bobo(): 50}}), + json!({"users": {alice(): 100, bob(): 50}}), ), - to_init_pdu_event("T3", bobo(), EventType::RoomTopic, Some(""), json!({})), + to_init_pdu_event("T3", bob(), EventType::RoomTopic, Some(""), json!({})), ]; let edges = vec![ @@ -580,14 +576,14 @@ fn topic_reset() { alice(), EventType::RoomPowerLevels, Some(""), - json!({"users": {alice(): 100, bobo(): 50}}), + json!({"users": {alice(): 100, bob(): 50}}), ), - to_init_pdu_event("T2", bobo(), EventType::RoomTopic, Some(""), json!({})), + to_init_pdu_event("T2", bob(), EventType::RoomTopic, Some(""), json!({})), to_init_pdu_event( "MB", alice(), EventType::RoomMember, - Some(bobo().to_string().as_str()), + Some(bob().to_string().as_str()), member_content_ban(), ), ]; @@ -616,6 +612,30 @@ fn topic_reset() { do_check(events, edges, expected_state_ids) } +#[test] +fn test_event_map_none() { + let mut resolver = StateResolution::default(); + + let store = TestStore(RefCell::new(btreemap! {})); + + // build up the DAG + let (state_at_bob, state_at_charlie, expected) = store.set_up(); + + let resolved = match resolver.resolve( + &room_id(), + &RoomVersionId::version_2(), + &[state_at_bob, state_at_charlie], + None, + &store, + ) { + Ok(ResolutionResult::Resolved(state)) => state, + Err(e) => panic!("{}", e), + _ => panic!("conflicted state left"), + }; + + assert_eq!(expected, resolved) +} + #[test] fn test_lexicographical_sort() { let mut resolver = StateResolution::default(); @@ -699,16 +719,10 @@ impl StateStore for TestStore { ) -> Result, String> { use itertools::Itertools; - // println!( - // "EVENTS FOR AUTH {:?}", - // event_ids - // .iter() - // .map(|v| v.iter().map(ToString::to_string).collect::>()) - // .collect::>() - // ); - let mut chains = vec![]; for ids in event_ids { + // TODO state store `auth_event_ids` returns self in the event ids list + // when an event returns `auth_event_ids` self is not contained let chain = self .auth_event_ids(room_id, &ids)? .into_iter() @@ -719,10 +733,7 @@ impl StateStore for TestStore { if let Some(chain) = chains.first() { let rest = chains.iter().skip(1).flatten().cloned().collect(); let common = chain.intersection(&rest).collect::>(); - // println!( - // "COMMON {:?}", - // common.iter().map(ToString::to_string).collect::>() - // ); + Ok(chains .iter() .flatten() @@ -736,3 +747,113 @@ impl StateStore for TestStore { } } } + +impl TestStore { + pub fn set_up(&self) -> (StateMap, StateMap, StateMap) { + let create_event = to_pdu_event::( + "CREATE", + alice(), + EventType::RoomCreate, + Some(""), + json!({ "creator": alice() }), + &[], + &[], + ); + let cre = create_event.event_id().unwrap().clone(); + self.0 + .borrow_mut() + .insert(cre.clone(), create_event.clone()); + + let alice_mem = to_pdu_event( + "IMA", + alice(), + EventType::RoomMember, + Some(alice().to_string().as_str()), + member_content_join(), + &[cre.clone()], + &[cre.clone()], + ); + self.0 + .borrow_mut() + .insert(alice_mem.event_id().unwrap().clone(), alice_mem.clone()); + + let join_rules = to_pdu_event( + "IJR", + alice(), + EventType::RoomJoinRules, + Some(""), + json!({ "join_rule": JoinRule::Public }), + &[cre.clone(), alice_mem.event_id().unwrap().clone()], + &[alice_mem.event_id().unwrap().clone()], + ); + self.0 + .borrow_mut() + .insert(join_rules.event_id().unwrap().clone(), join_rules.clone()); + + // Bob and Charlie join at the same time, so there is a fork + // this will be represented in the state_sets when we resolve + let bob_mem = to_pdu_event( + "IMB", + bob(), + EventType::RoomMember, + Some(bob().to_string().as_str()), + member_content_join(), + &[cre.clone(), join_rules.event_id().unwrap().clone()], + &[join_rules.event_id().unwrap().clone()], + ); + self.0 + .borrow_mut() + .insert(bob_mem.event_id().unwrap().clone(), bob_mem.clone()); + + let charlie_mem = to_pdu_event( + "IMC", + charlie(), + EventType::RoomMember, + Some(charlie().to_string().as_str()), + member_content_join(), + &[cre.clone(), join_rules.event_id().unwrap().clone()], + &[join_rules.event_id().unwrap().clone()], + ); + self.0 + .borrow_mut() + .insert(charlie_mem.event_id().unwrap().clone(), charlie_mem.clone()); + + let state_at_bob = [&create_event, &alice_mem, &join_rules, &bob_mem] + .iter() + .map(|e| { + ( + (e.kind(), e.state_key().unwrap().clone()), + e.event_id().unwrap().clone(), + ) + }) + .collect::>(); + + let state_at_charlie = [&create_event, &alice_mem, &join_rules, &charlie_mem] + .iter() + .map(|e| { + ( + (e.kind(), e.state_key().unwrap().clone()), + e.event_id().unwrap().clone(), + ) + }) + .collect::>(); + + let expected = [ + &create_event, + &alice_mem, + &join_rules, + &bob_mem, + &charlie_mem, + ] + .iter() + .map(|e| { + ( + (e.kind(), e.state_key().unwrap().clone()), + e.event_id().unwrap().clone(), + ) + }) + .collect::>(); + + (state_at_bob, state_at_charlie, expected) + } +}