Document the state resolution algorithm
or at least, my understanding of it.
This commit is contained in:
parent
3b82b2ce09
commit
0960229a80
2 changed files with 87 additions and 91 deletions
|
@ -4,25 +4,6 @@ Federation
|
||||||
.. contents:: Table of Contents
|
.. contents:: Table of Contents
|
||||||
|
|
||||||
|
|
||||||
Auth events
|
|
||||||
~~~~~~~~~~~
|
|
||||||
|
|
||||||
The auth events of an event are the set of events used by the authorization
|
|
||||||
algorithm to accept the event. These should be a subset of the current state.
|
|
||||||
|
|
||||||
A server is required to store the complete chain of auth events for all events
|
|
||||||
it serves to remote servers.
|
|
||||||
|
|
||||||
All auth events have type:
|
|
||||||
|
|
||||||
- ``m.room.create``
|
|
||||||
- ``m.room.power_levels``
|
|
||||||
- ``m.room.member``
|
|
||||||
|
|
||||||
.. todo
|
|
||||||
We probably should probably give a lower band of how long auth events
|
|
||||||
should be kept around for.
|
|
||||||
|
|
||||||
Auth chain
|
Auth chain
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -66,51 +47,6 @@ that the other is correct; i.e. we always accept that the other side is correct
|
||||||
unless we can prove otherwise.
|
unless we can prove otherwise.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
State Resolution
|
|
||||||
----------------
|
|
||||||
|
|
||||||
**TODO**
|
|
||||||
|
|
||||||
When two branches in the event graph merge, the state of those branches might
|
|
||||||
differ, so a *state resolution* algorithm must be used to determine the current
|
|
||||||
state of the resultant merge.
|
|
||||||
|
|
||||||
The properties of the state resolution algorithm are:
|
|
||||||
|
|
||||||
- Must only depend on the event graph, and not local server state.
|
|
||||||
- When two state events are comparable, the descendant one should be picked.
|
|
||||||
- Must not require the full event graph.
|
|
||||||
|
|
||||||
The following algorithm satisfies these requirements; given two or more events,
|
|
||||||
pick the one with the greatest:
|
|
||||||
|
|
||||||
#. Depth.
|
|
||||||
#. Hash of event_id.
|
|
||||||
|
|
||||||
|
|
||||||
This works except in the case of auth events, where we need to mitigate against
|
|
||||||
the attack where servers artificially netsplit to avoid bans or power level
|
|
||||||
changes.
|
|
||||||
|
|
||||||
We want the following rules to apply:
|
|
||||||
|
|
||||||
#. If power levels have been changed on two different branches use the rules
|
|
||||||
above, ensuring that the one picked is a valid change from the one not picked.
|
|
||||||
#. Similarly handle membership changes (e.g. bans, kicks, etc.)
|
|
||||||
#. Any state merged must be allowed by the newly merged auth events. If none of
|
|
||||||
the candidate events for a given state are allowed, we pick the last event
|
|
||||||
given by the ordering above (i.e. we pick one with the least depth).
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
State Conflict Resolution
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
If a server discovers that it disagrees with another about the current state,
|
|
||||||
it can follow the same process outlined in *Auth chain resolution* to resolve
|
|
||||||
these conflicts.
|
|
||||||
|
|
||||||
Constructing a new event
|
Constructing a new event
|
||||||
------------------------
|
------------------------
|
||||||
|
|
||||||
|
@ -315,4 +251,3 @@ Example event:
|
||||||
"age": 500
|
"age": 500
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -446,6 +446,7 @@ following subset of the room state:
|
||||||
|
|
||||||
- The ``m.room.create`` event.
|
- The ``m.room.create`` event.
|
||||||
- The current ``m.room.power_levels`` event, if any.
|
- The current ``m.room.power_levels`` event, if any.
|
||||||
|
- The current ``m.room.join_rules`` event, if any.
|
||||||
- The sender's current ``m.room.member`` event, if any.
|
- The sender's current ``m.room.member`` event, if any.
|
||||||
|
|
||||||
Authorization of PDUs
|
Authorization of PDUs
|
||||||
|
@ -473,8 +474,6 @@ Target User
|
||||||
For an ``m.room.member`` state event, the user given by the ``state_key`` of
|
For an ``m.room.member`` state event, the user given by the ``state_key`` of
|
||||||
the event.
|
the event.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Rules
|
Rules
|
||||||
+++++
|
+++++
|
||||||
|
|
||||||
|
@ -615,6 +614,9 @@ the state of the room.
|
||||||
EDUs
|
EDUs
|
||||||
----
|
----
|
||||||
|
|
||||||
|
.. WARNING::
|
||||||
|
This section may be misleading or inaccurate.
|
||||||
|
|
||||||
EDUs, by comparison to PDUs, do not have an ID, a room ID, or a list of
|
EDUs, by comparison to PDUs, do not have an ID, a room ID, or a list of
|
||||||
"previous" IDs. The only mandatory fields for these are the type, origin and
|
"previous" IDs. The only mandatory fields for these are the type, origin and
|
||||||
destination homeserver names, and the actual nested content.
|
destination homeserver names, and the actual nested content.
|
||||||
|
@ -635,6 +637,89 @@ destination homeserver names, and the actual nested content.
|
||||||
"content":{...}
|
"content":{...}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Room State Resolution
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
The *state* of a room is a map of ``(event_type, state_key)`` to
|
||||||
|
``event_id``. Each room starts with an empty state, and each state event which
|
||||||
|
is accepted into the room updates the state of that room.
|
||||||
|
|
||||||
|
Where each event has a single ``prev_event``, it is clear what the state of the
|
||||||
|
room after each event should be. However, when two branches in the event graph
|
||||||
|
merge, the state of those branches might differ, so a *state resolution*
|
||||||
|
algorithm must be used to determine the resultant state.
|
||||||
|
|
||||||
|
For example, consider the following event graph (where the oldest event, E0,
|
||||||
|
is at the top)::
|
||||||
|
|
||||||
|
E0
|
||||||
|
|
|
||||||
|
E1
|
||||||
|
/ \
|
||||||
|
E2 E4
|
||||||
|
| |
|
||||||
|
E3 |
|
||||||
|
\ /
|
||||||
|
E5
|
||||||
|
|
||||||
|
|
||||||
|
Suppose E3 and E4 are both ``m.room.name`` events which set the name of the
|
||||||
|
room. What should the name of the room be at E5?
|
||||||
|
|
||||||
|
Servers should follow the following recursively-defined algorithm to determine
|
||||||
|
the room state at a given point on the DAG.
|
||||||
|
|
||||||
|
State resolution algorithm
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The room state :math:`S'(E)` after an event :math:`E` is defined in terms of
|
||||||
|
the room state :math:`S(E)` before :math:`E`, and depends on whether
|
||||||
|
:math:`E` is a state event or a message event:
|
||||||
|
|
||||||
|
* If :math:`E` is a message event, then :math:`S'(E) = S(E)`.
|
||||||
|
|
||||||
|
* If :math:`E` is a state event, then :math:`S'(E)` is :math:`S(E)`, except
|
||||||
|
that its entry corresponding to :math:`E`'s ``event_type`` and ``state_key``
|
||||||
|
is replaced by :math:`E`'s ``event_id``.
|
||||||
|
|
||||||
|
The room state :math:`S(E)` before :math:`E` is the *resolution* of the set of
|
||||||
|
states :math:`\{ S'(E'), S'(E''), … \}` consisting of the states after each of
|
||||||
|
:math:`E`'s ``prev_event``\s :math:`\{ E', E'', … \}`.
|
||||||
|
|
||||||
|
The *resolution* of a set of states is defined as follows. The resolved state
|
||||||
|
is built up in a number of passes; here we use :math:`R` to refer to the
|
||||||
|
results of the resolution so far.
|
||||||
|
|
||||||
|
* Start by setting :math:`R` to the union of the states to be resolved,
|
||||||
|
excluding any *conflicting* events.
|
||||||
|
|
||||||
|
* First we resolve conflicts between ``m.room.power_levels`` events. If there
|
||||||
|
is no conflict, this step is skipped, otherwise:
|
||||||
|
|
||||||
|
* Assemble all the ``m.room.power_levels`` events from the states to
|
||||||
|
be resolved into a list.
|
||||||
|
|
||||||
|
* Sort the list by ascending ``depth`` then descending ``sha1(event_id)``.
|
||||||
|
|
||||||
|
* Add the first event in the list to :math:`R`.
|
||||||
|
|
||||||
|
* For each subsequent event in the list, check that the event would be
|
||||||
|
allowed by the Authorization Rules for a room in state :math:`R`. If the
|
||||||
|
event would be allowed, then update :math:`R` with the event and continue
|
||||||
|
with the next event in the list. If it would not be allowed, stop and
|
||||||
|
continue below with ``m.room.join_rules`` events.
|
||||||
|
|
||||||
|
* Repeat the above process for conflicts between ``m.room.join_rules`` events.
|
||||||
|
|
||||||
|
* Repeat the above process for conflicts between ``m.room.member`` events.
|
||||||
|
|
||||||
|
* No other events affect the authorization rules, so for all other conflicts,
|
||||||
|
just pick the event with the highest depth and lowest ``sha1(event_id)`` that
|
||||||
|
passes authentication in :math:`R` and add it to :math:`R`.
|
||||||
|
|
||||||
|
A *conflict* occurs between states where those states have different
|
||||||
|
``event_ids`` for the same ``(state_type, state_key)``. The events thus
|
||||||
|
affected are said to be *conflicting* events.
|
||||||
|
|
||||||
Protocol URLs
|
Protocol URLs
|
||||||
-------------
|
-------------
|
||||||
|
@ -1150,30 +1235,6 @@ A homeserver may provide a TLS client certificate and the receiving homeserver
|
||||||
may check that the client certificate matches the certificate of the origin
|
may check that the client certificate matches the certificate of the origin
|
||||||
homeserver.
|
homeserver.
|
||||||
|
|
||||||
Server-Server Authorization
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
.. TODO-doc
|
|
||||||
- PDU signing (see the Event signing section earlier)
|
|
||||||
- State conflict resolution (see below)
|
|
||||||
|
|
||||||
State Conflict Resolution
|
|
||||||
-------------------------
|
|
||||||
.. NOTE::
|
|
||||||
This section is a work in progress.
|
|
||||||
|
|
||||||
.. TODO-doc
|
|
||||||
- How do conflicts arise (diagrams?)
|
|
||||||
- How are they resolved (incl tie breaks)
|
|
||||||
- How does this work with deleting current state
|
|
||||||
- How do we reject invalid federation traffic?
|
|
||||||
|
|
||||||
[[TODO(paul): At this point we should probably have a long description of how
|
|
||||||
State management works, with descriptions of clobbering rules, power levels, etc
|
|
||||||
etc... But some of that detail is rather up-in-the-air, on the whiteboard, and
|
|
||||||
so on. This part needs refining. And writing in its own document as the details
|
|
||||||
relate to the server/system as a whole, not specifically to server-server
|
|
||||||
federation.]]
|
|
||||||
|
|
||||||
Presence
|
Presence
|
||||||
--------
|
--------
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue