Packages
elixium_core
0.3.14
0.6.3
0.6.2
0.6.1
0.6.0
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.12
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.19
0.3.18
0.3.17
0.3.16
0.3.15
0.3.14
0.3.13
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.1
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.4
0.1.3
The core package for the Elixium blockchain, containing all the modules needed to run the chain
Current section
Files
Jump to
Current section
Files
lib/p2p/node/supervisor.ex
defmodule Elixium.Node.Supervisor do
alias Elixium.Store.Oracle
use Supervisor
require Logger
@default_port 31013
def start_link, do: start_link(self(), @default_port)
def start_link([router_pid]) when is_pid(router_pid), do: start_link(router_pid, @default_port)
def start_link([port]) when is_number(port), do: start_link(self(), port)
def start_link([nil]), do: start_link(self(), @default_port)
def start_link([router_pid, nil]), do: start_link(router_pid, @default_port)
def start_link([router_pid, port]), do: start_link(router_pid, port)
def start_link(router_pid, port) do
Supervisor.start_link(__MODULE__, [router_pid, port], name: __MODULE__)
end
def init([router_pid, port]) do
Oracle.start_link(Elixium.Store.Peer)
:pg2.create(:p2p_handlers)
socket = open_socket(port)
# Fetch known peers. We're going to try to connect to them
# before setting up a listener
peers = find_potential_peers(port)
handlers = generate_handlers(socket, port, router_pid, peers)
children = handlers ++ [Elixium.HostAvailability.Supervisor]
Supervisor.init(children, strategy: :one_for_one)
end
defp generate_handlers(socket, port, router_pid, peers) do
for i <- 1..10 do
%{
id: :"ConnectionHandler#{i}",
start: {
Elixium.Node.ConnectionHandler,
:start_link,
[socket, router_pid, peers, i]
},
type: :worker,
restart: :permanent
}
end
end
defp open_socket(port) do
options = [:binary, reuseaddr: true, active: false]
case :gen_tcp.listen(port, options) do
{:ok, socket} ->
Logger.info("Opened listener socket on port #{port}.")
socket
_ -> Logger.warn("Listen socket not started, something went wrong.")
end
end
# Either loads peers from a local storage or connects to the
# bootstrapping registry
@spec find_potential_peers(integer) :: List | :not_found
defp find_potential_peers(port) do
case Oracle.inquire(:"Elixir.Elixium.Store.PeerOracle", {:load_known_peers, []}) do
:not_found -> fetch_peers_from_registry(port)
peers -> peers
end
end
# Connects to the bootstrapping peer registry and returns a list of
# previously connected peers.
@spec fetch_peers_from_registry(integer) :: List
def fetch_peers_from_registry(port) do
url = Application.get_env(:elixium_core, :registry_url)
case :httpc.request(url ++ '/' ++ Integer.to_charlist(port)) do
{:ok, {{'HTTP/1.1', 200, 'OK'}, _headers, body}} ->
peers =
body
|> Jason.decode!()
|> Enum.map(&peerstring_to_tuple/1)
|> Enum.filter(fn {_, port} -> port != nil end)
if peers == [], do: :not_found, else: peers
{:error, _} -> :not_found
end
end
@doc """
Given a peer supervisor, return a list of all the
handlers that are currently connected to another peer
"""
@spec connected_handlers :: List
def connected_handlers do
:p2p_handlers
|> :pg2.get_members()
|> Enum.filter(fn p ->
p
|> Process.info()
|> Keyword.get(:dictionary)
|> Keyword.has_key?(:connected)
end)
end
@doc """
Broadcast a message to all peers
"""
@spec gossip(String.t(), map) :: none
def gossip(type, message) do
Enum.each(connected_handlers(), &(send(&1, {type, message})))
end
# Converts from a colon delimited string to a tuple containing the
# ip and port. "127.0.0.1:3000" becomes {'127.0.0.1', 3000}
defp peerstring_to_tuple(peer) do
[ip, port] = String.split(peer, ":")
ip = String.to_charlist(ip)
port =
case Integer.parse(port) do
{port, _} -> port
:error -> nil
end
{ip, port}
end
end