Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Commit

Permalink
Return a 404 from /state for an outlier (#12087)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
richvdh authored Mar 21, 2022
1 parent bf9d549 commit afa17f0
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 44 deletions.
1 change: 1 addition & 0 deletions changelog.d/12087.bugfix
Original file line number Diff line number Diff line change
@@ -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".
7 changes: 3 additions & 4 deletions synapse/federation/federation_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
Callable,
Collection,
Dict,
Iterable,
List,
Optional,
Tuple,
Expand Down Expand Up @@ -577,10 +576,10 @@ async def _on_state_ids_request_compute(
async def _on_context_state_request_compute(
self, room_id: str, event_id: Optional[str]
) -> Dict[str, list]:
pdus: Collection[EventBase]
if event_id:
pdus: Iterable[EventBase] = await self.handler.get_state_for_pdu(
room_id, event_id
)
event_ids = await self.handler.get_state_ids_for_pdu(room_id, event_id)
pdus = await self.store.get_events_as_list(event_ids)
else:
pdus = (await self.state.get_current_state(room_id)).values()

Expand Down
61 changes: 21 additions & 40 deletions synapse/handlers/federation.py
Original file line number Diff line number Diff line change
Expand Up @@ -950,54 +950,35 @@ async def on_make_knock_request(

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]:
"""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)
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])

if state_groups:
_, state = list(state_groups.items()).pop()
results = state
# get_state_groups_ids should return exactly one result
assert len(state_groups) == 1

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:
results[(event.type, event.state_key)] = prev_id
else:
results.pop((event.type, event.state_key), None)
state_map = next(iter(state_groups.values()))

return list(results.values())
else:
return []
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:
prev_id = event.unsigned["replaces_state"]
state_map[(event.type, state_key)] = prev_id
else:
del state_map[(event.type, state_key)]

return list(state_map.values())

async def on_backfill_request(
self, origin: str, room_id: str, pdu_list: List[str], limit: int
Expand Down

0 comments on commit afa17f0

Please sign in to comment.