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