diff options
Diffstat (limited to 'shard/lib/app/chat.ex')
-rw-r--r-- | shard/lib/app/chat.ex | 77 |
1 files changed, 52 insertions, 25 deletions
diff --git a/shard/lib/app/chat.ex b/shard/lib/app/chat.ex index db2cb64..e61d648 100644 --- a/shard/lib/app/chat.ex +++ b/shard/lib/app/chat.ex @@ -28,7 +28,7 @@ defmodule SApp.Chat do defimpl Shard.Manifest, for: Manifest do def start(m) do - SApp.Chat.start_link(m.channel) + DynamicSupervisor.start_child(Shard.DynamicSupervisor, {SApp.Chat, m.channel}) end end @@ -69,6 +69,12 @@ defmodule SApp.Chat do end end + def find_proc(chan) do + manifest = %Manifest{channel: chan} + id = SData.term_hash manifest + Shard.Manager.find_proc id + end + @doc """ Implementation of the :manifest call that returns the chat room's manifest """ @@ -98,10 +104,11 @@ defmodule SApp.Chat do to send a message to the chat room. Puts the message in the store and syncs with all connected peers. """ - def handle_cast({:chat_send, msg}, state) do - msgitem = {(System.os_time :seconds), - Shard.Identity.get_nickname(), - msg} + def handle_cast({:chat_send, pk, msg}, state) do + msgbin = SData.term_bin {(System.os_time :seconds), msg} + {:ok, sign} = Shard.Keys.sign_detached(pk, msgbin) + msgitem = {pk, msgbin, sign} + prev_root = state.mst.root mst = MST.insert(state.mst, msgitem) state = %{state | mst: mst} @@ -157,21 +164,26 @@ defmodule SApp.Chat do state else # Try adding the message - if prev_root == state.mst.root do - mst2 = MST.insert(state.mst, msgitem) - if mst2.root == new_root do - # This was the only message missing, we are happy! - state = %{state | mst: mst2} - Shard.Manager.save_state(state.id, mst2.root) - GenServer.cast(state.page_store, {:set_roots, [mst2.root]}) - msg_callback(state, msgitem) - state + {pk, bin, sign} = msgitem + if Shard.Keys.verify(pk, bin, sign) == :ok do + if prev_root == state.mst.root do + mst2 = MST.insert(state.mst, msgitem) + if mst2.root == new_root do + # This was the only message missing, we are happy! + state = %{state | mst: mst2} + Shard.Manager.save_state(state.id, mst2.root) + GenServer.cast(state.page_store, {:set_roots, [mst2.root]}) + msg_callback(state, msgitem) + state + else + Logger.warn("Invalid new root after inserting same message item!") + state + end else - # More messages are missing, start a full merge init_merge(state, new_root, peer_id) end else - init_merge(state, new_root, peer_id) + state end end {:root, new_root} -> @@ -190,21 +202,33 @@ defmodule SApp.Chat do defp init_merge(state, new_root, source_peer) do # TODO: make the merge asynchronous + + Logger.info("Starting merge for #{inspect state.manifest}, merging root: #{new_root|>Base.encode16}") + prev_last = for {x, true} <- MST.last(state.mst, nil, 100), into: MapSet.new, do: x mgmst = %{state.mst | root: new_root} mgmst = put_in(mgmst.store.prefer_ask, [source_peer]) mst = MST.merge(state.mst, mgmst) - for {x, true} <- MST.last(mst, nil, 100) do + correct = for {x, true} <- MST.last(mst, nil, 100) do if not MapSet.member? prev_last, x do msg_callback(state, x) + {pk, bin, sign} = x + Shard.Keys.verify(pk, bin, sign) + else + true end end - GenServer.cast(state.page_store, {:set_roots, [mst.root]}) - Shard.Manager.save_state(state.id, mst.root) - %{state | mst: mst} + if Enum.all? correct do + GenServer.cast(state.page_store, {:set_roots, [mst.root]}) + Shard.Manager.save_state(state.id, mst.root) + %{state | mst: mst} + else + Logger.warn("Incorrect signatures somewhere while merging, dropping merged data") + state + end end def handle_info({:DOWN, _ref, :process, pid, _reason}, state) do @@ -212,20 +236,23 @@ defmodule SApp.Chat do {:noreply, %{ state | subs: new_subs }} end - defp msg_callback(state, {ts, nick, msg}) do + defp msg_callback(state, {pk, msgbin, _sign}) do + {ts, msg} = SData.term_unbin msgbin for pid <- state.subs do if Process.alive?(pid) do - send(pid, {:chat_recv, state.channel, {ts, nick, msg}}) + send(pid, {:chat_recv, state.channel, {ts, pk, msg}}) end end end - defp msg_cmp({ts1, nick1, msg1}, {ts2, nick2, msg2}) do + defp msg_cmp({pk1, msgbin1, _sign1}, {pk2, msgbin2, _sign2}) do + {ts1, msg1} = SData.term_unbin msgbin1 + {ts2, msg2} = SData.term_unbin msgbin2 cond do ts1 > ts2 -> :after ts1 < ts2 -> :before - nick1 > nick2 -> :after - nick1 < nick2 -> :before + pk1 > pk2 -> :after + pk1 < pk2 -> :before msg1 > msg2 -> :after msg1 < msg2 -> :before true -> :duplicate |