Return a 404 from `/state` for an outlier (#12087)

* Replace `get_state_for_pdu` with  `get_state_ids_for_pdu` and `get_events_as_list`.
* Return a 404 from `/state` and `/state_ids` for an outlier
This commit is contained in:
Richard van der Hoff 2022-03-21 11:23:32 +00:00 committed by GitHub
parent bf9d549e3a
commit afa17f0eab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 25 additions and 44 deletions

1
changelog.d/12087.bugfix Normal file
View File

@ -0,0 +1 @@
Fix a long-standing bug which caused the `/_matrix/federation/v1/state` and `.../state_ids` endpoints to return incorrect or invalid data when called for an event which we have stored as an "outlier".

View File

@ -22,7 +22,6 @@ from typing import (
Callable, Callable,
Collection, Collection,
Dict, Dict,
Iterable,
List, List,
Optional, Optional,
Tuple, Tuple,
@ -577,10 +576,10 @@ class FederationServer(FederationBase):
async def _on_context_state_request_compute( async def _on_context_state_request_compute(
self, room_id: str, event_id: Optional[str] self, room_id: str, event_id: Optional[str]
) -> Dict[str, list]: ) -> Dict[str, list]:
pdus: Collection[EventBase]
if event_id: if event_id:
pdus: Iterable[EventBase] = await self.handler.get_state_for_pdu( event_ids = await self.handler.get_state_ids_for_pdu(room_id, event_id)
room_id, event_id pdus = await self.store.get_events_as_list(event_ids)
)
else: else:
pdus = (await self.state.get_current_state(room_id)).values() pdus = (await self.state.get_current_state(room_id)).values()

View File

@ -950,54 +950,35 @@ class FederationHandler:
return event return event
async def get_state_for_pdu(self, room_id: str, event_id: str) -> List[EventBase]:
"""Returns the state at the event. i.e. not including said event."""
event = await self.store.get_event(event_id, check_room_id=room_id)
state_groups = await self.state_store.get_state_groups(room_id, [event_id])
if state_groups:
_, state = list(state_groups.items()).pop()
results = {(e.type, e.state_key): e for e in state}
if event.is_state():
# Get previous state
if "replaces_state" in event.unsigned:
prev_id = event.unsigned["replaces_state"]
if prev_id != event.event_id:
prev_event = await self.store.get_event(prev_id)
results[(event.type, event.state_key)] = prev_event
else:
del results[(event.type, event.state_key)]
res = list(results.values())
return res
else:
return []
async def get_state_ids_for_pdu(self, room_id: str, event_id: str) -> List[str]: async def get_state_ids_for_pdu(self, room_id: str, event_id: str) -> List[str]:
"""Returns the state at the event. i.e. not including said event.""" """Returns the state at the event. i.e. not including said event."""
event = await self.store.get_event(event_id, check_room_id=room_id) event = await self.store.get_event(event_id, check_room_id=room_id)
if event.internal_metadata.outlier:
raise NotFoundError("State not known at event %s" % (event_id,))
state_groups = await self.state_store.get_state_groups_ids(room_id, [event_id]) state_groups = await self.state_store.get_state_groups_ids(room_id, [event_id])
if state_groups: # get_state_groups_ids should return exactly one result
_, state = list(state_groups.items()).pop() assert len(state_groups) == 1
results = state
if event.is_state(): state_map = next(iter(state_groups.values()))
# Get previous state
state_key = event.get_state_key()
if state_key is not None:
# the event was not rejected (get_event raises a NotFoundError for rejected
# events) so the state at the event should include the event itself.
assert (
state_map.get((event.type, state_key)) == event.event_id
), "State at event did not include event itself"
# ... but we need the state *before* that event
if "replaces_state" in event.unsigned: if "replaces_state" in event.unsigned:
prev_id = event.unsigned["replaces_state"] prev_id = event.unsigned["replaces_state"]
if prev_id != event.event_id: state_map[(event.type, state_key)] = prev_id
results[(event.type, event.state_key)] = prev_id
else: else:
results.pop((event.type, event.state_key), None) del state_map[(event.type, state_key)]
return list(results.values()) return list(state_map.values())
else:
return []
async def on_backfill_request( async def on_backfill_request(
self, origin: str, room_id: str, pdu_list: List[str], limit: int self, origin: str, room_id: str, pdu_list: List[str], limit: int