From 8a5bba72fb46cde13fe28e971e47398556db63af Mon Sep 17 00:00:00 2001 From: Pim Kunis Date: Sun, 25 Jul 2021 14:57:52 +0200 Subject: [PATCH] Add forward extremities to rooms --- lib/matrix_server/event.ex | 16 +++++++ lib/matrix_server/room.ex | 23 +++++++++- lib/matrix_server/room_server.ex | 24 ++++++++--- lib/matrix_server/state_resolution.ex | 43 ++++++------------- ...21639_add_forward_extremities_to_rooms.exs | 9 ++++ 5 files changed, 78 insertions(+), 37 deletions(-) create mode 100644 priv/repo/migrations/20210725121639_add_forward_extremities_to_rooms.exs diff --git a/lib/matrix_server/event.ex b/lib/matrix_server/event.ex index 6eaca67..d59e8a9 100644 --- a/lib/matrix_server/event.ex +++ b/lib/matrix_server/event.ex @@ -106,4 +106,20 @@ defmodule MatrixServer.Event do def generate_event_id do "$" <> MatrixServer.random_string(17) <> ":" <> MatrixServer.server_name() end + + def is_control_event(%Event{type: "m.room.power_levels", state_key: ""}), do: true + def is_control_event(%Event{type: "m.room.join_rules", state_key: ""}), do: true + + def is_control_event(%Event{ + type: "m.room.member", + state_key: state_key, + sender: sender, + content: %{membership: membership} + }) + when sender != state_key and membership in ["leave", "ban"], + do: true + + def is_control_event(_), do: false + + def is_state_event(%Event{state_key: state_key}), do: state_key != nil end diff --git a/lib/matrix_server/room.ex b/lib/matrix_server/room.ex index a277344..767abba 100644 --- a/lib/matrix_server/room.ex +++ b/lib/matrix_server/room.ex @@ -2,13 +2,15 @@ defmodule MatrixServer.Room do use Ecto.Schema import Ecto.Changeset + import Ecto.Query - alias MatrixServer.{Room, Event} + alias MatrixServer.{Repo, Room, Event} alias MatrixServerWeb.API.CreateRoom @primary_key {:id, :string, []} schema "rooms" do field :visibility, Ecto.Enum, values: [:public, :private] + field :forward_extremities, {:array, :string} has_many :events, Event, foreign_key: :event_id end @@ -27,4 +29,23 @@ defmodule MatrixServer.Room do def generate_room_id do "!" <> MatrixServer.random_string(18) <> "@" <> MatrixServer.server_name() end + + def update_forward_extremities(%Event{ + event_id: event_id, + prev_events: prev_event_ids, + room_id: room_id + }) do + %Room{forward_extremities: forward_extremities} = + Room + |> where([r], r.id == ^room_id) + |> Repo.one!() + + new_forward_extremities = [event_id | forward_extremities -- prev_event_ids] + + {_, [room | _]} = + from(r in Room, where: r.id == ^room_id, select: r) + |> Repo.update_all(set: [forward_extremities: new_forward_extremities]) + + room + end end diff --git a/lib/matrix_server/room_server.ex b/lib/matrix_server/room_server.ex index 37ad9a5..1a1c811 100644 --- a/lib/matrix_server/room_server.ex +++ b/lib/matrix_server/room_server.ex @@ -1,6 +1,8 @@ defmodule MatrixServer.RoomServer do use GenServer + import Ecto.Query + alias MatrixServer.{Repo, Room, Event, Account, StateResolution} alias MatrixServerWeb.API.CreateRoom @@ -45,13 +47,12 @@ defmodule MatrixServer.RoomServer do ) do create_room_event = Event.create_room(room_id, MatrixServer.get_mxid(localpart), room_version) - verify_event(create_room_event) - |> IO.inspect() + verify_and_insert_event(create_room_event) {:ok, %{}} end - defp verify_event(event) do + defp verify_and_insert_event(event) do # Check the following things: # 1. TODO: Is a valid event, otherwise it is dropped. # 2. TODO: Passes signature checks, otherwise it is dropped. @@ -61,9 +62,22 @@ defmodule MatrixServer.RoomServer do # 6. TODO: Passes authorization rules based on the current state of the room, otherwise it is "soft failed". if StateResolution.is_authorized_by_auth_events(event) do state_set = StateResolution.resolve(event, false) - StateResolution.is_authorized(event, state_set) + + if StateResolution.is_authorized(event, state_set) do + # TODO: Assume the event is a forward extremity, should check this actually. + Room.update_forward_extremities(event) + {:ok, event} = Repo.insert(event) + {:ok, state_set} + else + {:error, :rejected} + end else - false + {:error, :rejected} end end + + def testing do + account = Repo.one!(from a in Account, limit: 1) + create_room(%CreateRoom{}, account) + end end diff --git a/lib/matrix_server/state_resolution.ex b/lib/matrix_server/state_resolution.ex index 1cc9851..094cf16 100644 --- a/lib/matrix_server/state_resolution.ex +++ b/lib/matrix_server/state_resolution.ex @@ -3,15 +3,6 @@ defmodule MatrixServer.StateResolution do alias MatrixServer.{Repo, Event} - def example do - %Event{content: content} = event = Event.power_levels("room1", "charlie") - event = %Event{event | content: %{content | "ban" => 0}} - - event - |> Map.put(:prev_events, ["b", "fork"]) - |> Map.put(:auth_events, ["create", "join_charlie", "b"]) - end - def resolve(%Event{room_id: room_id} = event, apply_state) do room_events = Event @@ -34,8 +25,8 @@ defmodule MatrixServer.StateResolution do |> Enum.map(&resolve(&1, room_events, apply_state)) resolved_state = do_resolve(state_sets, room_events) - # TODO: check if state event - if apply_state do + + if apply_state and Event.is_state_event(event) do Map.put(resolved_state, {type, state_key}, event) else resolved_state @@ -55,13 +46,12 @@ defmodule MatrixServer.StateResolution do end def do_resolve(state_sets, room_events, unconflicted_state_map, conflicted_state_set) do - # TODO: make the state set a hashmap instead of a set. full_conflicted_set = MapSet.union(conflicted_state_set, auth_difference(state_sets, room_events)) conflicted_control_event_ids = full_conflicted_set - |> Enum.filter(&is_control_event(&1, room_events)) + |> Enum.filter(&Event.is_control_event(room_events[&1])) |> MapSet.new() conflicted_control_events_with_auth_ids = @@ -125,7 +115,6 @@ defmodule MatrixServer.StateResolution do end def auth_difference(state_sets, room_events) do - # TODO: memoization possible full_auth_chains = Enum.map(state_sets, fn state_set -> state_set @@ -146,7 +135,6 @@ defmodule MatrixServer.StateResolution do end def auth_chain(%Event{auth_events: auth_events}, room_events) do - # TODO: handle when auth event is not found. auth_events |> Enum.map(&room_events[&1]) |> Enum.reduce(MapSet.new(), fn %Event{event_id: auth_event_id} = auth_event, acc -> @@ -157,22 +145,6 @@ defmodule MatrixServer.StateResolution do end) end - def is_control_event(event_id, room_events), do: is_control_event(room_events[event_id]) - - def is_control_event(%Event{type: "m.room.power_levels", state_key: ""}), do: true - def is_control_event(%Event{type: "m.room.join_rules", state_key: ""}), do: true - - def is_control_event(%Event{ - type: "m.room.member", - state_key: state_key, - sender: sender, - content: %{membership: membership} - }) - when sender != state_key and membership in ["leave", "ban"], - do: true - - def is_control_event(_), do: false - def rev_top_pow_order(room_events) do fn %Event{origin_server_ts: timestamp1, event_id: event_id1} = event1, %Event{origin_server_ts: timestamp2, event_id: event_id2} = event2 -> @@ -349,4 +321,13 @@ defmodule MatrixServer.StateResolution do is_authorized(event, state_set) end + + def testing do + %Event{content: content} = event = Event.power_levels("room1", "charlie") + event = %Event{event | content: %{content | "ban" => 0}} + + event + |> Map.put(:prev_events, ["b", "fork"]) + |> Map.put(:auth_events, ["create", "join_charlie", "b"]) + end end diff --git a/priv/repo/migrations/20210725121639_add_forward_extremities_to_rooms.exs b/priv/repo/migrations/20210725121639_add_forward_extremities_to_rooms.exs new file mode 100644 index 0000000..ae1faa2 --- /dev/null +++ b/priv/repo/migrations/20210725121639_add_forward_extremities_to_rooms.exs @@ -0,0 +1,9 @@ +defmodule MatrixServer.Repo.Migrations.AddForwardExtremitiesToRooms do + use Ecto.Migration + + def change do + alter table(:rooms) do + add :forward_extremities, {:array, :string}, default: [], null: false + end + end +end