|
|
@ -617,11 +617,137 @@ is at the top)::
|
|
|
|
Suppose E3 and E4 are both ``m.room.name`` events which set the name of the
|
|
|
|
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?
|
|
|
|
room. What should the name of the room be at E5?
|
|
|
|
|
|
|
|
|
|
|
|
Servers should follow the following recursively-defined algorithm to determine
|
|
|
|
Servers should follow one of the following recursively-defined algorithms,
|
|
|
|
the room state at a given point on the DAG.
|
|
|
|
depending on the room version, to determine the room state at a given point on
|
|
|
|
|
|
|
|
the DAG.
|
|
|
|
|
|
|
|
|
|
|
|
State resolution algorithm
|
|
|
|
State resolution algorithm for version 2 rooms
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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_1), S'(E_2), … \}` consisting of the states after each of
|
|
|
|
|
|
|
|
:math:`E`'s ``prev_event``\s :math:`\{ E_1, E_2, … \}`, where the resolution of
|
|
|
|
|
|
|
|
a set of states is given in the algorithm below.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Definitions
|
|
|
|
|
|
|
|
+++++++++++
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The state resolution algorithm for version 2 rooms uses the following
|
|
|
|
|
|
|
|
definitions, given the set of room states :math:`\{ S_1, S_2, \ldots \}`:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Power events
|
|
|
|
|
|
|
|
A *power event* is a state event with type ``m.room.power_levels`` or
|
|
|
|
|
|
|
|
``m.room.join_rules``, or a state event with type ``m.room.member`` where the
|
|
|
|
|
|
|
|
``membership`` is ``leave`` or ``ban`` and the ``sender`` does not match the
|
|
|
|
|
|
|
|
``state_key``. The idea behind this is that power events are events that have
|
|
|
|
|
|
|
|
may remove someone's ability to do something in the room.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Unconflicted state map and conflicted state set
|
|
|
|
|
|
|
|
The *unconflicted state map* is the state where the value of each key exists
|
|
|
|
|
|
|
|
and is the same in each state :math:`S_i`. The *conflicted state set* is the
|
|
|
|
|
|
|
|
set of all other state events. Note that the unconflicted state map only has
|
|
|
|
|
|
|
|
one event per ``(event_type, state_key)``, whereas the conflicted state set
|
|
|
|
|
|
|
|
may have multiple events.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Auth difference
|
|
|
|
|
|
|
|
The *auth difference* is calculated by first calculating the full auth chain
|
|
|
|
|
|
|
|
for each state :math:`S_i`, that is the union of the auth chains for each
|
|
|
|
|
|
|
|
event in :math:`S_i`, and then taking every event that doesn't appear in
|
|
|
|
|
|
|
|
every auth chain. If :math:`C_i` is the full auth chain of :math:`S_i`, then
|
|
|
|
|
|
|
|
the auth difference is :math:`\cup C_i - \cap C_i`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Full conflicted set
|
|
|
|
|
|
|
|
The *full conflicted set* is the union of the conflicted state set and the
|
|
|
|
|
|
|
|
auth difference.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Reverse topological power ordering
|
|
|
|
|
|
|
|
The *reverse topological power ordering* of a set of events is the
|
|
|
|
|
|
|
|
lexicographically smallest topological ordering based on the DAG formed by
|
|
|
|
|
|
|
|
auth events. The reverse topological power ordering is ordered from earliest
|
|
|
|
|
|
|
|
event to latest. For comparing two topological orderings to determine which
|
|
|
|
|
|
|
|
is the lexicographically smallest, the following comparison relation on
|
|
|
|
|
|
|
|
events is used: for events :math:`x` and :math:`y`, :math:`x<y` if
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1. :math:`x`'s sender has *greater* power level than :math:`y`'s sender,
|
|
|
|
|
|
|
|
when looking at their respective ``auth_event``\s; or
|
|
|
|
|
|
|
|
2. the senders have the same power level, but :math:`x`'s
|
|
|
|
|
|
|
|
``origin_server_ts`` is *less* than :math:`y`'s ``origin_server_ts``; or
|
|
|
|
|
|
|
|
3. the senders have the same power level and the events have the same
|
|
|
|
|
|
|
|
``origin_server_ts``, but :math:`x`'s ``event_id`` is *less* than
|
|
|
|
|
|
|
|
:math:`y`'s ``event_id``.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The reverse topological power ordering can be found by sorting the events
|
|
|
|
|
|
|
|
using Kahn's algorithm for topological sorting, and at each step selecting,
|
|
|
|
|
|
|
|
among all the candidate vertices, the smallest vertex using the above
|
|
|
|
|
|
|
|
comparison relation.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Mainline ordering
|
|
|
|
|
|
|
|
Given an ``m.room.power_levels`` event :math:`P`, the *mainline of* :math:`P`
|
|
|
|
|
|
|
|
is the list of events generated by starting with :math:`P` and recursively
|
|
|
|
|
|
|
|
taking the ``m.room.power_levels`` events from the ``auth_events``, ordered
|
|
|
|
|
|
|
|
such that :math:`P` is last. Given another event :math:`e`, the *closest
|
|
|
|
|
|
|
|
mainline event to* :math:`e` is the first event encountered in the mainline
|
|
|
|
|
|
|
|
when iteratively descending through the ``m.room.power_levels`` events in the
|
|
|
|
|
|
|
|
``auth_events`` starting at :math:`e`. If no mainline event is encountered
|
|
|
|
|
|
|
|
when iteratively descending through the ``m.room.power_levels`` events, then
|
|
|
|
|
|
|
|
the closest mainline event to :math:`e` can be considered to be a dummy event
|
|
|
|
|
|
|
|
that is before any other event in the mainline of :math:`P` for the purposes
|
|
|
|
|
|
|
|
of condition 1 below.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The *mainline ordering based on* :math:`P` of a set of events is the
|
|
|
|
|
|
|
|
ordering, from smallest to largest, using the following comparision relation
|
|
|
|
|
|
|
|
on events: for events :math:`x` and :math:`y`, :math:`x<y` if
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1. the closest mainline event to :math:`x` appears *before* the closest
|
|
|
|
|
|
|
|
mainline event to :math:`y`; or
|
|
|
|
|
|
|
|
2. the closest mainline events are the same, but :math:`x`\'s
|
|
|
|
|
|
|
|
``origin_server_ts`` is *less* than :math:`y`\'s ``origin_server_ts``; or
|
|
|
|
|
|
|
|
3. the closest mainline events are the same and the events have the same
|
|
|
|
|
|
|
|
``origin_server_ts``, but :math:`x`\'s ``event_id`` is *less* than
|
|
|
|
|
|
|
|
:math:`y`\'s ``event_id``.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Iterative auth checks
|
|
|
|
|
|
|
|
The *iterative auth checks algorithm* takes as input an initial room state
|
|
|
|
|
|
|
|
and a sorted list of state events, and constructs a new room state by
|
|
|
|
|
|
|
|
iterating through the event list and applying the state event to the room
|
|
|
|
|
|
|
|
state if the state event is allowed by the `authorization rules`_. If the
|
|
|
|
|
|
|
|
state event is not allowed by the authorization rules, then the event is
|
|
|
|
|
|
|
|
ignored. If a ``(event_type, state_key)`` key that is required for checking
|
|
|
|
|
|
|
|
the authorization rules is not present in the state, then the appropriate
|
|
|
|
|
|
|
|
state event from the event's ``auth_events`` is used.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Algorithm
|
|
|
|
|
|
|
|
+++++++++
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The *resolution* of a set of states is obtained as follows:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1. Take all *power events* and any events in their auth chains, recursively,
|
|
|
|
|
|
|
|
that appear in the *full conflicted set* and order them by the *reverse
|
|
|
|
|
|
|
|
topological power ordering*.
|
|
|
|
|
|
|
|
2. Apply the *iterative auth checks algorithm* on the *unconflicted state map*
|
|
|
|
|
|
|
|
and the list of events from the previous step to get a partially resolved
|
|
|
|
|
|
|
|
state.
|
|
|
|
|
|
|
|
3. Take all remaining events that weren't picked in step 1 and order them by
|
|
|
|
|
|
|
|
the mainline ordering based on the power level in the partially resolved
|
|
|
|
|
|
|
|
state obtained in step 2.
|
|
|
|
|
|
|
|
4. Apply the *iterative auth checks algorithm* on the partial resolved
|
|
|
|
|
|
|
|
state and the list of events from the previous step.
|
|
|
|
|
|
|
|
5. Update the result by replacing any event with the event with the same key
|
|
|
|
|
|
|
|
from the *unconflicted state map*, if such an event exists, to get the final
|
|
|
|
|
|
|
|
resolved state.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
State resolution algorithm for version 1 rooms
|
|
|
|
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
|
|
|
|
|
|
|
|
.. WARNING::
|
|
|
|
.. WARNING::
|
|
|
|
This section documents the state resolution algorithm as implemented by
|
|
|
|
This section documents the state resolution algorithm as implemented by
|
|
|
|