Skip to content

Commit 3c3f7de

Browse files
authored
Allow Complement to create v12 rooms (#840)
* Allow Complement to create v12 rooms Previously we always relied on the HS under test creating v12 rooms, and Complement could only join/send into those rooms. This is a problem if we want to build room versions on top of v12 rooms, as we need Complement to be able to create v12 rooms too. * Remove v12 shims as it's native now * Set unsigned field correctly * Add entropy * Review comments
1 parent 2a797e8 commit 3c3f7de

File tree

3 files changed

+73
-59
lines changed

3 files changed

+73
-59
lines changed

federation/server.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,18 +180,35 @@ func (s *Server) MustMakeRoom(t ct.TestLike, roomVer gomatrixserverlib.RoomVersi
180180
if !s.listening {
181181
ct.Fatalf(s.t, "MustMakeRoom() called before Listen() - this is not supported because Listen() chooses a high-numbered port and thus changes the server name and thus changes the room ID. Ensure you Listen() first!")
182182
}
183+
183184
// Generate a unique room ID, prefixed with an incrementing counter.
184185
// This ensures that room IDs are not re-used across tests, even if a Complement server happens
185186
// to re-use the same port as a previous one, which
186187
// * reduces noise when searching through logs and
187188
// * prevents homeservers from getting confused when multiple test cases re-use the same homeserver deployment.
189+
// This value is temporary for domainless room IDs and will be replaced with the create event ID.
188190
roomID := fmt.Sprintf("!%d-%s:%s", len(s.rooms), util.RandomString(18), s.serverName)
189-
t.Logf("Creating room %s with version %s", roomID, roomVer)
190191
room := NewServerRoom(roomVer, roomID)
191192
for _, opt := range opts {
193+
// let the caller replace the room impl before we try to create events
192194
opt(room)
193195
}
194196

197+
iRoomVer := gomatrixserverlib.MustGetRoomVersion(roomVer)
198+
if iRoomVer.DomainlessRoomIDs() {
199+
if len(events) == 0 || events[0].Type != spec.MRoomCreate {
200+
ct.Fatalf(s.t, "MustMakeRoom: room version %s requires the create event as an initial event but it wasn't found", roomVer)
201+
}
202+
room.RoomID = ""
203+
// build and sign the create event to work out the room ID
204+
createEvent := s.MustCreateEvent(t, room, events[0])
205+
events = events[1:]
206+
room.RoomID = "!" + createEvent.EventID()[1:]
207+
room.AddEvent(createEvent)
208+
}
209+
210+
t.Logf("Creating room %s with version %s", room.RoomID, roomVer)
211+
195212
// sign all these events
196213
for _, ev := range events {
197214
signedEvent := s.MustCreateEvent(t, room, ev)

federation/server_room.go

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/matrix-org/gomatrixserverlib"
1111
"github.com/matrix-org/gomatrixserverlib/fclient"
1212
"github.com/matrix-org/gomatrixserverlib/spec"
13+
"github.com/matrix-org/util"
1314

1415
"github.com/matrix-org/complement/b"
1516
"github.com/matrix-org/complement/ct"
@@ -302,7 +303,7 @@ func (r *ServerRoom) GetEventInTimeline(eventID string) (gomatrixserverlib.PDU,
302303
return nil, false
303304
}
304305

305-
func initialPowerLevelsContent(roomCreator string) (c gomatrixserverlib.PowerLevelContent) {
306+
func initialPowerLevelsContent(ver gomatrixserverlib.IRoomVersion, roomCreator string) (c gomatrixserverlib.PowerLevelContent) {
306307
c.Defaults()
307308
c.Events = map[string]int64{
308309
"m.room.name": 50,
@@ -312,14 +313,18 @@ func initialPowerLevelsContent(roomCreator string) (c gomatrixserverlib.PowerLev
312313
"m.room.avatar": 50,
313314
"m.room.aliases": 0, // anyone can publish aliases by default. Has to be 0 else state_default is used.
314315
}
315-
c.Users = map[string]int64{roomCreator: 100}
316+
if ver.PrivilegedCreators() {
317+
c.Users = map[string]int64{}
318+
} else {
319+
c.Users = map[string]int64{roomCreator: 100}
320+
}
316321
return c
317322
}
318323

319324
// InitialRoomEvents returns the initial set of events that get created when making a room.
320325
func InitialRoomEvents(roomVer gomatrixserverlib.RoomVersion, creator string) []Event {
321326
// need to serialise/deserialise to get map[string]interface{} annoyingly
322-
plContent := initialPowerLevelsContent(creator)
327+
plContent := initialPowerLevelsContent(gomatrixserverlib.MustGetRoomVersion(roomVer), creator)
323328
plBytes, _ := json.Marshal(plContent)
324329
var plContentMap map[string]interface{}
325330
json.Unmarshal(plBytes, &plContentMap)
@@ -331,6 +336,11 @@ func InitialRoomEvents(roomVer gomatrixserverlib.RoomVersion, creator string) []
331336
Content: map[string]interface{}{
332337
"creator": creator,
333338
"room_version": roomVer,
339+
// We have to add randomness to the create event, else if you create 2x v12+ rooms in the same millisecond
340+
// they will get the same room ID, clobbering internal data structures and causing extremely confusing
341+
// behaviour. By adding this entropy, we ensure that even if rooms are created in the same millisecond, their
342+
// hashes will not be the same.
343+
"complement_entropy": util.RandomString(18),
334344
},
335345
},
336346
{
@@ -441,19 +451,25 @@ func (i *ServerRoomImplDefault) ProtoEventCreator(room *ServerRoom, ev Event) (*
441451
PrevEvents: prevEvents,
442452
AuthEvents: ev.AuthEvents,
443453
Redacts: ev.Redacts,
454+
Version: gomatrixserverlib.MustGetRoomVersion(room.Version),
444455
}
445456
if err := proto.SetContent(ev.Content); err != nil {
446457
return nil, fmt.Errorf("EventCreator: failed to marshal event content: %s - %+v", err, ev.Content)
447458
}
448-
if err := proto.SetUnsigned(ev.Content); err != nil {
449-
return nil, fmt.Errorf("EventCreator: failed to marshal event unsigned: %s - %+v", err, ev.Unsigned)
459+
if len(ev.Unsigned) > 0 {
460+
if err := proto.SetUnsigned(ev.Unsigned); err != nil {
461+
return nil, fmt.Errorf("EventCreator: failed to marshal event unsigned: %s - %+v", err, ev.Unsigned)
462+
}
450463
}
451464
if proto.AuthEvents == nil {
452465
var stateNeeded gomatrixserverlib.StateNeeded
453466
stateNeeded, err := gomatrixserverlib.StateNeededForProtoEvent(&proto)
454467
if err != nil {
455468
return nil, fmt.Errorf("EventCreator: failed to work out auth_events : %s", err)
456469
}
470+
if proto.Version.DomainlessRoomIDs() {
471+
stateNeeded.Create = false
472+
}
457473
proto.AuthEvents = room.AuthEvents(stateNeeded)
458474
}
459475
return &proto, nil

tests/v12_test.go

Lines changed: 34 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -29,55 +29,6 @@ var maxCanonicalJSONInt = math.Pow(2, 53) - 1
2929

3030
const roomVersion12 = "12"
3131

32-
var V12ServerRoom = federation.ServerRoomImplCustom{
33-
ProtoEventCreatorFn: Protov12EventCreator,
34-
}
35-
36-
// Override how Complement makes proto events so we can conditionally disable/enable the inclusion of the create event
37-
// depending on whether we're running in combined mode or not.
38-
// Complement also doesn't set the room version correctly on the ProtoEvent as this was a new addition to GMSL.
39-
func Protov12EventCreator(def federation.ServerRoomImpl, room *federation.ServerRoom, ev federation.Event) (*gomatrixserverlib.ProtoEvent, error) {
40-
var prevEvents interface{}
41-
if ev.PrevEvents != nil {
42-
// We deliberately want to set the prev events.
43-
prevEvents = ev.PrevEvents
44-
} else {
45-
// No other prev events were supplied so we'll just
46-
// use the forward extremities of the room, which is
47-
// the usual behaviour.
48-
prevEvents = room.ForwardExtremities
49-
}
50-
proto := gomatrixserverlib.ProtoEvent{
51-
SenderID: ev.Sender,
52-
Depth: int64(room.Depth + 1), // depth starts at 1
53-
Type: ev.Type,
54-
StateKey: ev.StateKey,
55-
RoomID: room.RoomID,
56-
PrevEvents: prevEvents,
57-
AuthEvents: ev.AuthEvents,
58-
Redacts: ev.Redacts,
59-
Version: gomatrixserverlib.MustGetRoomVersion(room.Version),
60-
}
61-
if err := proto.SetContent(ev.Content); err != nil {
62-
return nil, fmt.Errorf("EventCreator: failed to marshal event content: %s - %+v", err, ev.Content)
63-
}
64-
if err := proto.SetUnsigned(ev.Content); err != nil {
65-
return nil, fmt.Errorf("EventCreator: failed to marshal event unsigned: %s - %+v", err, ev.Unsigned)
66-
}
67-
if proto.AuthEvents == nil {
68-
var stateNeeded gomatrixserverlib.StateNeeded
69-
// this does the right thing for v12
70-
stateNeeded, err := gomatrixserverlib.StateNeededForProtoEvent(&proto)
71-
if err != nil {
72-
return nil, fmt.Errorf("EventCreator: failed to work out auth_events : %s", err)
73-
}
74-
// we never include the create event if the HS supports MSC4291
75-
stateNeeded.Create = false
76-
proto.AuthEvents = room.AuthEvents(stateNeeded)
77-
}
78-
return &proto, nil
79-
}
80-
8132
// Test that the creator can kick an admin created both via
8233
// trusted_private_chat and by explicit promotion, including beyond PL100.
8334
// Also checks the creator isn't in the PL event.
@@ -246,7 +197,7 @@ func TestMSC4289PrivilegedRoomCreators(t *testing.T) {
246197
"room_version": roomVersion12,
247198
"preset": "public_chat",
248199
})
249-
room := srv.MustJoinRoom(t, deployment, "hs1", roomID, bob, federation.WithRoomOpts(federation.WithImpl(&V12ServerRoom)))
200+
room := srv.MustJoinRoom(t, deployment, "hs1", roomID, bob)
250201
plEventID := alice.SendEventSynced(t, roomID, b.Event{
251202
Type: spec.MRoomPowerLevels,
252203
StateKey: b.Ptr(""),
@@ -663,6 +614,36 @@ func TestMSC4291RoomIDAsHashOfCreateEvent(t *testing.T) {
663614
assertCreateEventIsRoomID(t, alice, roomID)
664615
}
665616

617+
func TestComplementCanCreateValidV12Rooms(t *testing.T) {
618+
deployment := complement.Deploy(t, 1)
619+
defer deployment.Destroy(t)
620+
alice := deployment.Register(t, "hs1", helpers.RegistrationOpts{})
621+
srv := federation.NewServer(t, deployment,
622+
federation.HandleKeyRequests(),
623+
federation.HandleMakeSendJoinRequests(),
624+
federation.HandleTransactionRequests(nil, nil),
625+
federation.HandleEventRequests(),
626+
)
627+
srv.UnexpectedRequestsAreErrors = false
628+
cancel := srv.Listen()
629+
defer cancel()
630+
bob := srv.UserID("bob")
631+
srvRoom := srv.MustMakeRoom(t, roomVersion12, federation.InitialRoomEvents(roomVersion12, bob))
632+
alice.MustJoinRoom(t, srvRoom.RoomID, []spec.ServerName{srv.ServerName()})
633+
634+
msg := srv.MustCreateEvent(t, srvRoom, federation.Event{
635+
Type: "m.room.message",
636+
Sender: bob,
637+
Content: map[string]interface{}{
638+
"msgtype": "m.text",
639+
"body": "Hello world",
640+
},
641+
})
642+
srvRoom.AddEvent(msg)
643+
srv.MustSendTransaction(t, deployment, "hs1", []json.RawMessage{msg.JSON()}, nil)
644+
alice.MustSyncUntil(t, client.SyncReq{}, client.SyncTimelineHasEventID(srvRoom.RoomID, msg.EventID()))
645+
}
646+
666647
func TestMSC4291RoomIDAsHashOfCreateEvent_AuthEventsOmitsCreateEvent(t *testing.T) {
667648
deployment := complement.Deploy(t, 1)
668649
defer deployment.Destroy(t)
@@ -683,7 +664,7 @@ func TestMSC4291RoomIDAsHashOfCreateEvent_AuthEventsOmitsCreateEvent(t *testing.
683664
defer cancel()
684665
bob := srv.UserID("bob")
685666

686-
room := srv.MustJoinRoom(t, deployment, "hs1", roomID, bob, federation.WithRoomOpts(federation.WithImpl(&V12ServerRoom)))
667+
room := srv.MustJoinRoom(t, deployment, "hs1", roomID, bob)
687668

688669
createEvent := room.CurrentState(spec.MRoomCreate, "")
689670
if createEvent == nil {
@@ -954,7 +935,7 @@ func TestMSC4297StateResolutionV2_1_starts_from_empty_set(t *testing.T) {
954935
"preset": "public_chat",
955936
})
956937
bob.MustJoinRoom(t, roomID, []spec.ServerName{"hs1"})
957-
room := srv.MustJoinRoom(t, deployment, "hs1", roomID, charlie, federation.WithRoomOpts(federation.WithImpl(&V12ServerRoom)))
938+
room := srv.MustJoinRoom(t, deployment, "hs1", roomID, charlie)
958939
joinRulePublic := room.CurrentState(spec.MRoomJoinRules, "")
959940
aliceJoin := room.CurrentState(spec.MRoomMember, alice.UserID)
960941
synchronisationEventID := bob.SendEventSynced(t, room.RoomID, b.Event{
@@ -1150,7 +1131,7 @@ func TestMSC4297StateResolutionV2_1_includes_conflicted_subgraph(t *testing.T) {
11501131
})
11511132
alice.MustJoinRoom(t, roomID, []spec.ServerName{"hs1"})
11521133
bob.MustJoinRoom(t, roomID, []spec.ServerName{"hs1"})
1153-
room := srv.MustJoinRoom(t, deployment, "hs1", roomID, charlie, federation.WithRoomOpts(federation.WithImpl(&V12ServerRoom)))
1134+
room := srv.MustJoinRoom(t, deployment, "hs1", roomID, charlie)
11541135
firstPowerLevelEvent := room.CurrentState(spec.MRoomPowerLevels, "")
11551136
alice.SendEventSynced(t, roomID, b.Event{
11561137
Type: spec.MRoomPowerLevels,

0 commit comments

Comments
 (0)