aboutsummaryrefslogtreecommitdiff
path: root/shard/lib/app/chat.ex
diff options
context:
space:
mode:
Diffstat (limited to 'shard/lib/app/chat.ex')
-rw-r--r--shard/lib/app/chat.ex77
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