diff --git a/apps/aecore/lib/aecore/peers/worker.ex b/apps/aecore/lib/aecore/peers/worker.ex index b620e4e6..458d0e92 100644 --- a/apps/aecore/lib/aecore/peers/worker.ex +++ b/apps/aecore/lib/aecore/peers/worker.ex @@ -15,6 +15,7 @@ defmodule Aecore.Peers.Worker do require Logger @mersenne_prime 2147483647 + @max_peers 50 def start_link(_args) do GenServer.start_link(__MODULE__, %{peers: %{}, nonce: :rand.uniform(@mersenne_prime)}, name: __MODULE__) @@ -48,6 +49,11 @@ defmodule Aecore.Peers.Worker do GenServer.call(__MODULE__, :get_peers_nonce) end + @spec add_peers_from_peers() :: :ok + def add_peers_from_peers() do + GenServer.call(__MODULE__, :add_peers_from_peers) + end + @spec genesis_block_header_hash() :: term() def genesis_block_header_hash() do Block.genesis_block().header @@ -72,28 +78,17 @@ defmodule Aecore.Peers.Worker do {:ok, initial_peers} end -def handle_call({:add_peer,uri}, _from, %{peers: peers, nonce: own_nonce} = state) do - case(Client.get_info(uri)) do - {:ok, info} -> - case own_nonce == info.peer_nonce do - false -> - if(info.genesis_block_hash == genesis_block_header_hash()) do - updated_peers = Map.put(peers, uri, info.current_block_hash) - Logger.info(fn -> "Added #{uri} to the peer list" end) - {:reply, :ok, %{state | peers: updated_peers}} - else - Logger.error(fn -> - "Failed to add #{uri}, genesis header hash not valid" end) - {:reply, {:error, "Genesis header hash not valid"}, %{state | peers: peers}} - end - true -> - Logger.debug(fn -> - "Failed to add #{uri}, equal peer nonces" end) - {:reply, {:error, "Equal peer nonces"}, %{state | peers: peers}} + def handle_call({:add_peer, uri}, _from, state) do + case add_peer(uri, state) do + {:ok, state_with_peer} -> + case add_peer_peers(uri, state_with_peer) do + {:ok, state_with_peer_peers} -> + {:reply, :ok, state_with_peer_peers} + {:error, message} -> + {:reply, {:error, message}, state} end - :error -> - Logger.error("GET /info request error") - {:reply, :error, %{state | peers: peers}} + {:error, message} -> + {:reply, {:error, message}, state} end end @@ -142,6 +137,27 @@ def handle_call({:add_peer,uri}, _from, %{peers: peers, nonce: own_nonce} = stat {:reply, state.nonce, state} end + def handle_call(:add_peers_from_peers, _from, %{peers: peers, } = state) do + peers = Enum.reduce(peers, peers, fn({uri, _}, own_peers_acc) -> + case HttpClient.get_peers(uri) do + {:ok, peer_peers} -> + Enum.reduce(peer_peers, own_peers_acc, fn({uri, _}, updated_own_peers_acc) -> + case add_peer(uri, state) do + {:reply, :ok, state} -> + %{peers: updated_state_peers} = state + updated_state_peers + {:reply, :error, _} -> + updated_own_peers_acc + end + end) + :error -> + own_peers_acc + end + end) + + {:reply, peers, %{state | peers: peers}} + end + ## Async operations def handle_cast({:broadcast_to_all, {type, data}}, %{peers: peers} = state) do @@ -164,4 +180,55 @@ def handle_call({:add_peer,uri}, _from, %{peers: peers, nonce: own_nonce} = stat defp prep_data(:new_tx, %{}=data), do: Serialization.tx(data, :serialize) defp prep_data(:new_block, %{}=data), do: Serialization.block(data, :serialize) + defp add_peer(uri, state) do + %{peers: peers, nonce: own_nonce} = state + if(peers |> Map.keys() |> Enum.count() < @max_peers) do + case(Client.get_info(uri)) do + {:ok, info} -> + case own_nonce == info.peer_nonce do + false -> + if(info.genesis_block_hash == genesis_block_header_hash()) do + updated_peers = Map.put(peers, uri, info.current_block_hash) + Logger.info(fn -> "Added #{uri} to the peer list" end) + {:ok, %{state | peers: updated_peers}} + else + Logger.error(fn -> + "Failed to add #{uri}, genesis header hash not valid" end) + {:error, "Genesis header hash not valid"} + end + true -> + Logger.debug(fn -> + "Failed to add #{uri}, equal peer nonces" end) + {:error, "Equal peer nonces"} + end + :error -> + Logger.error("GET /info request error") + {:error, "GET /info request error"} + end + else + Logger.info("Max peers count reached") + {:error, "Max peers count reached"} + end + end + + defp add_peer_peers(uri, state) do + %{peers: peers, } = state + case HttpClient.get_peers(uri) do + {:ok, peer_peers} -> + updated_peers = Enum.reduce(peer_peers, peers, fn({uri, _}, acc) -> + case add_peer(uri, %{state | peers: acc}) do + {:ok, state} -> + %{peers: updated_state_peers} = state + updated_state_peers + {:error, _} -> + acc + end + end) + + {:ok, %{state | peers: updated_peers}} + :error -> + {:error, "GET /peers request error"} + end + end + end diff --git a/apps/aecore/test/aecore_peers_test.exs b/apps/aecore/test/aecore_peers_test.exs index 41d8a5e7..3353afb5 100644 --- a/apps/aecore/test/aecore_peers_test.exs +++ b/apps/aecore/test/aecore_peers_test.exs @@ -10,7 +10,7 @@ defmodule AecorePeersTest do end test "add peer, get all peers, check peers and remove the peer" do - assert :error = Peers.add_peer("localhost:4000") + assert {:error, _} = Peers.add_peer("localhost:4000") assert Enum.count(Peers.all_peers) == 0 assert :ok = Peers.check_peers assert {:error, "Peer not found"} =