From 39efad1b8fcaa25e14b4f4bd59b5194cb4ab5e11 Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Wed, 3 Nov 2021 18:38:26 -0500 Subject: [PATCH 01/13] Allow events to be created with no prev_events The event still needs to have auth_events defined to be valid. Split out from https://github.com/matrix-org/synapse/pull/11114 --- synapse/api/room_versions.py | 31 +++++++++++++++++++++++++++++++ synapse/handlers/message.py | 22 ++++++++++++++++------ 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/synapse/api/room_versions.py b/synapse/api/room_versions.py index 0a895bba480a..7ef4701b137a 100644 --- a/synapse/api/room_versions.py +++ b/synapse/api/room_versions.py @@ -81,6 +81,8 @@ class RoomVersion: msc2716_historical = attr.ib(type=bool) # MSC2716: Adds support for redacting "insertion", "chunk", and "marker" events msc2716_redactions = attr.ib(type=bool) + # MSC2716: Adds support for events with no `prev_events` but with some `auth_events` + msc2716_empty_prev_events = attr.ib(type=bool) class RoomVersions: @@ -99,6 +101,7 @@ class RoomVersions: msc2403_knocking=False, msc2716_historical=False, msc2716_redactions=False, + msc2716_empty_prev_events=False, ) V2 = RoomVersion( "2", @@ -115,6 +118,7 @@ class RoomVersions: msc2403_knocking=False, msc2716_historical=False, msc2716_redactions=False, + msc2716_empty_prev_events=False, ) V3 = RoomVersion( "3", @@ -131,6 +135,7 @@ class RoomVersions: msc2403_knocking=False, msc2716_historical=False, msc2716_redactions=False, + msc2716_empty_prev_events=False, ) V4 = RoomVersion( "4", @@ -147,6 +152,7 @@ class RoomVersions: msc2403_knocking=False, msc2716_historical=False, msc2716_redactions=False, + msc2716_empty_prev_events=False, ) V5 = RoomVersion( "5", @@ -163,6 +169,7 @@ class RoomVersions: msc2403_knocking=False, msc2716_historical=False, msc2716_redactions=False, + msc2716_empty_prev_events=False, ) V6 = RoomVersion( "6", @@ -179,6 +186,7 @@ class RoomVersions: msc2403_knocking=False, msc2716_historical=False, msc2716_redactions=False, + msc2716_empty_prev_events=False, ) MSC2176 = RoomVersion( "org.matrix.msc2176", @@ -195,6 +203,7 @@ class RoomVersions: msc2403_knocking=False, msc2716_historical=False, msc2716_redactions=False, + msc2716_empty_prev_events=False, ) V7 = RoomVersion( "7", @@ -211,6 +220,7 @@ class RoomVersions: msc2403_knocking=True, msc2716_historical=False, msc2716_redactions=False, + msc2716_empty_prev_events=False, ) V8 = RoomVersion( "8", @@ -227,6 +237,7 @@ class RoomVersions: msc2403_knocking=True, msc2716_historical=False, msc2716_redactions=False, + msc2716_empty_prev_events=False, ) V9 = RoomVersion( "9", @@ -243,6 +254,7 @@ class RoomVersions: msc2403_knocking=True, msc2716_historical=False, msc2716_redactions=False, + msc2716_empty_prev_events=False, ) MSC2716v3 = RoomVersion( "org.matrix.msc2716v3", @@ -259,6 +271,24 @@ class RoomVersions: msc2403_knocking=True, msc2716_historical=True, msc2716_redactions=True, + msc2716_empty_prev_events=False, + ) + MSC2716v4 = RoomVersion( + "org.matrix.msc2716v4", + RoomDisposition.UNSTABLE, + EventFormatVersions.V3, + StateResolutionVersions.V2, + enforce_key_validity=True, + special_case_aliases_auth=False, + strict_canonicaljson=True, + limit_notifications_power_levels=True, + msc2176_redaction_rules=False, + msc3083_join_rules=False, + msc3375_redaction_rules=False, + msc2403_knocking=True, + msc2716_historical=True, + msc2716_redactions=True, + msc2716_empty_prev_events=True, ) @@ -276,6 +306,7 @@ class RoomVersions: RoomVersions.V8, RoomVersions.V9, RoomVersions.MSC2716v3, + RoomVersions.MSC2716v4, ) } diff --git a/synapse/handlers/message.py b/synapse/handlers/message.py index b7bc187169dc..9064e659e3bf 100644 --- a/synapse/handlers/message.py +++ b/synapse/handlers/message.py @@ -949,14 +949,24 @@ async def create_new_client_event( else: prev_event_ids = await self.store.get_prev_events_for_room(builder.room_id) - # we now ought to have some prev_events (unless it's a create event). - # - # do a quick sanity check here, rather than waiting until we've created the + # Do a quick sanity check here, rather than waiting until we've created the # event and then try to auth it (which fails with a somewhat confusing "No # create event in auth events") - assert ( - builder.type == EventTypes.Create or len(prev_event_ids) > 0 - ), "Attempting to create an event with no prev_events" + room_version_obj = await self.store.get_room_version(builder.room_id) + if room_version_obj.msc2716_empty_prev_events: + # We allow events with no `prev_events` but it better have some `auth_events` + assert ( + builder.type == EventTypes.Create + or len(prev_event_ids) > 0 + # Allow an event to have empty list of prev_event_ids + # only if it has auth_event_ids. + or (auth_event_ids and len(auth_event_ids) > 0) + ), "Attempting to create an event with no prev_events or auth_event_ids" + else: + # we now ought to have some prev_events (unless it's a create event). + assert ( + builder.type == EventTypes.Create or len(prev_event_ids) > 0 + ), "Attempting to create an event with no prev_events" event = await builder.build( prev_event_ids=prev_event_ids, From 66f085911cf309eee6b283663647e223b55d8c49 Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Wed, 3 Nov 2021 18:45:17 -0500 Subject: [PATCH 02/13] Add changelog --- changelog.d/11243.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/11243.feature diff --git a/changelog.d/11243.feature b/changelog.d/11243.feature new file mode 100644 index 000000000000..4f8176434585 --- /dev/null +++ b/changelog.d/11243.feature @@ -0,0 +1 @@ +Allow experimental room version `org.matrix.msc2716v4` to allow events to be created without `prev_events` (only `auth_events`). From 9f45d09fe7d7fb927d9c17aff9247bef0e0c20c7 Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Wed, 3 Nov 2021 18:51:29 -0500 Subject: [PATCH 03/13] Allow member state events without prev_events --- synapse/handlers/room_member.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/synapse/handlers/room_member.py b/synapse/handlers/room_member.py index 08244b690d4c..343e3c6b7b03 100644 --- a/synapse/handlers/room_member.py +++ b/synapse/handlers/room_member.py @@ -644,7 +644,7 @@ async def update_membership_locked( if block_invite: raise SynapseError(403, "Invites have been disabled on this server") - if prev_event_ids: + if prev_event_ids is not None: return await self._local_membership_update( requester=requester, target=target, From e0934819666c7d14088a924329bf0b7853296081 Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Thu, 4 Nov 2021 15:27:40 -0500 Subject: [PATCH 04/13] More clarification and specificity in errors/comments Co-authored-by: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> --- changelog.d/11243.feature | 2 +- synapse/handlers/message.py | 4 ++-- synapse/handlers/room_member.py | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/changelog.d/11243.feature b/changelog.d/11243.feature index 4f8176434585..f4331826ce71 100644 --- a/changelog.d/11243.feature +++ b/changelog.d/11243.feature @@ -1 +1 @@ -Allow experimental room version `org.matrix.msc2716v4` to allow events to be created without `prev_events` (only `auth_events`). +Add experimental room version `org.matrix.msc2716v4` to allow events to be created without `prev_events` (only `auth_events`). diff --git a/synapse/handlers/message.py b/synapse/handlers/message.py index 9064e659e3bf..0755a51904bf 100644 --- a/synapse/handlers/message.py +++ b/synapse/handlers/message.py @@ -961,12 +961,12 @@ async def create_new_client_event( # Allow an event to have empty list of prev_event_ids # only if it has auth_event_ids. or (auth_event_ids and len(auth_event_ids) > 0) - ), "Attempting to create an event with no prev_events or auth_event_ids" + ), "Attempting to create a non-m.room.create event with no prev_events or auth_event_ids" else: # we now ought to have some prev_events (unless it's a create event). assert ( builder.type == EventTypes.Create or len(prev_event_ids) > 0 - ), "Attempting to create an event with no prev_events" + ), "Attempting to create a non-m.room.create event with no prev_events" event = await builder.build( prev_event_ids=prev_event_ids, diff --git a/synapse/handlers/room_member.py b/synapse/handlers/room_member.py index 343e3c6b7b03..e4feb4acb8d6 100644 --- a/synapse/handlers/room_member.py +++ b/synapse/handlers/room_member.py @@ -644,6 +644,7 @@ async def update_membership_locked( if block_invite: raise SynapseError(403, "Invites have been disabled on this server") + # An empty prev_events list is allowed by room version "org.matrix.msc2716v4" if prev_event_ids is not None: return await self._local_membership_update( requester=requester, From 4bb5fd33a3aabfd4f2e591f4bb0541771fa859e1 Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Thu, 4 Nov 2021 20:18:46 -0500 Subject: [PATCH 05/13] Add tests for creating events with empty prev_events --- synapse/handlers/message.py | 1 + tests/handlers/test_message.py | 75 ++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/synapse/handlers/message.py b/synapse/handlers/message.py index 0755a51904bf..30b7bfca3ca0 100644 --- a/synapse/handlers/message.py +++ b/synapse/handlers/message.py @@ -918,6 +918,7 @@ async def create_new_client_event( full_state_ids_at_event = None if auth_event_ids is not None: # If auth events are provided, prev events must be also. + # prev_event_ids could be an empty array though. assert prev_event_ids is not None # Copy the full auth state before it stripped down diff --git a/tests/handlers/test_message.py b/tests/handlers/test_message.py index 8a8d369faca1..99b11f11bfd5 100644 --- a/tests/handlers/test_message.py +++ b/tests/handlers/test_message.py @@ -15,12 +15,14 @@ from typing import Tuple from synapse.api.constants import EventTypes +from synapse.api.room_versions import RoomVersions from synapse.events import EventBase from synapse.events.snapshot import EventContext from synapse.rest import admin from synapse.rest.client import login, room from synapse.types import create_requester from synapse.util.stringutils import random_string +from tests.test_utils.event_injection import create_event from tests import unittest @@ -156,6 +158,79 @@ def test_duplicated_txn_id_one_call(self): self.assertEqual(len(events), 2) self.assertEqual(events[0].event_id, events[1].event_id) + def test_create_empty_prev_events_in_msc2716_room_version(self): + """Try to create an event without any prev_events (only auth_events). + + This is currently only supported in the experimental MSC2716 room versions. + """ + room_id = self.helper.create_room_as( + self.user_id, + tok=self.access_token, + room_version=RoomVersions.MSC2716v4.identifier, + ) + + # Create a member event we can use as an auth_event + memberEvent, memberEventContext = self.get_success( + create_event( + self.hs, + room_id=room_id, + type="m.room.member", + sender=self.requester.user.to_string(), + state_key=self.requester.user.to_string(), + content={"membership": "join"}, + ) + ) + self.get_success( + self.persist_event_storage.persist_event(memberEvent, memberEventContext) + ) + + # Try to create the event with empty prev_events (only auth_events) + event, _ = self.get_success( + self.handler.create_event( + self.requester, + { + "type": EventTypes.Message, + "room_id": room_id, + "sender": self.requester.user.to_string(), + "content": {"msgtype": "m.text", "body": random_string(5)}, + }, + # Empty prev_events is the key thing we're testing here + prev_event_ids=[], + auth_event_ids=[memberEvent.event_id], + ) + ) + self.assertIsNotNone(event) + + def test_reject_empty_prev_events_and_auth_events_in_msc2716_room_version( + self, + ): + """Try to create an event without any prev_events (only auth_events). + + This is currently only supported in the experimental MSC2716 room versions. + """ + room_id = self.helper.create_room_as( + self.user_id, + tok=self.access_token, + room_version=RoomVersions.MSC2716v4.identifier, + ) + + # Try to create the event with empty prev_events and empty auth_events + self.get_failure( + self.handler.create_event( + self.requester, + { + "type": EventTypes.Message, + "room_id": room_id, + "sender": self.requester.user.to_string(), + "content": {"msgtype": "m.text", "body": random_string(5)}, + }, + prev_event_ids=[], + # The event should be rejected when there are no auth_events + auth_event_ids=[], + ), + AssertionError, + ) + class ServerAclValidationTestCase(unittest.HomeserverTestCase): servlets = [ From f421a2df2d95da79b54a17e50a306dda4b631547 Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Thu, 4 Nov 2021 20:25:30 -0500 Subject: [PATCH 06/13] Fix lints --- tests/handlers/test_message.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/handlers/test_message.py b/tests/handlers/test_message.py index 99b11f11bfd5..6a38fa4dd785 100644 --- a/tests/handlers/test_message.py +++ b/tests/handlers/test_message.py @@ -22,9 +22,9 @@ from synapse.rest.client import login, room from synapse.types import create_requester from synapse.util.stringutils import random_string -from tests.test_utils.event_injection import create_event from tests import unittest +from tests.test_utils.event_injection import create_event logger = logging.getLogger(__name__) From d10625eb18f010623df5cff12987f09347b0af5e Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Mon, 8 Nov 2021 12:35:29 -0600 Subject: [PATCH 07/13] Update test description to be accurate Previously just a copy paste Co-authored-by: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> --- tests/handlers/test_message.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/handlers/test_message.py b/tests/handlers/test_message.py index 6a38fa4dd785..d854c7547398 100644 --- a/tests/handlers/test_message.py +++ b/tests/handlers/test_message.py @@ -161,7 +161,7 @@ def test_duplicated_txn_id_one_call(self): def test_create_empty_prev_events_in_msc2716_room_version(self): """Try to create an event without any prev_events (only auth_events). - This is currently only supported in the experimental MSC2716 room versions. + This is currently only supported in the experimental MSC2716v4+ room versions. """ room_id = self.helper.create_room_as( self.user_id, @@ -204,9 +204,10 @@ def test_create_empty_prev_events_in_msc2716_room_version(self): def test_reject_empty_prev_events_and_auth_events_in_msc2716_room_version( self, ): - """Try to create an event without any prev_events (only auth_events). + """Try to create an event without any prev_events or auth_events. + Expect an exception to be raised. - This is currently only supported in the experimental MSC2716 room versions. + This is currently only supported in the experimental MSC2716v4 room versions. """ room_id = self.helper.create_room_as( self.user_id, From 2370dcadf67bd4a0785c37479f9b33b6a03696c6 Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Tue, 9 Nov 2021 14:14:37 -0600 Subject: [PATCH 08/13] Remove redundant length check > if `len(auth_event_ids) == 0` then `auth_event_ids` will be falsey, so the extra check is redundant. > > -- https://github.com/matrix-org/synapse/pull/11243#discussion_r745408186 --- synapse/handlers/message.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/synapse/handlers/message.py b/synapse/handlers/message.py index 30b7bfca3ca0..8683c9879047 100644 --- a/synapse/handlers/message.py +++ b/synapse/handlers/message.py @@ -961,7 +961,7 @@ async def create_new_client_event( or len(prev_event_ids) > 0 # Allow an event to have empty list of prev_event_ids # only if it has auth_event_ids. - or (auth_event_ids and len(auth_event_ids) > 0) + or auth_event_ids ), "Attempting to create a non-m.room.create event with no prev_events or auth_event_ids" else: # we now ought to have some prev_events (unless it's a create event). From e2928b54a2b4359f6034b6d557c7f36512747ec3 Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Wed, 1 Dec 2021 17:34:33 -0600 Subject: [PATCH 09/13] No new room version necessary and allow no prev_events via allow_no_prev_events=True See https://github.com/matrix-org/synapse/pull/11243/files#discussion_r750818736 --- changelog.d/11243.feature | 2 +- synapse/api/room_versions.py | 31 ----------- synapse/handlers/message.py | 6 +- synapse/handlers/room_member.py | 2 +- tests/handlers/test_message.py | 98 +++++++++++++++++++++------------ 5 files changed, 69 insertions(+), 70 deletions(-) diff --git a/changelog.d/11243.feature b/changelog.d/11243.feature index f4331826ce71..e1de5a4d3e9c 100644 --- a/changelog.d/11243.feature +++ b/changelog.d/11243.feature @@ -1 +1 @@ -Add experimental room version `org.matrix.msc2716v4` to allow events to be created without `prev_events` (only `auth_events`). +Allow events to be created without `prev_events` (only `auth_events`). diff --git a/synapse/api/room_versions.py b/synapse/api/room_versions.py index 7ef4701b137a..0a895bba480a 100644 --- a/synapse/api/room_versions.py +++ b/synapse/api/room_versions.py @@ -81,8 +81,6 @@ class RoomVersion: msc2716_historical = attr.ib(type=bool) # MSC2716: Adds support for redacting "insertion", "chunk", and "marker" events msc2716_redactions = attr.ib(type=bool) - # MSC2716: Adds support for events with no `prev_events` but with some `auth_events` - msc2716_empty_prev_events = attr.ib(type=bool) class RoomVersions: @@ -101,7 +99,6 @@ class RoomVersions: msc2403_knocking=False, msc2716_historical=False, msc2716_redactions=False, - msc2716_empty_prev_events=False, ) V2 = RoomVersion( "2", @@ -118,7 +115,6 @@ class RoomVersions: msc2403_knocking=False, msc2716_historical=False, msc2716_redactions=False, - msc2716_empty_prev_events=False, ) V3 = RoomVersion( "3", @@ -135,7 +131,6 @@ class RoomVersions: msc2403_knocking=False, msc2716_historical=False, msc2716_redactions=False, - msc2716_empty_prev_events=False, ) V4 = RoomVersion( "4", @@ -152,7 +147,6 @@ class RoomVersions: msc2403_knocking=False, msc2716_historical=False, msc2716_redactions=False, - msc2716_empty_prev_events=False, ) V5 = RoomVersion( "5", @@ -169,7 +163,6 @@ class RoomVersions: msc2403_knocking=False, msc2716_historical=False, msc2716_redactions=False, - msc2716_empty_prev_events=False, ) V6 = RoomVersion( "6", @@ -186,7 +179,6 @@ class RoomVersions: msc2403_knocking=False, msc2716_historical=False, msc2716_redactions=False, - msc2716_empty_prev_events=False, ) MSC2176 = RoomVersion( "org.matrix.msc2176", @@ -203,7 +195,6 @@ class RoomVersions: msc2403_knocking=False, msc2716_historical=False, msc2716_redactions=False, - msc2716_empty_prev_events=False, ) V7 = RoomVersion( "7", @@ -220,7 +211,6 @@ class RoomVersions: msc2403_knocking=True, msc2716_historical=False, msc2716_redactions=False, - msc2716_empty_prev_events=False, ) V8 = RoomVersion( "8", @@ -237,7 +227,6 @@ class RoomVersions: msc2403_knocking=True, msc2716_historical=False, msc2716_redactions=False, - msc2716_empty_prev_events=False, ) V9 = RoomVersion( "9", @@ -254,7 +243,6 @@ class RoomVersions: msc2403_knocking=True, msc2716_historical=False, msc2716_redactions=False, - msc2716_empty_prev_events=False, ) MSC2716v3 = RoomVersion( "org.matrix.msc2716v3", @@ -271,24 +259,6 @@ class RoomVersions: msc2403_knocking=True, msc2716_historical=True, msc2716_redactions=True, - msc2716_empty_prev_events=False, - ) - MSC2716v4 = RoomVersion( - "org.matrix.msc2716v4", - RoomDisposition.UNSTABLE, - EventFormatVersions.V3, - StateResolutionVersions.V2, - enforce_key_validity=True, - special_case_aliases_auth=False, - strict_canonicaljson=True, - limit_notifications_power_levels=True, - msc2176_redaction_rules=False, - msc3083_join_rules=False, - msc3375_redaction_rules=False, - msc2403_knocking=True, - msc2716_historical=True, - msc2716_redactions=True, - msc2716_empty_prev_events=True, ) @@ -306,7 +276,6 @@ class RoomVersions: RoomVersions.V8, RoomVersions.V9, RoomVersions.MSC2716v3, - RoomVersions.MSC2716v4, ) } diff --git a/synapse/handlers/message.py b/synapse/handlers/message.py index 8683c9879047..67e256d70518 100644 --- a/synapse/handlers/message.py +++ b/synapse/handlers/message.py @@ -502,6 +502,7 @@ async def create_event( require_consent: bool = True, outlier: bool = False, historical: bool = False, + allow_no_prev_events: bool = False, depth: Optional[int] = None, ) -> Tuple[EventBase, EventContext]: """ @@ -613,6 +614,7 @@ async def create_event( prev_event_ids=prev_event_ids, auth_event_ids=auth_event_ids, depth=depth, + allow_no_prev_events=allow_no_prev_events, ) # In an ideal world we wouldn't need the second part of this condition. However, @@ -888,6 +890,7 @@ async def create_new_client_event( prev_event_ids: Optional[List[str]] = None, auth_event_ids: Optional[List[str]] = None, depth: Optional[int] = None, + allow_no_prev_events: bool = False, ) -> Tuple[EventBase, EventContext]: """Create a new event for a local client @@ -953,8 +956,7 @@ async def create_new_client_event( # Do a quick sanity check here, rather than waiting until we've created the # event and then try to auth it (which fails with a somewhat confusing "No # create event in auth events") - room_version_obj = await self.store.get_room_version(builder.room_id) - if room_version_obj.msc2716_empty_prev_events: + if allow_no_prev_events: # We allow events with no `prev_events` but it better have some `auth_events` assert ( builder.type == EventTypes.Create diff --git a/synapse/handlers/room_member.py b/synapse/handlers/room_member.py index e4feb4acb8d6..9dc39240229a 100644 --- a/synapse/handlers/room_member.py +++ b/synapse/handlers/room_member.py @@ -644,7 +644,7 @@ async def update_membership_locked( if block_invite: raise SynapseError(403, "Invites have been disabled on this server") - # An empty prev_events list is allowed by room version "org.matrix.msc2716v4" + # An empty prev_events list is allowed as long as the auth_event_ids are present if prev_event_ids is not None: return await self._local_membership_update( requester=requester, diff --git a/tests/handlers/test_message.py b/tests/handlers/test_message.py index d854c7547398..328d0e1aaa9a 100644 --- a/tests/handlers/test_message.py +++ b/tests/handlers/test_message.py @@ -53,6 +53,24 @@ def prepare(self, reactor, clock, hs): self.requester = create_requester(self.user_id, access_token_id=self.token_id) + def _create_and_persist_member_event(self) -> Tuple[EventBase, EventContext]: + # Create a member event we can use as an auth_event + memberEvent, memberEventContext = self.get_success( + create_event( + self.hs, + room_id=self.room_id, + type="m.room.member", + sender=self.requester.user.to_string(), + state_key=self.requester.user.to_string(), + content={"membership": "join"}, + ) + ) + self.get_success( + self.persist_event_storage.persist_event(memberEvent, memberEventContext) + ) + + return memberEvent, memberEventContext + def _create_duplicate_event(self, txn_id: str) -> Tuple[EventBase, EventContext]: """Create a new event with the given transaction ID. All events produced by this method will be considered duplicates. @@ -158,76 +176,86 @@ def test_duplicated_txn_id_one_call(self): self.assertEqual(len(events), 2) self.assertEqual(events[0].event_id, events[1].event_id) - def test_create_empty_prev_events_in_msc2716_room_version(self): - """Try to create an event without any prev_events (only auth_events). - - This is currently only supported in the experimental MSC2716v4+ room versions. + def test_when_empty_prev_events_allowed_create_event_with_empty_prev_events(self): + """When we set allow_no_prev_events=True, should be able to create a + event without any prev_events (only auth_events). """ - room_id = self.helper.create_room_as( - self.user_id, - tok=self.access_token, - room_version=RoomVersions.MSC2716v4.identifier, - ) - # Create a member event we can use as an auth_event - memberEvent, memberEventContext = self.get_success( - create_event( - self.hs, - room_id=room_id, - type="m.room.member", - sender=self.requester.user.to_string(), - state_key=self.requester.user.to_string(), - content={"membership": "join"}, - ) - ) - self.get_success( - self.persist_event_storage.persist_event(memberEvent, memberEventContext) - ) + memberEvent, _ = self._create_and_persist_member_event() - # Try to create the event with empty prev_events (only auth_events) + # Try to create the event with empty prev_events bit with some auth_events event, _ = self.get_success( self.handler.create_event( self.requester, { "type": EventTypes.Message, - "room_id": room_id, + "room_id": self.room_id, "sender": self.requester.user.to_string(), "content": {"msgtype": "m.text", "body": random_string(5)}, }, # Empty prev_events is the key thing we're testing here prev_event_ids=[], + # But with some auth_events auth_event_ids=[memberEvent.event_id], + # Allow no prev_events! + allow_no_prev_events=True, ) ) self.assertIsNotNone(event) - def test_reject_empty_prev_events_and_auth_events_in_msc2716_room_version( + def test_when_empty_prev_events_not_allowed_reject_event_with_empty_prev_events( self, ): - """Try to create an event without any prev_events or auth_events. - Expect an exception to be raised. - - This is currently only supported in the experimental MSC2716v4 room versions. + """When we set allow_no_prev_events=False, shouldn't be able to create a + event without any prev_events even if it has auth_events. Expect an + exception to be raised. """ - room_id = self.helper.create_room_as( - self.user_id, - tok=self.access_token, - room_version=RoomVersions.MSC2716v4.identifier, + # Create a member event we can use as an auth_event + memberEvent, _ = self._create_and_persist_member_event() + + # Try to create the event with empty prev_events but with some auth_events + self.get_failure( + self.handler.create_event( + self.requester, + { + "type": EventTypes.Message, + "room_id": self.room_id, + "sender": self.requester.user.to_string(), + "content": {"msgtype": "m.text", "body": random_string(5)}, + }, + # Empty prev_events is the key thing we're testing here + prev_event_ids=[], + # But with some auth_events + auth_event_ids=[memberEvent.event_id], + # We expect the test to fail because empty prev_events are not + # allowed here! + allow_no_prev_events=False, + ), + AssertionError, ) + def test_when_empty_prev_events_allowed_reject_event_with_empty_prev_events_and_auth_events( + self, + ): + """When we set allow_no_prev_events=True, should be able to create a + event without any prev_events or auth_events. Expect an exception to be + raised. + """ # Try to create the event with empty prev_events and empty auth_events self.get_failure( self.handler.create_event( self.requester, { "type": EventTypes.Message, - "room_id": room_id, + "room_id": self.room_id, "sender": self.requester.user.to_string(), "content": {"msgtype": "m.text", "body": random_string(5)}, }, prev_event_ids=[], # The event should be rejected when there are no auth_events auth_event_ids=[], + # Allow no prev_events! + allow_no_prev_events=True, ), AssertionError, ) From 85364c50b3aa277de83d6e5850e7bd2228a6634e Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Wed, 1 Dec 2021 17:38:36 -0600 Subject: [PATCH 10/13] Remove extra verbose check > if `len(auth_event_ids) == 0` then `auth_event_ids` will be falsey, so the extra check is redundant. > > https://github.com/matrix-org/synapse/pull/11243/files#discussion_r745985469 --- synapse/handlers/message.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/synapse/handlers/message.py b/synapse/handlers/message.py index 67e256d70518..fdf4526f1242 100644 --- a/synapse/handlers/message.py +++ b/synapse/handlers/message.py @@ -960,7 +960,7 @@ async def create_new_client_event( # We allow events with no `prev_events` but it better have some `auth_events` assert ( builder.type == EventTypes.Create - or len(prev_event_ids) > 0 + or prev_event_ids # Allow an event to have empty list of prev_event_ids # only if it has auth_event_ids. or auth_event_ids @@ -968,7 +968,7 @@ async def create_new_client_event( else: # we now ought to have some prev_events (unless it's a create event). assert ( - builder.type == EventTypes.Create or len(prev_event_ids) > 0 + builder.type == EventTypes.Create or prev_event_ids ), "Attempting to create a non-m.room.create event with no prev_events" event = await builder.build( From 0ed300efac8e839514ce4892bf2981534e829045 Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Fri, 10 Dec 2021 21:05:53 -0600 Subject: [PATCH 11/13] Only worry about having auth_events when allowing no prev_events See https://github.com/matrix-org/synapse/pull/11243#discussion_r766537414 --- synapse/handlers/message.py | 1 - 1 file changed, 1 deletion(-) diff --git a/synapse/handlers/message.py b/synapse/handlers/message.py index fdf4526f1242..c07aedde0e31 100644 --- a/synapse/handlers/message.py +++ b/synapse/handlers/message.py @@ -960,7 +960,6 @@ async def create_new_client_event( # We allow events with no `prev_events` but it better have some `auth_events` assert ( builder.type == EventTypes.Create - or prev_event_ids # Allow an event to have empty list of prev_event_ids # only if it has auth_event_ids. or auth_event_ids From 93ca0f8b8221e537dc4d9947c80f128abe4204ac Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Fri, 10 Dec 2021 21:07:46 -0600 Subject: [PATCH 12/13] Update changelog to describe intended usage See https://github.com/matrix-org/synapse/pull/11243#discussion_r766540153 --- changelog.d/11243.feature | 1 - changelog.d/11243.misc | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 changelog.d/11243.feature create mode 100644 changelog.d/11243.misc diff --git a/changelog.d/11243.feature b/changelog.d/11243.feature deleted file mode 100644 index e1de5a4d3e9c..000000000000 --- a/changelog.d/11243.feature +++ /dev/null @@ -1 +0,0 @@ -Allow events to be created without `prev_events` (only `auth_events`). diff --git a/changelog.d/11243.misc b/changelog.d/11243.misc new file mode 100644 index 000000000000..5ef7fe16d4c9 --- /dev/null +++ b/changelog.d/11243.misc @@ -0,0 +1 @@ +Allow specific, experimental events to be created without `prev_events`. Used by [MSC2716](https://github.com/matrix-org/matrix-doc/pull/2716). From 8fada7e210b70116875f7a48712316120814f780 Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Fri, 10 Dec 2021 21:13:29 -0600 Subject: [PATCH 13/13] Fix lint --- tests/handlers/test_message.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/handlers/test_message.py b/tests/handlers/test_message.py index 328d0e1aaa9a..5816295d8b97 100644 --- a/tests/handlers/test_message.py +++ b/tests/handlers/test_message.py @@ -15,7 +15,6 @@ from typing import Tuple from synapse.api.constants import EventTypes -from synapse.api.room_versions import RoomVersions from synapse.events import EventBase from synapse.events.snapshot import EventContext from synapse.rest import admin