diff options
Diffstat (limited to 'shard/lib/app/identity.ex')
-rw-r--r-- | shard/lib/app/identity.ex | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/shard/lib/app/identity.ex b/shard/lib/app/identity.ex new file mode 100644 index 0000000..7e97897 --- /dev/null +++ b/shard/lib/app/identity.ex @@ -0,0 +1,113 @@ +defmodule SApp.Identity do + use GenServer + + require Logger + + defmodule Manifest do + defstruct [:pk] + end + + defmodule State do + defstruct [:info, :rev, :signed] + end + + defimpl Shard.Manifest, for: Manifest do + def start(m) do + DynamicSupervisor.start_child(Shard.DynamicSupervisor, {SApp.Identity, m.pk}) + end + end + + def start_link(pk) do + GenServer.start_link(__MODULE__, pk) + end + + def init(pk) do + manifest = %Manifest{pk: pk} + id = SData.term_hash manifest + + case Shard.Manager.register(id, manifest, self()) do + :ok -> + Shard.Manager.dispatch_to(id, nil, self()) + state = case Shard.Manager.load_state(id) do + nil -> + info = %{nick: default_nick(pk)} + SData.SignRev.new info + st -> + st + end + GenServer.cast(self(), :init_pull) + {:ok, %{pk: pk, id: id, state: state}} + :redundant -> + exit(:redundant) + end + end + + def default_nick(pk) do + nick_suffix = pk + |> binary_part(0, 4) + |> Base.encode16 + |> String.downcase + "Anon" <> nick_suffix + end + + def find_proc(pk) do + manifest = %Manifest{pk: pk} + id = SData.term_hash manifest + Shard.Manager.find_proc id + end + + def handle_call(:manifest, _from, state) do + {:replyl, state.manifest, state} + end + + def handle_call(:get_info, _from, state) do + {:reply, SData.SignRev.get(state.state), state} + end + + def handle_call({:set_info, new_info}, _from, state) do + case SData.SignRev.set(state.state, new_info, state.pk) do + {:ok, st2} -> + Shard.Manager.save_state(state.id, st2) + state = put_in(state.state, st2) + bcast_state(state) + {:reply, :ok, state} + err -> + {:reply, err, state} + end + end + + def handle_cast(:init_pull, state) do + for {_, pid, _, _} <- Shard.Manager.list_peers do + GenServer.cast(pid, {:send_msg, {:interested, [state.id]}}) + end + {:noreply, state} + end + + def handle_cast({:interested, peer_id}, state) do + Shard.Manager.send(peer_id, {state.id, nil, {:update, SData.SignRev.signed(state.state)}}) + end + + def handle_cast({:msg, peer_id, _shard_id, nil, msg}, state) do + state = case msg do + {:update, signed} -> + case SData.SignRev.merge(state.state, signed, state.pk) do + {true, st2} -> + Shard.Manager.save_state(state.id, st2) + state = put_in(state.state, st2) + bcast_state(state, [peer_id]) + state + {false, _} -> + state + end + end + {:noreply, state} + end + + def bcast_state(state, exclude \\ []) do + for peer_id <- Shard.Manager.get_shard_peers(state.id) do + if not Enum.member? exclude, peer_id do + Shard.Manager.send(peer_id, {state.id, nil, {:update, SData.SignRev.signed(state.state)}}) + end + end + end +end |