From 18f2c6aa57f5ea5ef01be90b34b493bf6994200a Mon Sep 17 00:00:00 2001 From: gabrielbosio Date: Thu, 19 May 2022 19:12:04 -0300 Subject: [PATCH 01/13] Add DNS data retrieval Add elixir-dns dependency. The function returns the data as it is, we still need to make the function return the URL that geth will use. I suppose that we can make that URL using the data that the DNS returns. --- eth_client/lib/eth_client/nodes_list.ex | 22 ++++++++++++++++++++++ eth_client/mix.exs | 3 ++- eth_client/mix.lock | 2 ++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 eth_client/lib/eth_client/nodes_list.ex diff --git a/eth_client/lib/eth_client/nodes_list.ex b/eth_client/lib/eth_client/nodes_list.ex new file mode 100644 index 0000000..ac56774 --- /dev/null +++ b/eth_client/lib/eth_client/nodes_list.ex @@ -0,0 +1,22 @@ +defmodule EthClient.NodesList do + alias DNS.Record + alias DNS.Resource + + @type network :: :mainnet | :ropsten | :rinkeby | :goerli + + @spec get_by_dns(network()) :: Record.t() + def get_by_dns(network) do + %Record{anlist: [%Resource{data: [enrtree_data]}]} = + network + |> get_domain_name() + |> DNS.query(:txt) + + enrtree_data + end + + @spec get_domain_name(network()) :: String.t() + defp get_domain_name(:mainnet), do: "all.mainnet.ethdisco.net" + defp get_domain_name(:ropsten), do: "all.ropsten.ethdisco.net" + defp get_domain_name(:rinkeby), do: "all.rinkeby.ethdisco.net" + defp get_domain_name(:goerli), do: "all.goerli.ethdisco.net" +end diff --git a/eth_client/mix.exs b/eth_client/mix.exs index f460b7c..ebfd861 100644 --- a/eth_client/mix.exs +++ b/eth_client/mix.exs @@ -31,7 +31,8 @@ defmodule EthClient.MixProject do {:rustler, "~> 0.25.0"}, {:ex_rlp, "~> 0.5.4"}, {:dialyxir, "~> 1.1", only: [:dev, :test], runtime: false}, - {:excoveralls, "~> 0.14.5", only: [:test], runtime: false} + {:excoveralls, "~> 0.14.5", only: [:test], runtime: false}, + {:dns, "~> 2.3.0"} ] end end diff --git a/eth_client/mix.lock b/eth_client/mix.lock index 8a1256f..67348ab 100644 --- a/eth_client/mix.lock +++ b/eth_client/mix.lock @@ -3,6 +3,7 @@ "certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"}, "credo": {:hex, :credo, "1.6.4", "ddd474afb6e8c240313f3a7b0d025cc3213f0d171879429bf8535d7021d9ad78", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "c28f910b61e1ff829bffa056ef7293a8db50e87f2c57a9b5c3f57eee124536b7"}, "dialyxir": {:hex, :dialyxir, "1.1.0", "c5aab0d6e71e5522e77beff7ba9e08f8e02bad90dfbeffae60eaf0cb47e29488", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "07ea8e49c45f15264ebe6d5b93799d4dd56a44036cf42d0ad9c960bc266c0b9a"}, + "dns": {:hex, :dns, "2.3.0", "3e750cf3e229898628a091fa7dca29524294db2962d0fc15696ac3a5cfff2315", [:mix], [{:socket, "~> 0.3.13", [hex: :socket, repo: "hexpm", optional: false]}], "hexpm", "f3f1074409b4ecf6f93379a49fb0aef30a898f3093ea22b5c64a85e9f99ca6f8"}, "erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"}, "ex_abi": {:hex, :ex_abi, "0.5.11", "a53307cf796231bf068a9941d57fbcb8654e72042c2a113a49f08dfb248875fb", [:mix], [{:ex_keccak, "~> 0.4.0", [hex: :ex_keccak, repo: "hexpm", optional: false]}, {:jason, "~> 1.3", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "e128577740bdc0f05ed6841cbb1c88bb80377613970ed870fa052b780870655a"}, "ex_keccak": {:hex, :ex_keccak, "0.4.0", "4eb8620c8a20a546e2d297b5ce3de150a90db0fdc4ba1dd88c854ace9ee47603", [:mix], [{:rustler, "~> 0.24", [hex: :rustler, repo: "hexpm", optional: false]}], "hexpm", "209ec5591d3cf79f9bdcdedf39c3d7a1fb208321e2b6de2660801117ae386f10"}, @@ -17,6 +18,7 @@ "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, "parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"}, "rustler": {:hex, :rustler, "0.25.0", "32526b51af7e58a740f61941bf923486ce6415a91c3934cc16c281aa201a2240", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:toml, "~> 0.6", [hex: :toml, repo: "hexpm", optional: false]}], "hexpm", "6b43a11a37fe79c6234d88c4102ab5dfede7a6a764dc5c7b539956cfa02f3cf4"}, + "socket": {:hex, :socket, "0.3.13", "98a2ab20ce17f95fb512c5cadddba32b57273e0d2dba2d2e5f976c5969d0c632", [:mix], [], "hexpm", "f82ea9833ef49dde272e6568ab8aac657a636acb4cf44a7de8a935acb8957c2e"}, "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"}, "tesla": {:hex, :tesla, "1.4.4", "bb89aa0c9745190930366f6a2ac612cdf2d0e4d7fff449861baa7875afd797b2", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.3", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "d5503a49f9dec1b287567ea8712d085947e247cb11b06bc54adb05bfde466457"}, "toml": {:hex, :toml, "0.6.2", "38f445df384a17e5d382befe30e3489112a48d3ba4c459e543f748c2f25dd4d1", [:mix], [], "hexpm", "d013e45126d74c0c26a38d31f5e8e9b83ea19fc752470feb9a86071ca5a672fa"}, From f1b0c9ca18b73390c279ea22b1aaa98bc4065707 Mon Sep 17 00:00:00 2001 From: gabrielbosio Date: Thu, 19 May 2022 19:19:45 -0300 Subject: [PATCH 02/13] Add moduledoc --- eth_client/lib/eth_client/nodes_list.ex | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/eth_client/lib/eth_client/nodes_list.ex b/eth_client/lib/eth_client/nodes_list.ex index ac56774..70536ee 100644 --- a/eth_client/lib/eth_client/nodes_list.ex +++ b/eth_client/lib/eth_client/nodes_list.ex @@ -1,4 +1,9 @@ defmodule EthClient.NodesList do + @moduledoc """ + Retrieve a nodes list for a specific network. Right now, only supports getting a list + by DNS. + """ + alias DNS.Record alias DNS.Resource From e344352ceae124de2810de911b159aa9c8c146b1 Mon Sep 17 00:00:00 2001 From: gabrielbosio Date: Fri, 20 May 2022 18:15:04 -0300 Subject: [PATCH 03/13] Add nodes tree search Add search so that it returns all the nodes from a network. Mainnet is taking too long. I will try to refactor the module to make it a bit faster. For example, by removing the list concatenation at line 64. --- eth_client/lib/eth_client/nodes_list.ex | 62 ++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/eth_client/lib/eth_client/nodes_list.ex b/eth_client/lib/eth_client/nodes_list.ex index 70536ee..990d33a 100644 --- a/eth_client/lib/eth_client/nodes_list.ex +++ b/eth_client/lib/eth_client/nodes_list.ex @@ -4,19 +4,67 @@ defmodule EthClient.NodesList do by DNS. """ - alias DNS.Record - alias DNS.Resource - @type network :: :mainnet | :ropsten | :rinkeby | :goerli - @spec get_by_dns(network()) :: Record.t() + @enr_prefix "enr:" + @enrtree_branch_prefix "enrtree-branch:" + + @spec get_by_dns(network()) :: {:ok, String.t()} | {:error, term()} def get_by_dns(network) do - %Record{anlist: [%Resource{data: [enrtree_data]}]} = + with {:ok, enr_root} <- get_root(network) do + get_children(network, enr_root) + end + end + + @spec get_root(network()) :: {:ok, String.t()} | {:error, term()} + defp get_root(network) do + {:ok, response_splitted} = network |> get_domain_name() - |> DNS.query(:txt) + |> DNS.resolve(:txt) + + response = Enum.join(response_splitted) + + case Regex.run(~r/^enrtree-root:v1 e=([\w\d]+) .*$/, response) do + [^response, enr_root] -> {:ok, enr_root} + nil -> {:error, "enr_root not found in DNS response: #{response}"} + end + end + + @spec get_children(network(), String.t()) :: {:ok, [String.t()]} | {:error, term()} + defp get_children(network, branch) do + branch_domain_name = branch <> "." <> get_domain_name(network) + {:ok, response_splitted} = DNS.resolve(branch_domain_name, :txt) + response = Enum.join(response_splitted) + + cond do + String.starts_with?(response, @enr_prefix) -> + @enr_prefix <> node = response + {:ok, [node]} + + String.starts_with?(response, @enrtree_branch_prefix) -> + @enrtree_branch_prefix <> branches = response + branches_splitted = String.split(branches, ",") + get_children_branches(network, branches_splitted, []) + + true -> + {:error, + "Neither #{@enr_prefix} nor #{@enrtree_branch_prefix} is in DNS response: #{response}"} + end + end + + @spec get_children_branches(network(), [String.t()], [String.t()]) :: + {:ok, [String.t()]} | {:error, term()} + defp get_children_branches(_network, [], nodes), do: {:ok, nodes} + + defp get_children_branches(network, branches, nodes) do + [branch | rest] = branches - enrtree_data + with {:ok, node} <- get_children(network, branch) do + get_children_branches(network, rest, node ++ nodes) + else + {:error, _} = error -> error + end end @spec get_domain_name(network()) :: String.t() From 95452da49354b42e38084caf9cd5896e7baf17ce Mon Sep 17 00:00:00 2001 From: gabrielbosio Date: Fri, 20 May 2022 18:19:43 -0300 Subject: [PATCH 04/13] Replace one-clause 'with' with case --- eth_client/lib/eth_client/nodes_list.ex | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/eth_client/lib/eth_client/nodes_list.ex b/eth_client/lib/eth_client/nodes_list.ex index 990d33a..c27b6b3 100644 --- a/eth_client/lib/eth_client/nodes_list.ex +++ b/eth_client/lib/eth_client/nodes_list.ex @@ -60,10 +60,12 @@ defmodule EthClient.NodesList do defp get_children_branches(network, branches, nodes) do [branch | rest] = branches - with {:ok, node} <- get_children(network, branch) do - get_children_branches(network, rest, node ++ nodes) - else - {:error, _} = error -> error + case get_children(network, branch) do + {:ok, node} -> + get_children_branches(network, rest, node ++ nodes) + + {:error, _} = error -> + error end end From 56c5323a8a92f1bc2e2c514dfae6a3f8f1b850d2 Mon Sep 17 00:00:00 2001 From: gabrielbosio Date: Tue, 24 May 2022 12:38:12 -0300 Subject: [PATCH 05/13] Paralellize tree search --- eth_client/lib/eth_client/application.ex | 3 +- eth_client/lib/eth_client/nodes_list.ex | 86 +++++++-------------- eth_client/lib/eth_client/nodes_list/dns.ex | 75 ++++++++++++++++++ eth_client/lib/eth_client/nodes_list_old.ex | 78 +++++++++++++++++++ 4 files changed, 185 insertions(+), 57 deletions(-) create mode 100644 eth_client/lib/eth_client/nodes_list/dns.ex create mode 100644 eth_client/lib/eth_client/nodes_list_old.ex diff --git a/eth_client/lib/eth_client/application.ex b/eth_client/lib/eth_client/application.ex index b0f7108..f41b2fa 100644 --- a/eth_client/lib/eth_client/application.ex +++ b/eth_client/lib/eth_client/application.ex @@ -22,7 +22,8 @@ defmodule EthClient.Application do } children = [ - {EthClient.Context, initial_context} + {EthClient.Context, initial_context}, + {EthClient.NodesList, EthClient.NodesList.DNS.Storage} ] # See https://hexdocs.pm/elixir/Supervisor.html diff --git a/eth_client/lib/eth_client/nodes_list.ex b/eth_client/lib/eth_client/nodes_list.ex index c27b6b3..3f1ff06 100644 --- a/eth_client/lib/eth_client/nodes_list.ex +++ b/eth_client/lib/eth_client/nodes_list.ex @@ -3,75 +3,49 @@ defmodule EthClient.NodesList do Retrieve a nodes list for a specific network. Right now, only supports getting a list by DNS. """ + use GenServer - @type network :: :mainnet | :ropsten | :rinkeby | :goerli + alias EthClient.NodesList.DNS, as: NodesListDNS - @enr_prefix "enr:" - @enrtree_branch_prefix "enrtree-branch:" + @type network :: :mainnet | :ropsten | :rinkeby | :goerli - @spec get_by_dns(network()) :: {:ok, String.t()} | {:error, term()} - def get_by_dns(network) do - with {:ok, enr_root} <- get_root(network) do - get_children(network, enr_root) - end + def start_link(storage_name) do + GenServer.start_link(__MODULE__, storage_name, name: __MODULE__) end - @spec get_root(network()) :: {:ok, String.t()} | {:error, term()} - defp get_root(network) do - {:ok, response_splitted} = - network - |> get_domain_name() - |> DNS.resolve(:txt) - - response = Enum.join(response_splitted) - - case Regex.run(~r/^enrtree-root:v1 e=([\w\d]+) .*$/, response) do - [^response, enr_root] -> {:ok, enr_root} - nil -> {:error, "enr_root not found in DNS response: #{response}"} - end + @spec update_using_dns(network()) :: :ok | {:error, term()} + def update_using_dns(network) do + GenServer.call(__MODULE__, {:update_using_dns, network}) end - @spec get_children(network(), String.t()) :: {:ok, [String.t()]} | {:error, term()} - defp get_children(network, branch) do - branch_domain_name = branch <> "." <> get_domain_name(network) - {:ok, response_splitted} = DNS.resolve(branch_domain_name, :txt) - response = Enum.join(response_splitted) - - cond do - String.starts_with?(response, @enr_prefix) -> - @enr_prefix <> node = response - {:ok, [node]} + def get(network) do + GenServer.call(__MODULE__, {:get, network}) + end - String.starts_with?(response, @enrtree_branch_prefix) -> - @enrtree_branch_prefix <> branches = response - branches_splitted = String.split(branches, ",") - get_children_branches(network, branches_splitted, []) + ## Server callbacks - true -> - {:error, - "Neither #{@enr_prefix} nor #{@enrtree_branch_prefix} is in DNS response: #{response}"} - end + @impl true + def init(storage_name) do + nodes = :ets.new(storage_name, [:named_table, :public, read_concurrency: true]) + dns_supervisor_name = NodesListDNS.supervisor_name() + Task.Supervisor.start_link(name: dns_supervisor_name) + {:ok, nodes} end - @spec get_children_branches(network(), [String.t()], [String.t()]) :: - {:ok, [String.t()]} | {:error, term()} - defp get_children_branches(_network, [], nodes), do: {:ok, nodes} + @impl true + def handle_call({:update_using_dns, network}, _from, nodes) do + result = + with {:ok, enr_root} <- NodesListDNS.get_root(network) do + NodesListDNS.search_for_nodes(network, nodes, enr_root) + end - defp get_children_branches(network, branches, nodes) do - [branch | rest] = branches + {:reply, result, nodes} + end - case get_children(network, branch) do - {:ok, node} -> - get_children_branches(network, rest, node ++ nodes) + @impl true + def handle_call({:get, network}, _from, nodes) do + result = NodesListDNS.get_nodes(network, nodes) - {:error, _} = error -> - error - end + {:reply, result, nodes} end - - @spec get_domain_name(network()) :: String.t() - defp get_domain_name(:mainnet), do: "all.mainnet.ethdisco.net" - defp get_domain_name(:ropsten), do: "all.ropsten.ethdisco.net" - defp get_domain_name(:rinkeby), do: "all.rinkeby.ethdisco.net" - defp get_domain_name(:goerli), do: "all.goerli.ethdisco.net" end diff --git a/eth_client/lib/eth_client/nodes_list/dns.ex b/eth_client/lib/eth_client/nodes_list/dns.ex new file mode 100644 index 0000000..4c899b6 --- /dev/null +++ b/eth_client/lib/eth_client/nodes_list/dns.ex @@ -0,0 +1,75 @@ +defmodule EthClient.NodesList.DNS do + alias EthClient.NodesList + + @enr_prefix "enr:" + @enrtree_branch_prefix "enrtree-branch:" + + def search_for_nodes(network, nodes, enr_root) do + supervisor_name() + |> Task.Supervisor.start_child(fn -> + get_children(network, nodes, enr_root) + end) + end + + def supervisor_name, do: EthClient.NodesList.DNS.Supervisor + + @spec get_root(NodesList.network()) :: {:ok, String.t()} | {:error, term()} + def get_root(network) do + {:ok, response_split} = + network + |> get_domain_name() + |> DNS.resolve(:txt) + + response = Enum.join(response_split) + + case Regex.run(~r/^enrtree-root:v1 e=([\w\d]+) .*$/, response) do + [^response, enr_root] -> {:ok, enr_root} + nil -> {:error, "enr_root not found in DNS response: #{response}"} + end + end + + def get_nodes(network, nodes), do: :ets.lookup(nodes, network) + + @spec get_children(NodesList.network(), atom(), String.t()) :: :ok | {:error, term()} + defp get_children(network, nodes, branch) do + branch_domain_name = branch <> "." <> get_domain_name(network) + {:ok, response_split} = DNS.resolve(branch_domain_name, :txt) + response = Enum.join(response_split) + parse_child(network, nodes, response) + end + + @spec parse_child(NodesList.network(), atom(), String.t()) :: :ok | {:error, term()} + defp parse_child(network, nodes, @enr_prefix <> new_node) do + network_nodes = + case get_nodes(network, nodes) do + [{_, result}] -> result + [] -> [] + end + + :ets.insert(nodes, {network, [new_node | network_nodes]}) + end + + defp parse_child(network, nodes, @enrtree_branch_prefix <> branches) do + branches_split = String.split(branches, ",") + get_children_branches(network, nodes, branches_split) + end + + defp parse_child(_, _, response) do + {:error, + "Neither #{@enr_prefix} nor #{@enrtree_branch_prefix} is in DNS response: #{response}"} + end + + @spec get_children_branches(NodesList.network(), atom(), [String.t()]) :: :ok | {:error, term()} + defp get_children_branches(_network, _, []), do: :ok + + defp get_children_branches(network, nodes, [first_branch | rest]) do + {:ok, _pid} = search_for_nodes(network, nodes, first_branch) + get_children_branches(network, nodes, rest) + end + + @spec get_domain_name(NodesList.network()) :: String.t() + defp get_domain_name(:mainnet), do: "all.mainnet.ethdisco.net" + defp get_domain_name(:ropsten), do: "all.ropsten.ethdisco.net" + defp get_domain_name(:rinkeby), do: "all.rinkeby.ethdisco.net" + defp get_domain_name(:goerli), do: "all.goerli.ethdisco.net" +end diff --git a/eth_client/lib/eth_client/nodes_list_old.ex b/eth_client/lib/eth_client/nodes_list_old.ex new file mode 100644 index 0000000..31ce178 --- /dev/null +++ b/eth_client/lib/eth_client/nodes_list_old.ex @@ -0,0 +1,78 @@ +defmodule EthClient.NodesListOld do + @moduledoc """ + Retrieve a nodes list for a specific network. Right now, only supports getting a list + by DNS. + """ + + @type network :: :mainnet | :ropsten | :rinkeby | :goerli + + @enr_prefix "enr:" + @enrtree_branch_prefix "enrtree-branch:" + + # Probar con :ets, ir guardando los nodos sobre la marcha + @spec get_by_dns(network()) :: {:ok, String.t()} | {:error, term()} + def get_by_dns(network) do + with {:ok, enr_root} <- get_root(network) do + get_children(network, enr_root) + end + end + + @spec get_root(network()) :: {:ok, String.t()} | {:error, term()} + defp get_root(network) do + {:ok, response_splitted} = + network + |> get_domain_name() + |> DNS.resolve(:txt) + + response = Enum.join(response_splitted) + + case Regex.run(~r/^enrtree-root:v1 e=([\w\d]+) .*$/, response) do + [^response, enr_root] -> {:ok, enr_root} + nil -> {:error, "enr_root not found in DNS response: #{response}"} + end + end + + @spec get_children(network(), String.t()) :: {:ok, [String.t()]} | {:error, term()} + defp get_children(network, branch) do + branch_domain_name = branch <> "." <> get_domain_name(network) + {:ok, response_splitted} = DNS.resolve(branch_domain_name, :txt) + response = Enum.join(response_splitted) + + cond do + String.starts_with?(response, @enr_prefix) -> + @enr_prefix <> node = response + {:ok, [node]} + + String.starts_with?(response, @enrtree_branch_prefix) -> + @enrtree_branch_prefix <> branches = response + branches_splitted = String.split(branches, ",") + get_children_branches(network, branches_splitted, []) + + true -> + {:error, + "Neither #{@enr_prefix} nor #{@enrtree_branch_prefix} is in DNS response: #{response}"} + end + end + + @spec get_children_branches(network(), [String.t()], [String.t()]) :: + {:ok, [String.t()]} | {:error, term()} + defp get_children_branches(_network, [], nodes), do: {:ok, nodes} + + defp get_children_branches(network, branches, nodes) do + [branch | rest] = branches + + case get_children(network, branch) do + {:ok, new_nodes} -> + get_children_branches(network, rest, new_nodes ++ nodes) + + {:error, _} = error -> + error + end + end + + @spec get_domain_name(network()) :: String.t() + defp get_domain_name(:mainnet), do: "all.mainnet.ethdisco.net" + defp get_domain_name(:ropsten), do: "all.ropsten.ethdisco.net" + defp get_domain_name(:rinkeby), do: "all.rinkeby.ethdisco.net" + defp get_domain_name(:goerli), do: "all.goerli.ethdisco.net" +end From 4d2e0a967888b550a8e0cfec850db1d800e9f635 Mon Sep 17 00:00:00 2001 From: gabrielbosio Date: Tue, 24 May 2022 13:02:22 -0300 Subject: [PATCH 06/13] Refactor ETS handling into new module --- eth_client/lib/eth_client/nodes_list.ex | 20 ++++++----- eth_client/lib/eth_client/nodes_list/dns.ex | 35 +++++++++---------- .../lib/eth_client/nodes_list/storage.ex | 24 +++++++++++++ 3 files changed, 52 insertions(+), 27 deletions(-) create mode 100644 eth_client/lib/eth_client/nodes_list/storage.ex diff --git a/eth_client/lib/eth_client/nodes_list.ex b/eth_client/lib/eth_client/nodes_list.ex index 3f1ff06..6f8a31e 100644 --- a/eth_client/lib/eth_client/nodes_list.ex +++ b/eth_client/lib/eth_client/nodes_list.ex @@ -1,11 +1,12 @@ defmodule EthClient.NodesList do @moduledoc """ - Retrieve a nodes list for a specific network. Right now, only supports getting a list + Retrieve a storage list for a specific network. Right now, only supports getting a list by DNS. """ use GenServer alias EthClient.NodesList.DNS, as: NodesListDNS + alias EthClient.NodesList.Storage @type network :: :mainnet | :ropsten | :rinkeby | :goerli @@ -18,6 +19,7 @@ defmodule EthClient.NodesList do GenServer.call(__MODULE__, {:update_using_dns, network}) end + @spec get(network()) :: :ok | {:error, term()} def get(network) do GenServer.call(__MODULE__, {:get, network}) end @@ -26,26 +28,26 @@ defmodule EthClient.NodesList do @impl true def init(storage_name) do - nodes = :ets.new(storage_name, [:named_table, :public, read_concurrency: true]) + storage = Storage.new(storage_name) dns_supervisor_name = NodesListDNS.supervisor_name() Task.Supervisor.start_link(name: dns_supervisor_name) - {:ok, nodes} + {:ok, storage} end @impl true - def handle_call({:update_using_dns, network}, _from, nodes) do + def handle_call({:update_using_dns, network}, _from, storage) do result = with {:ok, enr_root} <- NodesListDNS.get_root(network) do - NodesListDNS.search_for_nodes(network, nodes, enr_root) + NodesListDNS.search_for_nodes(network, storage, enr_root) end - {:reply, result, nodes} + {:reply, result, storage} end @impl true - def handle_call({:get, network}, _from, nodes) do - result = NodesListDNS.get_nodes(network, nodes) + def handle_call({:get, network}, _from, storage) do + result = NodesListDNS.get_nodes(network, storage) - {:reply, result, nodes} + {:reply, result, storage} end end diff --git a/eth_client/lib/eth_client/nodes_list/dns.ex b/eth_client/lib/eth_client/nodes_list/dns.ex index 4c899b6..2a426d0 100644 --- a/eth_client/lib/eth_client/nodes_list/dns.ex +++ b/eth_client/lib/eth_client/nodes_list/dns.ex @@ -1,13 +1,17 @@ defmodule EthClient.NodesList.DNS do + @moduledoc """ + Module for handling storage list retrieval using DNS. + """ alias EthClient.NodesList + alias EthClient.NodesList.Storage @enr_prefix "enr:" @enrtree_branch_prefix "enrtree-branch:" - def search_for_nodes(network, nodes, enr_root) do + def search_for_nodes(network, storage, enr_root) do supervisor_name() |> Task.Supervisor.start_child(fn -> - get_children(network, nodes, enr_root) + get_children(network, storage, enr_root) end) end @@ -28,30 +32,25 @@ defmodule EthClient.NodesList.DNS do end end - def get_nodes(network, nodes), do: :ets.lookup(nodes, network) + @spec get_nodes(NodesList.network(), :ets.tid()) :: [tuple()] + def get_nodes(network, storage), do: Storage.lookup(storage, network) @spec get_children(NodesList.network(), atom(), String.t()) :: :ok | {:error, term()} - defp get_children(network, nodes, branch) do + defp get_children(network, storage, branch) do branch_domain_name = branch <> "." <> get_domain_name(network) {:ok, response_split} = DNS.resolve(branch_domain_name, :txt) response = Enum.join(response_split) - parse_child(network, nodes, response) + parse_child(network, storage, response) end @spec parse_child(NodesList.network(), atom(), String.t()) :: :ok | {:error, term()} - defp parse_child(network, nodes, @enr_prefix <> new_node) do - network_nodes = - case get_nodes(network, nodes) do - [{_, result}] -> result - [] -> [] - end - - :ets.insert(nodes, {network, [new_node | network_nodes]}) + defp parse_child(network, storage, @enr_prefix <> new_node) do + Storage.insert(storage, network, new_node) end - defp parse_child(network, nodes, @enrtree_branch_prefix <> branches) do + defp parse_child(network, storage, @enrtree_branch_prefix <> branches) do branches_split = String.split(branches, ",") - get_children_branches(network, nodes, branches_split) + get_children_branches(network, storage, branches_split) end defp parse_child(_, _, response) do @@ -62,9 +61,9 @@ defmodule EthClient.NodesList.DNS do @spec get_children_branches(NodesList.network(), atom(), [String.t()]) :: :ok | {:error, term()} defp get_children_branches(_network, _, []), do: :ok - defp get_children_branches(network, nodes, [first_branch | rest]) do - {:ok, _pid} = search_for_nodes(network, nodes, first_branch) - get_children_branches(network, nodes, rest) + defp get_children_branches(network, storage, [first_branch | rest]) do + {:ok, _pid} = search_for_nodes(network, storage, first_branch) + get_children_branches(network, storage, rest) end @spec get_domain_name(NodesList.network()) :: String.t() diff --git a/eth_client/lib/eth_client/nodes_list/storage.ex b/eth_client/lib/eth_client/nodes_list/storage.ex new file mode 100644 index 0000000..dd98f42 --- /dev/null +++ b/eth_client/lib/eth_client/nodes_list/storage.ex @@ -0,0 +1,24 @@ +defmodule EthClient.NodesList.Storage do + @moduledoc """ + Module for handling nodes list storage. + """ + @spec new(atom) :: :ets.tid() + def new(name) do + :ets.new(name, [:named_table, :public, read_concurrency: true]) + end + + @spec lookup(:ets.tid(), String.t()) :: [tuple()] + def lookup(storage, network) do + :ets.lookup(storage, network) + end + + def insert(storage, network, new_node) do + network_nodes = + case lookup(storage, network) do + [{_, result}] -> result + [] -> [] + end + + :ets.insert(storage, {network, [new_node | network_nodes]}) + end +end From 286a77b3d7e98c0b627c1234f57063632067a9d9 Mon Sep 17 00:00:00 2001 From: gabrielbosio Date: Tue, 24 May 2022 13:32:56 -0300 Subject: [PATCH 07/13] Fix dialyzer in DNS modules --- eth_client/lib/eth_client/nodes_list/dns.ex | 6 +----- eth_client/lib/eth_client/nodes_list/storage.ex | 3 +-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/eth_client/lib/eth_client/nodes_list/dns.ex b/eth_client/lib/eth_client/nodes_list/dns.ex index 2a426d0..1b87bd1 100644 --- a/eth_client/lib/eth_client/nodes_list/dns.ex +++ b/eth_client/lib/eth_client/nodes_list/dns.ex @@ -17,7 +17,6 @@ defmodule EthClient.NodesList.DNS do def supervisor_name, do: EthClient.NodesList.DNS.Supervisor - @spec get_root(NodesList.network()) :: {:ok, String.t()} | {:error, term()} def get_root(network) do {:ok, response_split} = network @@ -32,10 +31,8 @@ defmodule EthClient.NodesList.DNS do end end - @spec get_nodes(NodesList.network(), :ets.tid()) :: [tuple()] def get_nodes(network, storage), do: Storage.lookup(storage, network) - @spec get_children(NodesList.network(), atom(), String.t()) :: :ok | {:error, term()} defp get_children(network, storage, branch) do branch_domain_name = branch <> "." <> get_domain_name(network) {:ok, response_split} = DNS.resolve(branch_domain_name, :txt) @@ -43,9 +40,9 @@ defmodule EthClient.NodesList.DNS do parse_child(network, storage, response) end - @spec parse_child(NodesList.network(), atom(), String.t()) :: :ok | {:error, term()} defp parse_child(network, storage, @enr_prefix <> new_node) do Storage.insert(storage, network, new_node) + :ok end defp parse_child(network, storage, @enrtree_branch_prefix <> branches) do @@ -58,7 +55,6 @@ defmodule EthClient.NodesList.DNS do "Neither #{@enr_prefix} nor #{@enrtree_branch_prefix} is in DNS response: #{response}"} end - @spec get_children_branches(NodesList.network(), atom(), [String.t()]) :: :ok | {:error, term()} defp get_children_branches(_network, _, []), do: :ok defp get_children_branches(network, storage, [first_branch | rest]) do diff --git a/eth_client/lib/eth_client/nodes_list/storage.ex b/eth_client/lib/eth_client/nodes_list/storage.ex index dd98f42..701c85b 100644 --- a/eth_client/lib/eth_client/nodes_list/storage.ex +++ b/eth_client/lib/eth_client/nodes_list/storage.ex @@ -2,12 +2,11 @@ defmodule EthClient.NodesList.Storage do @moduledoc """ Module for handling nodes list storage. """ - @spec new(atom) :: :ets.tid() + def new(name) do :ets.new(name, [:named_table, :public, read_concurrency: true]) end - @spec lookup(:ets.tid(), String.t()) :: [tuple()] def lookup(storage, network) do :ets.lookup(storage, network) end From 3f5f1a00d59419b9b85eca60c0a07b03fa263447 Mon Sep 17 00:00:00 2001 From: gabrielbosio Date: Tue, 24 May 2022 13:59:43 -0300 Subject: [PATCH 08/13] Add wait to DNS requests to resolve --- eth_client/lib/eth_client/nodes_list.ex | 2 +- eth_client/lib/eth_client/nodes_list/dns.ex | 43 ++++++++++++++++--- .../lib/eth_client/nodes_list/storage.ex | 4 ++ 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/eth_client/lib/eth_client/nodes_list.ex b/eth_client/lib/eth_client/nodes_list.ex index 6f8a31e..499c369 100644 --- a/eth_client/lib/eth_client/nodes_list.ex +++ b/eth_client/lib/eth_client/nodes_list.ex @@ -38,7 +38,7 @@ defmodule EthClient.NodesList do def handle_call({:update_using_dns, network}, _from, storage) do result = with {:ok, enr_root} <- NodesListDNS.get_root(network) do - NodesListDNS.search_for_nodes(network, storage, enr_root) + NodesListDNS.start_searching_for_nodes(network, storage, enr_root) end {:reply, result, storage} diff --git a/eth_client/lib/eth_client/nodes_list/dns.ex b/eth_client/lib/eth_client/nodes_list/dns.ex index 1b87bd1..ba5e1e2 100644 --- a/eth_client/lib/eth_client/nodes_list/dns.ex +++ b/eth_client/lib/eth_client/nodes_list/dns.ex @@ -7,12 +7,12 @@ defmodule EthClient.NodesList.DNS do @enr_prefix "enr:" @enrtree_branch_prefix "enrtree-branch:" + @attempts 100 + @waiting_time_ms 500 - def search_for_nodes(network, storage, enr_root) do - supervisor_name() - |> Task.Supervisor.start_child(fn -> - get_children(network, storage, enr_root) - end) + def start_searching_for_nodes(network, storage, enr_root) do + Storage.delete(storage, network) + search_for_nodes(network, storage, enr_root) end def supervisor_name, do: EthClient.NodesList.DNS.Supervisor @@ -33,9 +33,16 @@ defmodule EthClient.NodesList.DNS do def get_nodes(network, storage), do: Storage.lookup(storage, network) + defp search_for_nodes(network, storage, node) do + supervisor_name() + |> Task.Supervisor.start_child(fn -> + get_children(network, storage, node) + end) + end + defp get_children(network, storage, branch) do branch_domain_name = branch <> "." <> get_domain_name(network) - {:ok, response_split} = DNS.resolve(branch_domain_name, :txt) + {:ok, response_split} = wait_to_resolve(branch_domain_name) response = Enum.join(response_split) parse_child(network, storage, response) end @@ -62,6 +69,30 @@ defmodule EthClient.NodesList.DNS do get_children_branches(network, storage, rest) end + defp wait_to_resolve(domain_name), do: wait_to_resolve(domain_name, @attempts) + + defp wait_to_resolve(domain_name, 0), do: {:error, "Cannot resolve #{domain_name}"} + + defp wait_to_resolve(domain_name, attempts) do + response = + try do + try_to_resolve(domain_name, :start) + rescue + Socket.Error -> {:error, attempts} + end + + try_to_resolve(domain_name, response) + end + + defp try_to_resolve(domain_name, :start), do: DNS.resolve(domain_name, :txt) + + defp try_to_resolve(domain_name, {:error, attempts}) do + Process.sleep(@waiting_time_ms) + wait_to_resolve(domain_name, attempts - 1) + end + + defp try_to_resolve(_domain_name, {:ok, _} = success), do: success + @spec get_domain_name(NodesList.network()) :: String.t() defp get_domain_name(:mainnet), do: "all.mainnet.ethdisco.net" defp get_domain_name(:ropsten), do: "all.ropsten.ethdisco.net" diff --git a/eth_client/lib/eth_client/nodes_list/storage.ex b/eth_client/lib/eth_client/nodes_list/storage.ex index 701c85b..954df13 100644 --- a/eth_client/lib/eth_client/nodes_list/storage.ex +++ b/eth_client/lib/eth_client/nodes_list/storage.ex @@ -20,4 +20,8 @@ defmodule EthClient.NodesList.Storage do :ets.insert(storage, {network, [new_node | network_nodes]}) end + + def delete(storage, network) do + :ets.delete(storage, network) + end end From 428f6940dfcd9704195039c5bd62fa55b334d8a7 Mon Sep 17 00:00:00 2001 From: gabrielbosio Date: Tue, 24 May 2022 16:25:49 -0300 Subject: [PATCH 09/13] Add wait for tasks to finish --- eth_client/lib/eth_client/nodes_list.ex | 4 +-- eth_client/lib/eth_client/nodes_list/dns.ex | 29 +++++++-------------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/eth_client/lib/eth_client/nodes_list.ex b/eth_client/lib/eth_client/nodes_list.ex index 499c369..d7c9dc7 100644 --- a/eth_client/lib/eth_client/nodes_list.ex +++ b/eth_client/lib/eth_client/nodes_list.ex @@ -16,7 +16,7 @@ defmodule EthClient.NodesList do @spec update_using_dns(network()) :: :ok | {:error, term()} def update_using_dns(network) do - GenServer.call(__MODULE__, {:update_using_dns, network}) + GenServer.call(__MODULE__, {:update_using_dns, network}, :infinity) end @spec get(network()) :: :ok | {:error, term()} @@ -29,8 +29,6 @@ defmodule EthClient.NodesList do @impl true def init(storage_name) do storage = Storage.new(storage_name) - dns_supervisor_name = NodesListDNS.supervisor_name() - Task.Supervisor.start_link(name: dns_supervisor_name) {:ok, storage} end diff --git a/eth_client/lib/eth_client/nodes_list/dns.ex b/eth_client/lib/eth_client/nodes_list/dns.ex index ba5e1e2..96a3d09 100644 --- a/eth_client/lib/eth_client/nodes_list/dns.ex +++ b/eth_client/lib/eth_client/nodes_list/dns.ex @@ -12,11 +12,10 @@ defmodule EthClient.NodesList.DNS do def start_searching_for_nodes(network, storage, enr_root) do Storage.delete(storage, network) - search_for_nodes(network, storage, enr_root) + search = Task.async(fn -> get_children(network, storage, enr_root) end) + Task.await(search, :infinity) end - def supervisor_name, do: EthClient.NodesList.DNS.Supervisor - def get_root(network) do {:ok, response_split} = network @@ -33,13 +32,6 @@ defmodule EthClient.NodesList.DNS do def get_nodes(network, storage), do: Storage.lookup(storage, network) - defp search_for_nodes(network, storage, node) do - supervisor_name() - |> Task.Supervisor.start_child(fn -> - get_children(network, storage, node) - end) - end - defp get_children(network, storage, branch) do branch_domain_name = branch <> "." <> get_domain_name(network) {:ok, response_split} = wait_to_resolve(branch_domain_name) @@ -53,8 +45,14 @@ defmodule EthClient.NodesList.DNS do end defp parse_child(network, storage, @enrtree_branch_prefix <> branches) do - branches_split = String.split(branches, ",") - get_children_branches(network, storage, branches_split) + branches + |> String.split(",") + |> Enum.map(fn branch -> + Task.async(fn -> get_children(network, storage, branch) end) + end) + |> Task.await_many(:infinity) + + :ok end defp parse_child(_, _, response) do @@ -62,13 +60,6 @@ defmodule EthClient.NodesList.DNS do "Neither #{@enr_prefix} nor #{@enrtree_branch_prefix} is in DNS response: #{response}"} end - defp get_children_branches(_network, _, []), do: :ok - - defp get_children_branches(network, storage, [first_branch | rest]) do - {:ok, _pid} = search_for_nodes(network, storage, first_branch) - get_children_branches(network, storage, rest) - end - defp wait_to_resolve(domain_name), do: wait_to_resolve(domain_name, @attempts) defp wait_to_resolve(domain_name, 0), do: {:error, "Cannot resolve #{domain_name}"} From 3fa9668d33d19c579392a86976ff2cfe7a313592 Mon Sep 17 00:00:00 2001 From: gabrielbosio Date: Tue, 24 May 2022 16:50:20 -0300 Subject: [PATCH 10/13] Set finite timeout Refactor ETS lookup. --- eth_client/lib/eth_client/nodes_list.ex | 4 +++- eth_client/lib/eth_client/nodes_list/dns.ex | 4 ++-- eth_client/lib/eth_client/nodes_list/storage.ex | 11 ++++------- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/eth_client/lib/eth_client/nodes_list.ex b/eth_client/lib/eth_client/nodes_list.ex index d7c9dc7..7d535b5 100644 --- a/eth_client/lib/eth_client/nodes_list.ex +++ b/eth_client/lib/eth_client/nodes_list.ex @@ -16,7 +16,7 @@ defmodule EthClient.NodesList do @spec update_using_dns(network()) :: :ok | {:error, term()} def update_using_dns(network) do - GenServer.call(__MODULE__, {:update_using_dns, network}, :infinity) + GenServer.call(__MODULE__, {:update_using_dns, network}, search_timeout()) end @spec get(network()) :: :ok | {:error, term()} @@ -24,6 +24,8 @@ defmodule EthClient.NodesList do GenServer.call(__MODULE__, {:get, network}) end + def search_timeout, do: 20000 + ## Server callbacks @impl true diff --git a/eth_client/lib/eth_client/nodes_list/dns.ex b/eth_client/lib/eth_client/nodes_list/dns.ex index 96a3d09..0ef1e06 100644 --- a/eth_client/lib/eth_client/nodes_list/dns.ex +++ b/eth_client/lib/eth_client/nodes_list/dns.ex @@ -13,7 +13,7 @@ defmodule EthClient.NodesList.DNS do def start_searching_for_nodes(network, storage, enr_root) do Storage.delete(storage, network) search = Task.async(fn -> get_children(network, storage, enr_root) end) - Task.await(search, :infinity) + Task.await(search, NodesList.search_timeout()) end def get_root(network) do @@ -50,7 +50,7 @@ defmodule EthClient.NodesList.DNS do |> Enum.map(fn branch -> Task.async(fn -> get_children(network, storage, branch) end) end) - |> Task.await_many(:infinity) + |> Task.await_many(NodesList.search_timeout()) :ok end diff --git a/eth_client/lib/eth_client/nodes_list/storage.ex b/eth_client/lib/eth_client/nodes_list/storage.ex index 954df13..0df86c1 100644 --- a/eth_client/lib/eth_client/nodes_list/storage.ex +++ b/eth_client/lib/eth_client/nodes_list/storage.ex @@ -8,16 +8,13 @@ defmodule EthClient.NodesList.Storage do end def lookup(storage, network) do - :ets.lookup(storage, network) + with [{^network, nodes}] <- :ets.lookup(storage, network) do + nodes + end end def insert(storage, network, new_node) do - network_nodes = - case lookup(storage, network) do - [{_, result}] -> result - [] -> [] - end - + network_nodes = lookup(storage, network) :ets.insert(storage, {network, [new_node | network_nodes]}) end From ea8e26f3097bea09f09a140056a2ad8b7c91056c Mon Sep 17 00:00:00 2001 From: gabrielbosio Date: Tue, 24 May 2022 16:51:24 -0300 Subject: [PATCH 11/13] Fix credo issue with constant --- eth_client/lib/eth_client/nodes_list.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eth_client/lib/eth_client/nodes_list.ex b/eth_client/lib/eth_client/nodes_list.ex index 7d535b5..9801445 100644 --- a/eth_client/lib/eth_client/nodes_list.ex +++ b/eth_client/lib/eth_client/nodes_list.ex @@ -24,7 +24,7 @@ defmodule EthClient.NodesList do GenServer.call(__MODULE__, {:get, network}) end - def search_timeout, do: 20000 + def search_timeout, do: 20_000 ## Server callbacks From 100fdaab233d4e7ea2e67429cd6b52be75e09618 Mon Sep 17 00:00:00 2001 From: gabrielbosio Date: Tue, 24 May 2022 18:41:11 -0300 Subject: [PATCH 12/13] Remove old NodesList module --- eth_client/lib/eth_client/nodes_list_old.ex | 78 --------------------- 1 file changed, 78 deletions(-) delete mode 100644 eth_client/lib/eth_client/nodes_list_old.ex diff --git a/eth_client/lib/eth_client/nodes_list_old.ex b/eth_client/lib/eth_client/nodes_list_old.ex deleted file mode 100644 index 31ce178..0000000 --- a/eth_client/lib/eth_client/nodes_list_old.ex +++ /dev/null @@ -1,78 +0,0 @@ -defmodule EthClient.NodesListOld do - @moduledoc """ - Retrieve a nodes list for a specific network. Right now, only supports getting a list - by DNS. - """ - - @type network :: :mainnet | :ropsten | :rinkeby | :goerli - - @enr_prefix "enr:" - @enrtree_branch_prefix "enrtree-branch:" - - # Probar con :ets, ir guardando los nodos sobre la marcha - @spec get_by_dns(network()) :: {:ok, String.t()} | {:error, term()} - def get_by_dns(network) do - with {:ok, enr_root} <- get_root(network) do - get_children(network, enr_root) - end - end - - @spec get_root(network()) :: {:ok, String.t()} | {:error, term()} - defp get_root(network) do - {:ok, response_splitted} = - network - |> get_domain_name() - |> DNS.resolve(:txt) - - response = Enum.join(response_splitted) - - case Regex.run(~r/^enrtree-root:v1 e=([\w\d]+) .*$/, response) do - [^response, enr_root] -> {:ok, enr_root} - nil -> {:error, "enr_root not found in DNS response: #{response}"} - end - end - - @spec get_children(network(), String.t()) :: {:ok, [String.t()]} | {:error, term()} - defp get_children(network, branch) do - branch_domain_name = branch <> "." <> get_domain_name(network) - {:ok, response_splitted} = DNS.resolve(branch_domain_name, :txt) - response = Enum.join(response_splitted) - - cond do - String.starts_with?(response, @enr_prefix) -> - @enr_prefix <> node = response - {:ok, [node]} - - String.starts_with?(response, @enrtree_branch_prefix) -> - @enrtree_branch_prefix <> branches = response - branches_splitted = String.split(branches, ",") - get_children_branches(network, branches_splitted, []) - - true -> - {:error, - "Neither #{@enr_prefix} nor #{@enrtree_branch_prefix} is in DNS response: #{response}"} - end - end - - @spec get_children_branches(network(), [String.t()], [String.t()]) :: - {:ok, [String.t()]} | {:error, term()} - defp get_children_branches(_network, [], nodes), do: {:ok, nodes} - - defp get_children_branches(network, branches, nodes) do - [branch | rest] = branches - - case get_children(network, branch) do - {:ok, new_nodes} -> - get_children_branches(network, rest, new_nodes ++ nodes) - - {:error, _} = error -> - error - end - end - - @spec get_domain_name(network()) :: String.t() - defp get_domain_name(:mainnet), do: "all.mainnet.ethdisco.net" - defp get_domain_name(:ropsten), do: "all.ropsten.ethdisco.net" - defp get_domain_name(:rinkeby), do: "all.rinkeby.ethdisco.net" - defp get_domain_name(:goerli), do: "all.goerli.ethdisco.net" -end From fd0b2702154d2ac162ad842a25f0bf492572ede9 Mon Sep 17 00:00:00 2001 From: gabrielbosio Date: Thu, 26 May 2022 12:53:16 -0300 Subject: [PATCH 13/13] Add nodes decoding --- eth_client/lib/eth_client/nodes_list/dns.ex | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/eth_client/lib/eth_client/nodes_list/dns.ex b/eth_client/lib/eth_client/nodes_list/dns.ex index 0ef1e06..86450d5 100644 --- a/eth_client/lib/eth_client/nodes_list/dns.ex +++ b/eth_client/lib/eth_client/nodes_list/dns.ex @@ -40,7 +40,12 @@ defmodule EthClient.NodesList.DNS do end defp parse_child(network, storage, @enr_prefix <> new_node) do - Storage.insert(storage, network, new_node) + decoded_node = + new_node + |> Base.url_decode64!(padding: false) + |> ExRLP.decode() + + Storage.insert(storage, network, decoded_node) :ok end