1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
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
|