diff --git a/apps/explorer/lib/explorer/validator/metadata_retriever.ex b/apps/explorer/lib/explorer/validator/metadata_retriever.ex new file mode 100644 index 0000000000..682b1e906e --- /dev/null +++ b/apps/explorer/lib/explorer/validator/metadata_retriever.ex @@ -0,0 +1,76 @@ +defmodule Explorer.Validator.MetadataRetriever do + @moduledoc """ + Consults the configured smart contracts to fetch the valivators' metadata + """ + + alias Explorer.SmartContract.Reader + + def fetch_data do + fetch_validators_list() + |> Enum.map(fn validator -> + validator + |> fetch_validator_metadata + |> translate_metadata + |> Map.merge(%{address_hash: validator, primary: true}) + end) + end + + defp fetch_validators_list do + %{"getValidators" => {:ok, [validators]}} = + Reader.query_contract(config(:validators_contract_address), contract_abi("validators.json"), %{ + "getValidators" => [] + }) + + validators + end + + defp fetch_validator_metadata(validator_address) do + %{"validators" => {:ok, fields}} = + Reader.query_contract(config(:metadata_contract_address), contract_abi("metadata.json"), %{ + "validators" => [validator_address] + }) + + fields + end + + defp translate_metadata([ + first_name, + last_name, + license_id, + full_address, + state, + zipcode, + expiration_date, + created_date, + _updated_date, + _min_treshold + ]) do + %{ + name: trim_null_bytes(first_name) <> " " <> trim_null_bytes(last_name), + metadata: %{ + license_id: trim_null_bytes(license_id), + address: full_address, + state: trim_null_bytes(state), + zipcode: trim_null_bytes(zipcode), + expiration_date: expiration_date, + created_date: created_date + } + } + end + + defp trim_null_bytes(bytes) do + String.trim_trailing(bytes, <<0>>) + end + + defp config(key) do + Application.get_env(:explorer, __MODULE__, [])[key] + end + + # sobelow_skip ["Traversal"] + defp contract_abi(file_name) do + :explorer + |> Application.app_dir("priv/validator_contracts_abi/#{file_name}") + |> File.read!() + |> Jason.decode!() + end +end diff --git a/apps/explorer/test/explorer/validator/metadata_retriever_test.exs b/apps/explorer/test/explorer/validator/metadata_retriever_test.exs new file mode 100644 index 0000000000..b2656e7137 --- /dev/null +++ b/apps/explorer/test/explorer/validator/metadata_retriever_test.exs @@ -0,0 +1,100 @@ +defmodule Explorer.Validator.MetadataRetrieverTest do + use EthereumJSONRPC.Case + + alias Explorer.Validator.MetadataRetriever + import Mox + + setup :verify_on_exit! + setup :set_mox_global + + describe "fetch_data/0" do + test "returns maps with the info on each validator" do + validators_list_mox_ok() + validator_metadata_mox_ok() + + expected = [ + %{ + address_hash: <<0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1>>, + name: "Testname Unitarion", + primary: true, + metadata: %{ + address: "", + created_date: 0, + expiration_date: 253_370_764_800, + license_id: "00000000", + state: "XX", + zipcode: "00000" + } + } + ] + + assert MetadataRetriever.fetch_data() == expected + end + + test "raise error when the first contract call fails" do + contract_request_with_error("getValidators") + assert_raise(MatchError, fn -> MetadataRetriever.fetch_data() end) + end + + test "raise error when a call to the metadatc contract fails" do + validators_list_mox_ok() + contract_request_with_error("validators") + assert_raise(MatchError, fn -> MetadataRetriever.fetch_data() end) + end + end + + defp contract_request_with_error(id) do + expect( + EthereumJSONRPC.Mox, + :json_rpc, + fn [%{id: id, method: _, params: _}], _options -> + {:ok, + [ + %{ + error: %{code: -32015, data: "Reverted 0x", message: "VM execution error."}, + id: id, + jsonrpc: "2.0" + } + ]} + end + ) + end + + defp validators_list_mox_ok() do + expect( + EthereumJSONRPC.Mox, + :json_rpc, + 1, + fn [%{id: "getValidators"}], _opts -> + {:ok, + [ + %{ + id: "getValidators", + jsonrpc: "2.0", + result: + "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001" + } + ]} + end + ) + end + + defp validator_metadata_mox_ok() do + expect( + EthereumJSONRPC.Mox, + :json_rpc, + 1, + fn [%{id: "validators"}], _opts -> + {:ok, + [ + %{ + id: "validators", + jsonrpc: "2.0", + result: + "0x546573746e616d65000000000000000000000000000000000000000000000000556e69746172696f6e000000000000000000000000000000000000000000000030303030303030300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000140585800000000000000000000000000000000000000000000000000000000000030303030300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003afe130e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000058585858585858207374726565742058585858585800000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + ]} + end + ) + end +end