defmodule Shard.Identity do use Agent require Salty.Sign.Ed25519, as: Sign require Logger @identity_db [Application.get_env(:shard, :data_path), "identity_db"] |> Path.join |> String.to_atom def start_link(_) do Agent.start_link(__MODULE__, :init, [], name: __MODULE__) end def init() do :dets.start {:ok, @identity_db} = :dets.open_file @identity_db, type: :set case :dets.match @identity_db, :"$1" do [] -> Logger.info "Generating keypair..." {pk, sk} = gen_keypair(Application.get_env(:shard, :peer_id_suffix)) nick_suffix = pk |> binary_part(0, 3) |> Base.encode16 |> String.downcase nick = "Anon" <> nick_suffix :dets.insert @identity_db, {pk, sk, nick} %{ keypair: {pk, sk}, nickname: nick } [[{pk, sk, nick}] | _] -> %{ keypair: {pk, sk}, nickname: nick } end end defp gen_keypair(suffix, n \\ 0) do {:ok, pk, sk} = Sign.keypair if rem(n, 10000) == 0 do Logger.info "#{n}... expected #{:math.pow(256, byte_size(suffix))}" end if :binary.longest_common_suffix([pk, suffix]) == byte_size(suffix) do {pk, sk} else gen_keypair(suffix, n+1) end end def get_keypair() do Agent.get(__MODULE__, &(&1.keypair)) end def get_nickname() do Agent.get(__MODULE__, &(&1.nickname)) end def set_nickname(newnick) do Agent.update(__MODULE__, fn state -> {pk, sk} = state.keypair :dets.insert @identity_db, {pk, sk, newnick} %{state | nickname: newnick} end) end end