mirror of
https://gitlab.com/famedly/conduit.git
synced 2025-08-11 17:50:59 +00:00
feat: MSC4297, State Resolution v2.1
This commit is contained in:
parent
bd8686ec20
commit
d71d94a0c8
3 changed files with 132 additions and 20 deletions
22
Cargo.lock
generated
22
Cargo.lock
generated
|
@ -2521,7 +2521,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "ruma"
|
||||
version = "0.12.6"
|
||||
source = "git+https://github.com/ruma/ruma.git#796cc9f3aacb7e69f6ec3a0d5c2ba900c3e65910"
|
||||
source = "git+https://github.com/ruma/ruma.git#547efbf24831066ae3199dc51b93f6b3a30ea8e7"
|
||||
dependencies = [
|
||||
"assign",
|
||||
"js_int",
|
||||
|
@ -2540,7 +2540,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "ruma-appservice-api"
|
||||
version = "0.12.2"
|
||||
source = "git+https://github.com/ruma/ruma.git#796cc9f3aacb7e69f6ec3a0d5c2ba900c3e65910"
|
||||
source = "git+https://github.com/ruma/ruma.git#547efbf24831066ae3199dc51b93f6b3a30ea8e7"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
|
@ -2552,7 +2552,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "ruma-client-api"
|
||||
version = "0.20.4"
|
||||
source = "git+https://github.com/ruma/ruma.git#796cc9f3aacb7e69f6ec3a0d5c2ba900c3e65910"
|
||||
source = "git+https://github.com/ruma/ruma.git#547efbf24831066ae3199dc51b93f6b3a30ea8e7"
|
||||
dependencies = [
|
||||
"as_variant",
|
||||
"assign",
|
||||
|
@ -2575,7 +2575,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "ruma-common"
|
||||
version = "0.15.4"
|
||||
source = "git+https://github.com/ruma/ruma.git#796cc9f3aacb7e69f6ec3a0d5c2ba900c3e65910"
|
||||
source = "git+https://github.com/ruma/ruma.git#547efbf24831066ae3199dc51b93f6b3a30ea8e7"
|
||||
dependencies = [
|
||||
"as_variant",
|
||||
"base64 0.22.1",
|
||||
|
@ -2607,7 +2607,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "ruma-events"
|
||||
version = "0.30.5"
|
||||
source = "git+https://github.com/ruma/ruma.git#796cc9f3aacb7e69f6ec3a0d5c2ba900c3e65910"
|
||||
source = "git+https://github.com/ruma/ruma.git#547efbf24831066ae3199dc51b93f6b3a30ea8e7"
|
||||
dependencies = [
|
||||
"as_variant",
|
||||
"indexmap 2.9.0",
|
||||
|
@ -2631,7 +2631,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "ruma-federation-api"
|
||||
version = "0.11.2"
|
||||
source = "git+https://github.com/ruma/ruma.git#796cc9f3aacb7e69f6ec3a0d5c2ba900c3e65910"
|
||||
source = "git+https://github.com/ruma/ruma.git#547efbf24831066ae3199dc51b93f6b3a30ea8e7"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"headers",
|
||||
|
@ -2653,7 +2653,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "ruma-identifiers-validation"
|
||||
version = "0.10.1"
|
||||
source = "git+https://github.com/ruma/ruma.git#796cc9f3aacb7e69f6ec3a0d5c2ba900c3e65910"
|
||||
source = "git+https://github.com/ruma/ruma.git#547efbf24831066ae3199dc51b93f6b3a30ea8e7"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"thiserror 2.0.12",
|
||||
|
@ -2662,7 +2662,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "ruma-macros"
|
||||
version = "0.15.2"
|
||||
source = "git+https://github.com/ruma/ruma.git#796cc9f3aacb7e69f6ec3a0d5c2ba900c3e65910"
|
||||
source = "git+https://github.com/ruma/ruma.git#547efbf24831066ae3199dc51b93f6b3a30ea8e7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"proc-macro-crate",
|
||||
|
@ -2677,7 +2677,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "ruma-push-gateway-api"
|
||||
version = "0.11.0"
|
||||
source = "git+https://github.com/ruma/ruma.git#796cc9f3aacb7e69f6ec3a0d5c2ba900c3e65910"
|
||||
source = "git+https://github.com/ruma/ruma.git#547efbf24831066ae3199dc51b93f6b3a30ea8e7"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
|
@ -2689,7 +2689,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "ruma-signatures"
|
||||
version = "0.17.1"
|
||||
source = "git+https://github.com/ruma/ruma.git#796cc9f3aacb7e69f6ec3a0d5c2ba900c3e65910"
|
||||
source = "git+https://github.com/ruma/ruma.git#547efbf24831066ae3199dc51b93f6b3a30ea8e7"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"ed25519-dalek",
|
||||
|
@ -2705,7 +2705,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "ruma-state-res"
|
||||
version = "0.13.0"
|
||||
source = "git+https://github.com/ruma/ruma.git#796cc9f3aacb7e69f6ec3a0d5c2ba900c3e65910"
|
||||
source = "git+https://github.com/ruma/ruma.git#547efbf24831066ae3199dc51b93f6b3a30ea8e7"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::{
|
|||
};
|
||||
|
||||
pub use data::Data;
|
||||
use ruma::{api::client::error::ErrorKind, EventId, RoomId};
|
||||
use ruma::{api::client::error::ErrorKind, state_res::StateMap, EventId, RoomId};
|
||||
use tracing::{debug, error, warn};
|
||||
|
||||
use crate::{services, Error, Result};
|
||||
|
@ -161,4 +161,87 @@ impl Service {
|
|||
|
||||
Ok(found)
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self, conflicted_state_set))]
|
||||
/// Fetches the conflicted state subgraph of the given events
|
||||
pub fn get_conflicted_state_subgraph(
|
||||
&self,
|
||||
room_id: &RoomId,
|
||||
conflicted_state_set: &StateMap<Vec<Arc<EventId>>>,
|
||||
) -> Result<HashSet<Arc<EventId>>> {
|
||||
let conflicted_event_ids: HashSet<_> =
|
||||
conflicted_state_set.values().flatten().cloned().collect();
|
||||
let mut conflicted_state_subgraph = HashSet::new();
|
||||
|
||||
let mut stack = vec![conflicted_event_ids.iter().cloned().collect::<Vec<_>>()];
|
||||
let mut path = Vec::new();
|
||||
|
||||
let mut seen_events = HashSet::new();
|
||||
|
||||
let next_event = |stack: &mut Vec<Vec<_>>, path: &mut Vec<_>| {
|
||||
while stack.last().is_some_and(|s| s.is_empty()) {
|
||||
stack.pop();
|
||||
path.pop();
|
||||
}
|
||||
|
||||
stack.last_mut().and_then(|s| s.pop())
|
||||
};
|
||||
|
||||
while let Some(event_id) = next_event(&mut stack, &mut path) {
|
||||
path.push(event_id.clone());
|
||||
|
||||
if conflicted_state_subgraph.contains(&event_id) {
|
||||
// If we reach a conflicted state subgraph path, this path must also be part of
|
||||
// the conflicted state subgraph, as we will eventually reach a conflicted event
|
||||
// if we follow this path.
|
||||
//
|
||||
// We check if path > 1 here and below, as we don't consider a single conflicted
|
||||
// event to be a path from one conflicted to another.
|
||||
if path.len() > 1 {
|
||||
conflicted_state_subgraph.extend(path.iter().cloned());
|
||||
}
|
||||
|
||||
// All possible paths from this event must have been traversed in the iteration
|
||||
// that caused this event to be added to the conflicted state subgraph in the first
|
||||
// place.
|
||||
//
|
||||
// We pop the path here and below as it won't be removed by `next_event`, due to us
|
||||
// never pushing it's auth events to the stack.
|
||||
path.pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
if conflicted_event_ids.contains(&event_id) && path.len() > 1 {
|
||||
conflicted_state_subgraph.extend(path.iter().cloned());
|
||||
}
|
||||
|
||||
if seen_events.contains(&event_id) {
|
||||
// All possible paths from this event must have been traversed in the iteration
|
||||
// that caused this event to be added to the conflicted state subgraph in the first
|
||||
// place.
|
||||
path.pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(pdu) = services().rooms.timeline.get_pdu(&event_id)? {
|
||||
if pdu.room_id().as_ref() != room_id {
|
||||
return Err(Error::BadRequest(
|
||||
ErrorKind::forbidden(),
|
||||
"Evil event in db",
|
||||
));
|
||||
}
|
||||
|
||||
stack.push(pdu.auth_events.clone());
|
||||
} else {
|
||||
warn!(?event_id, "Could not find pdu mentioned in auth events");
|
||||
return Err(Error::BadDatabase(
|
||||
"Missing auth event for PDU stored in database",
|
||||
));
|
||||
}
|
||||
|
||||
seen_events.insert(event_id);
|
||||
}
|
||||
|
||||
Ok(conflicted_state_subgraph)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ use ruma::{
|
|||
StateEventType, TimelineEventType,
|
||||
},
|
||||
int,
|
||||
room_version_rules::{AuthorizationRules, RoomVersionRules},
|
||||
room_version_rules::{AuthorizationRules, RoomVersionRules, StateResolutionV2Rules},
|
||||
state_res::{self, StateMap},
|
||||
uint, CanonicalJsonObject, CanonicalJsonValue, EventId, MilliSecondsSinceUnixEpoch,
|
||||
OwnedServerName, OwnedServerSigningKeyId, RoomId, ServerName,
|
||||
|
@ -709,10 +709,11 @@ impl Service {
|
|||
let lock = services().globals.stateres_mutex.lock();
|
||||
|
||||
let result = state_res::resolve(
|
||||
&room_version_id
|
||||
.rules()
|
||||
.expect("Supported room version has rules")
|
||||
.authorization,
|
||||
&room_version_rules.authorization,
|
||||
room_version_rules
|
||||
.state_res
|
||||
.v2_rules()
|
||||
.expect("We only support room versions using state resolution v2"),
|
||||
&fork_states,
|
||||
auth_chain_sets,
|
||||
|id| {
|
||||
|
@ -722,6 +723,13 @@ impl Service {
|
|||
}
|
||||
res.ok().flatten()
|
||||
},
|
||||
|css| {
|
||||
services()
|
||||
.rooms
|
||||
.auth_chain
|
||||
.get_conflicted_state_subgraph(room_id, css)
|
||||
.ok()
|
||||
},
|
||||
);
|
||||
drop(lock);
|
||||
|
||||
|
@ -961,7 +969,15 @@ impl Service {
|
|||
}
|
||||
|
||||
let new_room_state = self
|
||||
.resolve_state(room_id, &room_version_rules.authorization, state_after)
|
||||
.resolve_state(
|
||||
room_id,
|
||||
&room_version_rules.authorization,
|
||||
room_version_rules
|
||||
.state_res
|
||||
.v2_rules()
|
||||
.expect("We only support room versions using state resolution v2"),
|
||||
state_after,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Set the new room state to the resolved state
|
||||
|
@ -1039,6 +1055,7 @@ impl Service {
|
|||
&self,
|
||||
room_id: &RoomId,
|
||||
auth_rules: &AuthorizationRules,
|
||||
state_res_rules: &StateResolutionV2Rules,
|
||||
incoming_state: HashMap<u64, Arc<EventId>>,
|
||||
) -> Result<Arc<HashSet<CompressedStateEvent>>> {
|
||||
debug!("Loading current room state ids");
|
||||
|
@ -1097,8 +1114,20 @@ impl Service {
|
|||
};
|
||||
|
||||
let lock = services().globals.stateres_mutex.lock();
|
||||
let state = match state_res::resolve(auth_rules, &fork_states, auth_chain_sets, fetch_event)
|
||||
{
|
||||
let state = match state_res::resolve(
|
||||
auth_rules,
|
||||
state_res_rules,
|
||||
&fork_states,
|
||||
auth_chain_sets,
|
||||
fetch_event,
|
||||
|css| {
|
||||
services()
|
||||
.rooms
|
||||
.auth_chain
|
||||
.get_conflicted_state_subgraph(room_id, css)
|
||||
.ok()
|
||||
},
|
||||
) {
|
||||
Ok(new_state) => new_state,
|
||||
Err(_) => {
|
||||
return Err(Error::bad_database("State resolution failed, either an event could not be found or deserialization"));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue