From 96e67a7836913b374644cd09d7569a4f925ea912 Mon Sep 17 00:00:00 2001 From: Till <2353100+S7evinK@users.noreply.github.com> Date: Mon, 23 Oct 2023 15:17:21 +0200 Subject: [PATCH] Fix state resets (#3231) Needs https://github.com/matrix-org/gomatrixserverlib/pull/419 May fix: https://github.com/matrix-org/dendrite/issues/2508, https://github.com/matrix-org/dendrite/issues/1760 --- clientapi/routing/profile.go | 2 +- go.mod | 2 +- go.sum | 4 +- roomserver/roomserver_test.go | 112 +++++++++++++++++++++++++++++++++- 4 files changed, 114 insertions(+), 6 deletions(-) diff --git a/clientapi/routing/profile.go b/clientapi/routing/profile.go index 35da15e0e..5bc68d029 100644 --- a/clientapi/routing/profile.go +++ b/clientapi/routing/profile.go @@ -290,7 +290,7 @@ func updateProfile( }, e } - if err := api.SendEvents(ctx, rsAPI, api.KindNew, events, device.UserDomain(), domain, domain, nil, true); err != nil { + if err := api.SendEvents(ctx, rsAPI, api.KindNew, events, device.UserDomain(), domain, domain, nil, false); err != nil { util.GetLogger(ctx).WithError(err).Error("SendEvents failed") return util.JSONResponse{ Code: http.StatusInternalServerError, diff --git a/go.mod b/go.mod index c246cd043..92492960f 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/lib/pq v1.10.9 github.com/matrix-org/dugong v0.0.0-20210921133753-66e6b1c67e2e github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530 - github.com/matrix-org/gomatrixserverlib v0.0.0-20230628151943-f6e3c7f7b093 + github.com/matrix-org/gomatrixserverlib v0.0.0-20231023121512-16e7431168be github.com/matrix-org/util v0.0.0-20221111132719-399730281e66 github.com/mattn/go-sqlite3 v1.14.17 github.com/nats-io/nats-server/v2 v2.9.19 diff --git a/go.sum b/go.sum index 9fc5b9427..bdcc2e9df 100644 --- a/go.sum +++ b/go.sum @@ -184,8 +184,8 @@ github.com/matrix-org/dugong v0.0.0-20210921133753-66e6b1c67e2e h1:DP5RC0Z3XdyBE github.com/matrix-org/dugong v0.0.0-20210921133753-66e6b1c67e2e/go.mod h1:NgPCr+UavRGH6n5jmdX8DuqFZ4JiCWIJoZiuhTRLSUg= github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530 h1:kHKxCOLcHH8r4Fzarl4+Y3K5hjothkVW5z7T1dUM11U= github.com/matrix-org/gomatrix v0.0.0-20220926102614-ceba4d9f7530/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s= -github.com/matrix-org/gomatrixserverlib v0.0.0-20230628151943-f6e3c7f7b093 h1:FHd3SYhU2ZxZhkssZ/7ms5+M2j+g94lYp8ztvA1E6tA= -github.com/matrix-org/gomatrixserverlib v0.0.0-20230628151943-f6e3c7f7b093/go.mod h1:H9V9N3Uqn1bBJqYJNGK1noqtgJTaCEhtTdcH/mp50uU= +github.com/matrix-org/gomatrixserverlib v0.0.0-20231023121512-16e7431168be h1:bZP16ydP8uRoRBo1p/7WHMexjg7JJGj81fKzZ1FULb4= +github.com/matrix-org/gomatrixserverlib v0.0.0-20231023121512-16e7431168be/go.mod h1:M8m7seOroO5ePlgxA7AFZymnG90Cnh94rYQyngSrZkk= github.com/matrix-org/util v0.0.0-20221111132719-399730281e66 h1:6z4KxomXSIGWqhHcfzExgkH3Z3UkIXry4ibJS4Aqz2Y= github.com/matrix-org/util v0.0.0-20221111132719-399730281e66/go.mod h1:iBI1foelCqA09JJgPV0FYz4qA5dUXYOxMi57FxKBdd4= github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= diff --git a/roomserver/roomserver_test.go b/roomserver/roomserver_test.go index 76b21ad23..cae375653 100644 --- a/roomserver/roomserver_test.go +++ b/roomserver/roomserver_test.go @@ -404,7 +404,9 @@ type fledglingEvent struct { RoomID string Redacts string Depth int64 - PrevEvents []interface{} + PrevEvents []any + AuthEvents []any + Content map[string]any } func mustCreateEvent(t *testing.T, ev fledglingEvent) (result *types.HeaderedEvent) { @@ -421,7 +423,13 @@ func mustCreateEvent(t *testing.T, ev fledglingEvent) (result *types.HeaderedEve Depth: ev.Depth, PrevEvents: ev.PrevEvents, }) - err := eb.SetContent(map[string]interface{}{}) + if ev.Content == nil { + ev.Content = map[string]any{} + } + if ev.AuthEvents != nil { + eb.AuthEvents = ev.AuthEvents + } + err := eb.SetContent(ev.Content) if err != nil { t.Fatalf("mustCreateEvent: failed to marshal event content %v", err) } @@ -1071,3 +1079,103 @@ func TestUpgrade(t *testing.T) { } }) } + +func TestStateReset(t *testing.T) { + alice := test.NewUser(t) + bob := test.NewUser(t) + charlie := test.NewUser(t) + ctx := context.Background() + + test.WithAllDatabases(t, func(t *testing.T, dbType test.DBType) { + // Prepare APIs + cfg, processCtx, close := testrig.CreateConfig(t, dbType) + defer close() + + cm := sqlutil.NewConnectionManager(processCtx, cfg.Global.DatabaseOptions) + natsInstance := jetstream.NATSInstance{} + caches := caching.NewRistrettoCache(128*1024*1024, time.Hour, caching.DisableMetrics) + rsAPI := roomserver.NewInternalAPI(processCtx, cfg, cm, &natsInstance, caches, caching.DisableMetrics) + rsAPI.SetFederationAPI(nil, nil) + + // create a new room + room := test.NewRoom(t, alice, test.RoomPreset(test.PresetPublicChat)) + + // join with Bob and Charlie + bobJoinEv := room.CreateAndInsert(t, bob, spec.MRoomMember, map[string]any{"membership": "join"}, test.WithStateKey(bob.ID)) + charlieJoinEv := room.CreateAndInsert(t, charlie, spec.MRoomMember, map[string]any{"membership": "join"}, test.WithStateKey(charlie.ID)) + + // Send and create the room + if err := api.SendEvents(ctx, rsAPI, api.KindNew, room.Events(), "test", "test", "test", nil, false); err != nil { + t.Errorf("failed to send events: %v", err) + } + + // send a message + bobMsg := room.CreateAndInsert(t, bob, "m.room.message", map[string]any{"body": "hello world"}) + charlieMsg := room.CreateAndInsert(t, charlie, "m.room.message", map[string]any{"body": "hello world"}) + + if err := api.SendEvents(ctx, rsAPI, api.KindNew, []*types.HeaderedEvent{bobMsg, charlieMsg}, "test", "test", "test", nil, false); err != nil { + t.Errorf("failed to send events: %v", err) + } + + // Bob changes his name + expectedDisplayname := "Bob!" + bobDisplayname := room.CreateAndInsert(t, bob, spec.MRoomMember, map[string]any{"membership": "join", "displayname": expectedDisplayname}, test.WithStateKey(bob.ID)) + + if err := api.SendEvents(ctx, rsAPI, api.KindNew, []*types.HeaderedEvent{bobDisplayname}, "test", "test", "test", nil, false); err != nil { + t.Errorf("failed to send events: %v", err) + } + + // Change another state event + jrEv := room.CreateAndInsert(t, alice, spec.MRoomJoinRules, gomatrixserverlib.JoinRuleContent{JoinRule: "invite"}, test.WithStateKey("")) + if err := api.SendEvents(ctx, rsAPI, api.KindNew, []*types.HeaderedEvent{jrEv}, "test", "test", "test", nil, false); err != nil { + t.Errorf("failed to send events: %v", err) + } + + // send a message + bobMsg = room.CreateAndInsert(t, bob, "m.room.message", map[string]any{"body": "hello world"}) + charlieMsg = room.CreateAndInsert(t, charlie, "m.room.message", map[string]any{"body": "hello world"}) + + if err := api.SendEvents(ctx, rsAPI, api.KindNew, []*types.HeaderedEvent{bobMsg, charlieMsg}, "test", "test", "test", nil, false); err != nil { + t.Errorf("failed to send events: %v", err) + } + + // Craft the state reset message, which is using Bobs initial join event and the + // last message Charlie sent as the prev_events. This should trigger the recalculation + // of the "current" state, since the message event does not have state and no missing events in the DB. + stateResetMsg := mustCreateEvent(t, fledglingEvent{ + Type: "m.room.message", + SenderID: charlie.ID, + RoomID: room.ID, + Depth: charlieMsg.Depth() + 1, + PrevEvents: []any{ + bobJoinEv.EventID(), + charlieMsg.EventID(), + }, + AuthEvents: []any{ + room.Events()[0].EventID(), // create event + room.Events()[2].EventID(), // PL event + charlieJoinEv.EventID(), // Charlie join event + }, + }) + + // Send the state reset message + if err := api.SendEvents(ctx, rsAPI, api.KindNew, []*types.HeaderedEvent{stateResetMsg}, "test", "test", "test", nil, false); err != nil { + t.Errorf("failed to send events: %v", err) + } + + // Validate that there is a membership event for Bob + bobMembershipEv := api.GetStateEvent(ctx, rsAPI, room.ID, gomatrixserverlib.StateKeyTuple{ + EventType: spec.MRoomMember, + StateKey: bob.ID, + }) + + if bobMembershipEv == nil { + t.Fatalf("Membership event for Bob does not exist. State reset?") + } else { + // Validate it's the correct membership event + if dn := gjson.GetBytes(bobMembershipEv.Content(), "displayname").Str; dn != expectedDisplayname { + t.Fatalf("Expected displayname to be %q, got %q", expectedDisplayname, dn) + } + } + }) +}