Repository: matrix-org/synapse

Issue:
Missing `content` for edited encrypted event
According to @BillCarsonFr (https://github.com/vector-im/element-web/issues/23550#issuecomment-1286667837):

1) Send a message **In a room with some history => the intitial sync has to be `limited=true`for that room**
2) Edit it once
3) Do a clear cache (initial sync)

The original event is then returned with with empty content, as if it's been redacted:

```json
{
  "type": "m.room.encrypted",
  "sender": "@alice:localhost:8480",
  "content": {},
  "origin_server_ts": 1666342304414,
  "unsigned": {
    "age": 500276,
    "transaction_id": "m1666342304366.6",
    "m.relations": {
      "m.replace": {
        "event_id": "$toxW3soIUzZbG6cmnw94p0-cvQ5_AlIM0NJ7SJz7vpY",
        "origin_server_ts": 1666342320779,
        "sender": "@alice:localhost:8480"
      }
    }
  },
  "event_id": "$q6rQxsGaW0XscWvlnTUiWVIAcV07AMNZqZzPF3Sbga4"
}
```


Discussion:
On web, the edited message is shown in the timeline with a strange `Re-request encryption keys`.
<img width="376" alt="image" src="https://user-images.githubusercontent.com/9841565/197172134-e2719f65-e416-4da4-947f-1d5c82f397bd.png">

On android the message edit is not even shown, just marked as unable to decrypt (you can see the edit history by using the context menu though)
<img width="190" alt="image" src="https://user-images.githubusercontent.com/9841565/197172171-f45646ba-44de-4c3a-ada4-88d9ca648fd9.png">

on iOS the message is displayed but for some reason all the history is in fail to decrypt?
<img width="448" alt="image" src="https://user-images.githubusercontent.com/9841565/197173089-6c3a3d5c-06f5-4b98-a13b-55b60c588638.png">


So, the problem here stems from an ambiguity in the spec.

https://spec.matrix.org/v1.4/client-server-api/#editing-encrypted-events says:

> [for encrypted events], `m.new_content` is placed in the content of the encrypted payload.

(An alternative would have been to put the encrypted payload inside `m.new_content`, rather than the other way around, but the spec follows existing implementations (see https://github.com/matrix-org/matrix-spec-proposals/pull/2676#discussion_r827240084)).

We then have https://spec.matrix.org/v1.4/client-server-api/#server-side-replacement-of-content, which says:

> Whenever an `m.replace` is to be bundled with an event as above, the server should also modify the content of the original event according to the `m.new_content` of the most recent replacement event.

Of course, the server doesn't have access to `m.new_content`. It turns out that, in this case, Synapse replaces the content with `{}`.

This behaviour changed in https://github.com/matrix-org/synapse/pull/14034, which updated Synapse to follow the spec in applying edits to encrypted events.
I've opened https://github.com/matrix-org/matrix-spec/issues/1299 to track this on the spec side.
Looks like web can be affected in some cases
![image](https://user-images.githubusercontent.com/9841565/197489021-dec91e42-a600-4744-bb0e-a928f0f6c8e3.png)

Original event (displayed in UTD in the screen shot)
```
{
  "content": {},
  ...
  "type": "m.room.encrypted",
  "unsigned": {
    "age": 177157836,
    "m.relations": {
      "m.replace": {
        "event_id": "$cbrzjtKuImUkE_QC3zRmfvoLFRpbyqmt9u8ZGGzUoLU",
        "origin_server_ts": 1666367697945,
        "sender": "@valere35:matrix.org"
      }
    }
  },
  "event_id": "$Oy6--KpT9fG2hIkk1toG-BabYwB5a5DeO8uN66g3gJQ",
  "user_id": "@valere35:matrix.org",
  "age": 177157836
}
```

And the actual edit event (visible because of dev mode) is decrypted correctly (eventID is `$cbrzjtKuImUkE_QC3zRmfvoLFRpbyqmt9u8ZGGzUoLU` and decrypted type is `m.room.message`)

Maybe the edit was received before the original event (back pagination / gappy sync?)

https://github.com/matrix-org/element-web-rageshakes/issues/16338

It's strange both message and edit were sent from this device (few seconds away). But somehow later I got back the message from server (probably a back pagination this morning following gappy sync?). Then the original message has been replaced with the one with empty content? and that did break the edit chain??


Resolving patch:
diff --git a/synapse/storage/databases/main/relations.py b/synapse/storage/databases/main/relations.py
--- a/synapse/storage/databases/main/relations.py
+++ b/synapse/storage/databases/main/relations.py
@@ -484,11 +484,12 @@ async def get_applicable_edits(
             the event will map to None.
         """
 
-        # We only allow edits for events that have the same sender and event type.
-        # We can't assert these things during regular event auth so we have to do
-        # the checks post hoc.
+        # We only allow edits for `m.room.message` events that have the same sender
+        # and event type. We can't assert these things during regular event auth so
+        # we have to do the checks post hoc.
 
-        # Fetches latest edit that has the same type and sender as the original.
+        # Fetches latest edit that has the same type and sender as the
+        # original, and is an `m.room.message`.
         if isinstance(self.database_engine, PostgresEngine):
             # The `DISTINCT ON` clause will pick the *first* row it encounters,
             # so ordering by origin server ts + event ID desc will ensure we get
@@ -504,6 +505,7 @@ async def get_applicable_edits(
                 WHERE
                     %s
                     AND relation_type = ?
+                    AND edit.type = 'm.room.message'
                 ORDER by original.event_id DESC, edit.origin_server_ts DESC, edit.event_id DESC
             """
         else:
@@ -522,6 +524,7 @@ async def get_applicable_edits(
                 WHERE
                     %s
                     AND relation_type = ?
+                    AND edit.type = 'm.room.message'
                 ORDER by edit.origin_server_ts, edit.event_id
             """
 


Test patch: