Merge pull request #1240 from poanetwork/gsf-validator-metadata

validator metadata retrieval and display
pull/1252/head
Gustavo 6 years ago committed by GitHub
commit cabee38387
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 41
      apps/block_scout_web/lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex
  2. 14
      apps/block_scout_web/lib/block_scout_web/templates/address/overview.html.eex
  3. 28
      apps/block_scout_web/lib/block_scout_web/views/address_view.ex
  4. 62
      apps/block_scout_web/priv/gettext/default.pot
  5. 62
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  6. 10
      apps/explorer/config/config.exs
  7. 3
      apps/explorer/lib/explorer/application.ex
  8. 6
      apps/explorer/lib/explorer/chain/address/name.ex
  9. 36
      apps/explorer/lib/explorer/validator/metadata_importer.ex
  10. 31
      apps/explorer/lib/explorer/validator/metadata_processor.ex
  11. 76
      apps/explorer/lib/explorer/validator/metadata_retriever.ex
  12. 9
      apps/explorer/priv/repo/migrations/20181213111656_add_metadata_field_to_address_names.exs
  13. 579
      apps/explorer/priv/validator_contracts_abi/metadata.json
  14. 462
      apps/explorer/priv/validator_contracts_abi/validators.json
  15. 48
      apps/explorer/test/explorer/validator/metadata_importer_test.exs
  16. 100
      apps/explorer/test/explorer/validator/metadata_retriever_test.exs

@ -0,0 +1,41 @@
<div class="modal fade" id="validatorModal" tabindex="-1" role="dialog" aria-labelledby="validatorModalLabel" aria-hidden="true">
<div class="modal-dialog modal-md" role="document">
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title" id="validatorModalLabel"><%= gettext"Validator Data" %></h2>
</div>
<div class="modal-body">
<h1 class="text-center"><%= @address_name %></h1>
<div class="row mt-2">
<div class="col-sm-4 text-right text-muted"><%= gettext"License ID" %></div>
<div class="col-sm-8">
<%= @validator_metadata["license_id"] %>
</div>
</div>
<div class="row mt-2">
<div class="col-sm-4 text-right text-muted"><%= gettext"Address" %></div>
<div class="col-sm-8">
<%= @validator_metadata["address"] %>,
<%= @validator_metadata["state"] %>,
<%= @validator_metadata["zipcode"] %>
</div>
</div>
<div class="row mt-2">
<div class="col-sm-4 text-right text-muted"><%= gettext"License Expires" %></div>
<div class="col-sm-8">
<%= format_datetime_string(@validator_metadata["expiration_date"]) %>
</div>
</div>
<div class="row mt-2">
<div class="col-sm-4 text-right text-muted"><%= gettext"Validator Creation Date" %></div>
<div class="col-sm-8">
<%= format_datetime_string(@validator_metadata["created_date"]) %>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-dismiss="modal"><%= gettext "Close" %></button>
</div>
</div>
</div>
</div>

@ -14,6 +14,13 @@
<i class="fas fa-qrcode"></i> <i class="fas fa-qrcode"></i>
</button> </button>
</span> </span>
<%= if validator_metadata = primary_validator_metadata(@address) do %>
<span data-toggle="modal" data-target="#validatorModal">
<button type="button" class="icon-link" data-toggle="tooltip" data-placement="top" title="<%= gettext("Validator Info") %>" aria-label="<%= gettext("Show Validator Info") %>">
<i class="fas fa-address-card"></i>
</button>
</span>
<% end %>
</div> </div>
<h1 class="card-title"><%= address_title(@address) %> <%= gettext "Details" %> </h1> <h1 class="card-title"><%= address_title(@address) %> <%= gettext "Details" %> </h1>
<h3 class="<%= if BlockScoutWeb.AddressView.contract?(@address) do %>contract-address<% end %>" data-test="address_detail_hash"><%= @address.hash %></h3> <h3 class="<%= if BlockScoutWeb.AddressView.contract?(@address) do %>contract-address<% end %>" data-test="address_detail_hash"><%= @address.hash %></h3>
@ -65,7 +72,7 @@
</div> </div>
</section> </section>
<!-- Modal --> <!-- Modal QR -->
<div class="modal fade" id="qrModal" tabindex="-1" role="dialog" aria-labelledby="qrModalLabel" aria-hidden="true"> <div class="modal fade" id="qrModal" tabindex="-1" role="dialog" aria-labelledby="qrModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm" role="document"> <div class="modal-dialog modal-sm" role="document">
<div class="modal-content"> <div class="modal-content">
@ -84,3 +91,8 @@
</div> </div>
</div> </div>
</div> </div>
<!-- Modal Validator -->
<%= if validator_metadata do %>
<%= render BlockScoutWeb.AddressView, "_validator_metadata_modal.html", address_name: address_name, validator_metadata: validator_metadata %>
<% end %>

@ -148,6 +148,34 @@ defmodule BlockScoutWeb.AddressView do
def primary_name(%Address{names: _}), do: nil def primary_name(%Address{names: _}), do: nil
def primary_validator_metadata(%Address{names: [_ | _] = address_names}) do
case Enum.find(address_names, &(&1.primary == true)) do
%Address.Name{
metadata:
metadata = %{
"license_id" => _,
"address" => _,
"state" => _,
"zipcode" => _,
"expiration_date" => _,
"created_date" => _
}
} ->
metadata
_ ->
nil
end
end
def primary_validator_metadata(%Address{names: _}), do: nil
def format_datetime_string(unix_date) do
unix_date
|> DateTime.from_unix!()
|> Timex.format!("{M}-{D}-{YYYY}")
end
def qr_code(%Address{hash: hash}) do def qr_code(%Address{hash: hash}) do
hash hash
|> to_string() |> to_string()

@ -97,6 +97,7 @@ msgid "Action"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:16
#: lib/block_scout_web/templates/transaction_log/index.html.eex:15 #: lib/block_scout_web/templates/transaction_log/index.html.eex:15
#: lib/block_scout_web/views/address_view.ex:92 #: lib/block_scout_web/views/address_view.ex:92
msgid "Address" msgid "Address"
@ -188,10 +189,10 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:40 #: lib/block_scout_web/templates/address/_tabs.html.eex:40
#: lib/block_scout_web/templates/address/_tabs.html.eex:103 #: lib/block_scout_web/templates/address/_tabs.html.eex:103
#: lib/block_scout_web/templates/address/overview.html.eex:37 #: lib/block_scout_web/templates/address/overview.html.eex:44
#: lib/block_scout_web/templates/address_validation/index.html.eex:30 #: lib/block_scout_web/templates/address_validation/index.html.eex:30
#: lib/block_scout_web/templates/address_validation/index.html.eex:57 #: lib/block_scout_web/templates/address_validation/index.html.eex:57
#: lib/block_scout_web/views/address_view.ex:225 #: lib/block_scout_web/views/address_view.ex:253
msgid "Blocks Validated" msgid "Blocks Validated"
msgstr "" msgstr ""
@ -207,8 +208,9 @@ msgid "Clear"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:74 #: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:37
#: lib/block_scout_web/templates/address/overview.html.eex:82 #: lib/block_scout_web/templates/address/overview.html.eex:81
#: lib/block_scout_web/templates/address/overview.html.eex:89
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:84 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:84
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:92 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:92
#: lib/block_scout_web/templates/transaction_log/index.html.eex:94 #: lib/block_scout_web/templates/transaction_log/index.html.eex:94
@ -222,7 +224,7 @@ msgstr ""
#: lib/block_scout_web/templates/address_validation/index.html.eex:39 #: lib/block_scout_web/templates/address_validation/index.html.eex:39
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:119 #: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:119
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:141 #: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:141
#: lib/block_scout_web/views/address_view.ex:222 #: lib/block_scout_web/views/address_view.ex:250
msgid "Code" msgid "Code"
msgstr "" msgstr ""
@ -340,7 +342,7 @@ msgid "Copyright %{year} POA"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:43 #: lib/block_scout_web/templates/address/overview.html.eex:50
msgid "Created by" msgid "Created by"
msgstr "" msgstr ""
@ -363,7 +365,7 @@ msgid "Description"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:18 #: lib/block_scout_web/templates/address/overview.html.eex:25
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:120 #: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:120
msgid "Details" msgid "Details"
msgstr "" msgstr ""
@ -504,7 +506,7 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:14 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:14
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:43 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:43
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:10 #: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:10
#: lib/block_scout_web/views/address_view.ex:221 #: lib/block_scout_web/views/address_view.ex:249
#: lib/block_scout_web/views/transaction_view.ex:190 #: lib/block_scout_web/views/transaction_view.ex:190
msgid "Internal Transactions" msgid "Internal Transactions"
msgstr "" msgstr ""
@ -706,7 +708,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:13 #: lib/block_scout_web/templates/address/overview.html.eex:13
#: lib/block_scout_web/templates/address/overview.html.eex:73 #: lib/block_scout_web/templates/address/overview.html.eex:80
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:13 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:13
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:13 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:13
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:83 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:83
@ -723,7 +725,7 @@ msgstr ""
#: lib/block_scout_web/templates/address/_tabs.html.eex:122 #: lib/block_scout_web/templates/address/_tabs.html.eex:122
#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:33 #: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:33
#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:75 #: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:75
#: lib/block_scout_web/views/address_view.ex:223 #: lib/block_scout_web/views/address_view.ex:251
#: lib/block_scout_web/views/tokens/overview_view.ex:37 #: lib/block_scout_web/views/tokens/overview_view.ex:37
msgid "Read Contract" msgid "Read Contract"
msgstr "" msgstr ""
@ -921,7 +923,7 @@ msgstr ""
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:12 #: lib/block_scout_web/templates/address_token_transfer/index.html.eex:12
#: lib/block_scout_web/templates/address_validation/index.html.eex:11 #: lib/block_scout_web/templates/address_validation/index.html.eex:11
#: lib/block_scout_web/templates/address_validation/index.html.eex:19 #: lib/block_scout_web/templates/address_validation/index.html.eex:19
#: lib/block_scout_web/views/address_view.ex:219 #: lib/block_scout_web/views/address_view.ex:247
msgid "Tokens" msgid "Tokens"
msgstr "" msgstr ""
@ -982,13 +984,13 @@ msgstr ""
#: lib/block_scout_web/templates/block_transaction/index.html.eex:35 #: lib/block_scout_web/templates/block_transaction/index.html.eex:35
#: lib/block_scout_web/templates/chain/show.html.eex:69 #: lib/block_scout_web/templates/chain/show.html.eex:69
#: lib/block_scout_web/templates/layout/_topnav.html.eex:35 #: lib/block_scout_web/templates/layout/_topnav.html.eex:35
#: lib/block_scout_web/views/address_view.ex:220 #: lib/block_scout_web/views/address_view.ex:248
msgid "Transactions" msgid "Transactions"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/_tile.html.eex:19 #: lib/block_scout_web/templates/address/_tile.html.eex:19
#: lib/block_scout_web/templates/address/overview.html.eex:33 #: lib/block_scout_web/templates/address/overview.html.eex:40
msgid "Transactions sent" msgid "Transactions sent"
msgstr "" msgstr ""
@ -1115,7 +1117,7 @@ msgid "Yes"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:49 #: lib/block_scout_web/templates/address/overview.html.eex:56
msgid "at" msgid "at"
msgstr "" msgstr ""
@ -1437,7 +1439,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:30 #: lib/block_scout_web/templates/address/_tabs.html.eex:30
#: lib/block_scout_web/templates/address/_tabs.html.eex:96 #: lib/block_scout_web/templates/address/_tabs.html.eex:96
#: lib/block_scout_web/views/address_view.ex:224 #: lib/block_scout_web/views/address_view.ex:252
msgid "Coin Balance History" msgid "Coin Balance History"
msgstr "" msgstr ""
@ -1470,3 +1472,33 @@ msgstr ""
#: lib/block_scout_web/templates/block/index.html.eex:21 #: lib/block_scout_web/templates/block/index.html.eex:21
msgid "There are no blocks." msgid "There are no blocks."
msgstr "" msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:24
msgid "License Expires"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:10
msgid "License ID"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:19
msgid "Show Validator Info"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:30
msgid "Validator Creation Date"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:5
msgid "Validator Data"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:19
msgid "Validator Info"
msgstr ""

@ -97,6 +97,7 @@ msgid "Action"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:16
#: lib/block_scout_web/templates/transaction_log/index.html.eex:15 #: lib/block_scout_web/templates/transaction_log/index.html.eex:15
#: lib/block_scout_web/views/address_view.ex:92 #: lib/block_scout_web/views/address_view.ex:92
msgid "Address" msgid "Address"
@ -188,10 +189,10 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:40 #: lib/block_scout_web/templates/address/_tabs.html.eex:40
#: lib/block_scout_web/templates/address/_tabs.html.eex:103 #: lib/block_scout_web/templates/address/_tabs.html.eex:103
#: lib/block_scout_web/templates/address/overview.html.eex:37 #: lib/block_scout_web/templates/address/overview.html.eex:44
#: lib/block_scout_web/templates/address_validation/index.html.eex:30 #: lib/block_scout_web/templates/address_validation/index.html.eex:30
#: lib/block_scout_web/templates/address_validation/index.html.eex:57 #: lib/block_scout_web/templates/address_validation/index.html.eex:57
#: lib/block_scout_web/views/address_view.ex:225 #: lib/block_scout_web/views/address_view.ex:253
msgid "Blocks Validated" msgid "Blocks Validated"
msgstr "" msgstr ""
@ -207,8 +208,9 @@ msgid "Clear"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:74 #: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:37
#: lib/block_scout_web/templates/address/overview.html.eex:82 #: lib/block_scout_web/templates/address/overview.html.eex:81
#: lib/block_scout_web/templates/address/overview.html.eex:89
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:84 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:84
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:92 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:92
#: lib/block_scout_web/templates/transaction_log/index.html.eex:94 #: lib/block_scout_web/templates/transaction_log/index.html.eex:94
@ -222,7 +224,7 @@ msgstr ""
#: lib/block_scout_web/templates/address_validation/index.html.eex:39 #: lib/block_scout_web/templates/address_validation/index.html.eex:39
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:119 #: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:119
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:141 #: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:141
#: lib/block_scout_web/views/address_view.ex:222 #: lib/block_scout_web/views/address_view.ex:250
msgid "Code" msgid "Code"
msgstr "" msgstr ""
@ -340,7 +342,7 @@ msgid "Copyright %{year} POA"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:43 #: lib/block_scout_web/templates/address/overview.html.eex:50
msgid "Created by" msgid "Created by"
msgstr "" msgstr ""
@ -363,7 +365,7 @@ msgid "Description"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:18 #: lib/block_scout_web/templates/address/overview.html.eex:25
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:120 #: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:120
msgid "Details" msgid "Details"
msgstr "" msgstr ""
@ -504,7 +506,7 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:14 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:14
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:43 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:43
#: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:10 #: lib/block_scout_web/templates/transaction_internal_transaction/index.html.eex:10
#: lib/block_scout_web/views/address_view.ex:221 #: lib/block_scout_web/views/address_view.ex:249
#: lib/block_scout_web/views/transaction_view.ex:190 #: lib/block_scout_web/views/transaction_view.ex:190
msgid "Internal Transactions" msgid "Internal Transactions"
msgstr "" msgstr ""
@ -706,7 +708,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:13 #: lib/block_scout_web/templates/address/overview.html.eex:13
#: lib/block_scout_web/templates/address/overview.html.eex:73 #: lib/block_scout_web/templates/address/overview.html.eex:80
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:13 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:13
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:13 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:13
#: lib/block_scout_web/templates/tokens/overview/_details.html.eex:83 #: lib/block_scout_web/templates/tokens/overview/_details.html.eex:83
@ -723,7 +725,7 @@ msgstr ""
#: lib/block_scout_web/templates/address/_tabs.html.eex:122 #: lib/block_scout_web/templates/address/_tabs.html.eex:122
#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:33 #: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:33
#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:75 #: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:75
#: lib/block_scout_web/views/address_view.ex:223 #: lib/block_scout_web/views/address_view.ex:251
#: lib/block_scout_web/views/tokens/overview_view.ex:37 #: lib/block_scout_web/views/tokens/overview_view.ex:37
msgid "Read Contract" msgid "Read Contract"
msgstr "" msgstr ""
@ -921,7 +923,7 @@ msgstr ""
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:12 #: lib/block_scout_web/templates/address_token_transfer/index.html.eex:12
#: lib/block_scout_web/templates/address_validation/index.html.eex:11 #: lib/block_scout_web/templates/address_validation/index.html.eex:11
#: lib/block_scout_web/templates/address_validation/index.html.eex:19 #: lib/block_scout_web/templates/address_validation/index.html.eex:19
#: lib/block_scout_web/views/address_view.ex:219 #: lib/block_scout_web/views/address_view.ex:247
msgid "Tokens" msgid "Tokens"
msgstr "" msgstr ""
@ -982,13 +984,13 @@ msgstr ""
#: lib/block_scout_web/templates/block_transaction/index.html.eex:35 #: lib/block_scout_web/templates/block_transaction/index.html.eex:35
#: lib/block_scout_web/templates/chain/show.html.eex:69 #: lib/block_scout_web/templates/chain/show.html.eex:69
#: lib/block_scout_web/templates/layout/_topnav.html.eex:35 #: lib/block_scout_web/templates/layout/_topnav.html.eex:35
#: lib/block_scout_web/views/address_view.ex:220 #: lib/block_scout_web/views/address_view.ex:248
msgid "Transactions" msgid "Transactions"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/_tile.html.eex:19 #: lib/block_scout_web/templates/address/_tile.html.eex:19
#: lib/block_scout_web/templates/address/overview.html.eex:33 #: lib/block_scout_web/templates/address/overview.html.eex:40
msgid "Transactions sent" msgid "Transactions sent"
msgstr "" msgstr ""
@ -1115,7 +1117,7 @@ msgid "Yes"
msgstr "" msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:49 #: lib/block_scout_web/templates/address/overview.html.eex:56
msgid "at" msgid "at"
msgstr "" msgstr ""
@ -1437,7 +1439,7 @@ msgstr ""
#, elixir-format #, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:30 #: lib/block_scout_web/templates/address/_tabs.html.eex:30
#: lib/block_scout_web/templates/address/_tabs.html.eex:96 #: lib/block_scout_web/templates/address/_tabs.html.eex:96
#: lib/block_scout_web/views/address_view.ex:224 #: lib/block_scout_web/views/address_view.ex:252
msgid "Coin Balance History" msgid "Coin Balance History"
msgstr "" msgstr ""
@ -1470,3 +1472,33 @@ msgstr ""
#: lib/block_scout_web/templates/block/index.html.eex:21 #: lib/block_scout_web/templates/block/index.html.eex:21
msgid "There are no blocks." msgid "There are no blocks."
msgstr "" msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:24
msgid "License Expires"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:10
msgid "License ID"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:19
msgid "Show Validator Info"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:30
msgid "Validator Creation Date"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_validator_metadata_modal.html.eex:5
msgid "Validator Data"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/overview.html.eex:19
msgid "Validator Info"
msgstr ""

@ -32,6 +32,16 @@ config :explorer, Explorer.Tracer,
adapter: SpandexDatadog.Adapter, adapter: SpandexDatadog.Adapter,
trace_key: :blockscout trace_key: :blockscout
if System.get_env("METADATA_CONTRACT") && System.get_env("VALIDATORS_CONTRACT") do
config :explorer, Explorer.Validator.MetadataRetriever,
metadata_contract_address: System.get_env("METADATA_CONTRACT"),
validators_contract_address: System.get_env("VALIDATORS_CONTRACT")
config :explorer, Explorer.Validator.MetadataProcessor, enabled: true
else
config :explorer, Explorer.Validator.MetadataProcessor, enabled: false
end
if System.get_env("SUPPLY_MODULE") == "TransactionAndLog" do if System.get_env("SUPPLY_MODULE") == "TransactionAndLog" do
config :explorer, supply: Explorer.Chain.Supply.TransactionAndLog config :explorer, supply: Explorer.Chain.Supply.TransactionAndLog
end end

@ -38,7 +38,8 @@ defmodule Explorer.Application do
configure(Explorer.Counters.TokenHoldersCounter), configure(Explorer.Counters.TokenHoldersCounter),
configure(Explorer.Counters.TokenTransferCounter), configure(Explorer.Counters.TokenTransferCounter),
configure(Explorer.Counters.BlockValidationCounter), configure(Explorer.Counters.BlockValidationCounter),
configure(Explorer.Counters.AddressesWithBalanceCounter) configure(Explorer.Counters.AddressesWithBalanceCounter),
configure(Explorer.Validator.MetadataProcessor)
] ]
|> List.flatten() |> List.flatten()
end end

@ -20,20 +20,22 @@ defmodule Explorer.Chain.Address.Name do
address: %Ecto.Association.NotLoaded{} | Address.t(), address: %Ecto.Association.NotLoaded{} | Address.t(),
address_hash: Hash.Address.t(), address_hash: Hash.Address.t(),
name: String.t(), name: String.t(),
primary: boolean() primary: boolean(),
metadata: map()
} }
@primary_key false @primary_key false
schema "address_names" do schema "address_names" do
field(:name, :string) field(:name, :string)
field(:primary, :boolean) field(:primary, :boolean)
field(:metadata, :map)
belongs_to(:address, Address, foreign_key: :address_hash, references: :hash, type: Hash.Address) belongs_to(:address, Address, foreign_key: :address_hash, references: :hash, type: Hash.Address)
timestamps() timestamps()
end end
@required_fields ~w(address_hash name)a @required_fields ~w(address_hash name)a
@optional_fields ~w(primary)a @optional_fields ~w(primary metadata)a
@allowed_fields @required_fields ++ @optional_fields @allowed_fields @required_fields ++ @optional_fields
def changeset(%__MODULE__{} = struct, params \\ %{}) do def changeset(%__MODULE__{} = struct, params \\ %{}) do

@ -0,0 +1,36 @@
defmodule Explorer.Validator.MetadataImporter do
@moduledoc """
module that upserts validator metadata from a list of maps
"""
alias Explorer.Chain.Address
alias Explorer.Repo
import Ecto.Query, only: [from: 2]
def import_metadata(metadata_maps) do
Repo.transaction(fn -> Enum.each(metadata_maps, &upsert_validator_metadata(&1)) end)
end
defp upsert_validator_metadata(validator_changeset) do
case Repo.get_by(Address.Name, address_hash: validator_changeset.address_hash, primary: true) do
nil ->
%Address.Name{}
|> Address.Name.changeset(validator_changeset)
|> Repo.insert()
_address_name ->
query =
from(an in Address.Name,
update: [
set: [
name: ^validator_changeset.name,
metadata: ^validator_changeset.metadata
]
],
where: an.address_hash == ^validator_changeset.address_hash and an.primary == true
)
Repo.update_all(query, [])
end
end
end

@ -0,0 +1,31 @@
defmodule Explorer.Validator.MetadataProcessor do
@moduledoc """
module to periodically retrieve and update metadata belonging to validators
"""
use GenServer
alias Explorer.Validator.{MetadataImporter, MetadataRetriever}
def start_link(_) do
GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
end
@impl true
def init(args) do
send(self(), :import_and_reschedule)
{:ok, args}
end
@impl true
def handle_info(:import_and_reschedule, state) do
MetadataRetriever.fetch_data()
|> MetadataImporter.import_metadata()
reschedule()
{:noreply, state}
end
defp reschedule do
Process.send_after(self(), :import_and_reschedule, :timer.hours(24))
end
end

@ -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

@ -0,0 +1,9 @@
defmodule Explorer.Repo.Migrations.AddMetadataFieldToAddressNames do
use Ecto.Migration
def change do
alter table(:address_names) do
add(:metadata, :map)
end
end
end

@ -0,0 +1,579 @@
[
{
"constant": true,
"inputs": [
{
"name": "_miningKey",
"type": "address"
}
],
"name": "pendingChanges",
"outputs": [
{
"name": "firstName",
"type": "bytes32"
},
{
"name": "lastName",
"type": "bytes32"
},
{
"name": "licenseId",
"type": "bytes32"
},
{
"name": "fullAddress",
"type": "string"
},
{
"name": "state",
"type": "bytes32"
},
{
"name": "zipcode",
"type": "bytes32"
},
{
"name": "expirationDate",
"type": "uint256"
},
{
"name": "createdDate",
"type": "uint256"
},
{
"name": "updatedDate",
"type": "uint256"
},
{
"name": "minThreshold",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0x022c254a"
},
{
"constant": false,
"inputs": [
{
"name": "_firstName",
"type": "bytes32"
},
{
"name": "_lastName",
"type": "bytes32"
},
{
"name": "_licenseId",
"type": "bytes32"
},
{
"name": "_fullAddress",
"type": "string"
},
{
"name": "_state",
"type": "bytes32"
},
{
"name": "_zipcode",
"type": "bytes32"
},
{
"name": "_expirationDate",
"type": "uint256"
}
],
"name": "createMetadata",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function",
"signature": "0x334460a4"
},
{
"constant": true,
"inputs": [
{
"name": "_miningKey",
"type": "address"
}
],
"name": "confirmations",
"outputs": [
{
"name": "count",
"type": "uint256"
},
{
"name": "voters",
"type": "address[]"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0x4ecb35c4"
},
{
"constant": false,
"inputs": [
{
"name": "_miningKey",
"type": "address"
}
],
"name": "finalize",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function",
"signature": "0x4ef39b75"
},
{
"constant": true,
"inputs": [],
"name": "getTime",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0x557ed1ba"
},
{
"constant": false,
"inputs": [
{
"name": "_miningKey",
"type": "address"
}
],
"name": "clearMetadata",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function",
"signature": "0x885c69b5"
},
{
"constant": false,
"inputs": [
{
"name": "_firstName",
"type": "bytes32"
},
{
"name": "_lastName",
"type": "bytes32"
},
{
"name": "_licenseId",
"type": "bytes32"
},
{
"name": "_fullAddress",
"type": "string"
},
{
"name": "_state",
"type": "bytes32"
},
{
"name": "_zipcode",
"type": "bytes32"
},
{
"name": "_expirationDate",
"type": "uint256"
},
{
"name": "_createdDate",
"type": "uint256"
},
{
"name": "_updatedDate",
"type": "uint256"
},
{
"name": "_minThreshold",
"type": "uint256"
},
{
"name": "_miningKey",
"type": "address"
}
],
"name": "initMetadata",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function",
"signature": "0x8ae881a6"
},
{
"constant": false,
"inputs": [
{
"name": "_miningKey",
"type": "address"
}
],
"name": "confirmPendingChange",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function",
"signature": "0x9c715535"
},
{
"constant": true,
"inputs": [],
"name": "initMetadataDisabled",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0xa6662a3c"
},
{
"constant": true,
"inputs": [],
"name": "proxyStorage",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0xae4b1b5b"
},
{
"constant": true,
"inputs": [
{
"name": "_miningKey",
"type": "address"
}
],
"name": "getValidatorName",
"outputs": [
{
"name": "firstName",
"type": "bytes32"
},
{
"name": "lastName",
"type": "bytes32"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0xaf2e2da9"
},
{
"constant": false,
"inputs": [
{
"name": "_oldMiningKey",
"type": "address"
},
{
"name": "_newMiningKey",
"type": "address"
}
],
"name": "moveMetadata",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function",
"signature": "0xc2d0916f"
},
{
"constant": false,
"inputs": [
{
"name": "_firstName",
"type": "bytes32"
},
{
"name": "_lastName",
"type": "bytes32"
},
{
"name": "_licenseId",
"type": "bytes32"
},
{
"name": "_fullAddress",
"type": "string"
},
{
"name": "_state",
"type": "bytes32"
},
{
"name": "_zipcode",
"type": "bytes32"
},
{
"name": "_expirationDate",
"type": "uint256"
}
],
"name": "changeRequest",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function",
"signature": "0xc3f1b0ea"
},
{
"constant": true,
"inputs": [],
"name": "getMinThreshold",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0xe6bbe9dd"
},
{
"constant": false,
"inputs": [],
"name": "initMetadataDisable",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function",
"signature": "0xf0174a25"
},
{
"constant": true,
"inputs": [
{
"name": "_miningKey",
"type": "address"
},
{
"name": "_voterMiningKey",
"type": "address"
}
],
"name": "isValidatorAlreadyVoted",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0xf73294b8"
},
{
"constant": false,
"inputs": [],
"name": "cancelPendingChange",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function",
"signature": "0xf94c12cb"
},
{
"constant": true,
"inputs": [
{
"name": "_miningKey",
"type": "address"
}
],
"name": "validators",
"outputs": [
{
"name": "firstName",
"type": "bytes32"
},
{
"name": "lastName",
"type": "bytes32"
},
{
"name": "licenseId",
"type": "bytes32"
},
{
"name": "fullAddress",
"type": "string"
},
{
"name": "state",
"type": "bytes32"
},
{
"name": "zipcode",
"type": "bytes32"
},
{
"name": "expirationDate",
"type": "uint256"
},
{
"name": "createdDate",
"type": "uint256"
},
{
"name": "updatedDate",
"type": "uint256"
},
{
"name": "minThreshold",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0xfa52c7d8"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "miningKey",
"type": "address"
}
],
"name": "MetadataCleared",
"type": "event",
"signature": "0x1928e25e316bab82325e01eaf5b4a29f7b2d5e3d77fc0b6ac959eb95112f3ee9"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "miningKey",
"type": "address"
}
],
"name": "MetadataCreated",
"type": "event",
"signature": "0x76a10cc0839d166c55eec8dba39ff22f75470574ede5014b744b45cebea5fc7e"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "oldMiningKey",
"type": "address"
},
{
"indexed": true,
"name": "newMiningKey",
"type": "address"
}
],
"name": "MetadataMoved",
"type": "event",
"signature": "0x6a3e5f5cc14de86bb3be87bc976b74890ecd0c8fa0cfca2fe3fefe55c9b47dde"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "miningKey",
"type": "address"
}
],
"name": "ChangeRequestInitiated",
"type": "event",
"signature": "0x6ec0e3afd4b29a1fe1c688cb1e6474d9e2c6a1032858c3add0ff32b7ba95f32f"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "miningKey",
"type": "address"
}
],
"name": "CancelledRequest",
"type": "event",
"signature": "0x5a1dcc05b2a41ad121a798e749b9ba9584177c68a4047ee52ef37d4ca76ce08c"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "miningKey",
"type": "address"
},
{
"indexed": false,
"name": "votingSender",
"type": "address"
},
{
"indexed": false,
"name": "votingSenderMiningKey",
"type": "address"
}
],
"name": "Confirmed",
"type": "event",
"signature": "0x603c57ecb4a9802537649ceb6523e5d48c939e7856768ce6f9b3128a889a5cfe"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "miningKey",
"type": "address"
}
],
"name": "FinalizedChange",
"type": "event",
"signature": "0xccf0f685803f0fba33ec88246b35d75b758b1e77c3d65ef5658f7e630f36b85b"
}
]

@ -0,0 +1,462 @@
[
{
"constant": true,
"inputs": [
{
"name": "",
"type": "uint256"
}
],
"name": "pendingList",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0x03aca792"
},
{
"constant": true,
"inputs": [],
"name": "getCurrentValidatorsLength",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0x0eaba26a"
},
{
"constant": false,
"inputs": [
{
"name": "_newAddress",
"type": "address"
}
],
"name": "setProxyStorage",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function",
"signature": "0x10855269"
},
{
"constant": false,
"inputs": [
{
"name": "_validator",
"type": "address"
},
{
"name": "_shouldFireEvent",
"type": "bool"
}
],
"name": "addValidator",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function",
"signature": "0x21a3fb85"
},
{
"constant": true,
"inputs": [],
"name": "isMasterOfCeremonyRemovedPending",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0x273cb593"
},
{
"constant": true,
"inputs": [],
"name": "isMasterOfCeremonyRemoved",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0x379fed9a"
},
{
"constant": true,
"inputs": [
{
"name": "",
"type": "address"
}
],
"name": "validatorsState",
"outputs": [
{
"name": "isValidator",
"type": "bool"
},
{
"name": "isValidatorFinalized",
"type": "bool"
},
{
"name": "index",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0x4110a489"
},
{
"constant": true,
"inputs": [],
"name": "getPendingList",
"outputs": [
{
"name": "",
"type": "address[]"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0x45199e0a"
},
{
"constant": false,
"inputs": [],
"name": "finalizeChange",
"outputs": [],
"payable": false,
"stateMutability": "nonpayable",
"type": "function",
"signature": "0x75286211"
},
{
"constant": false,
"inputs": [
{
"name": "_newKey",
"type": "address"
},
{
"name": "_oldKey",
"type": "address"
}
],
"name": "swapValidatorKey",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function",
"signature": "0x879736b2"
},
{
"constant": true,
"inputs": [
{
"name": "_someone",
"type": "address"
}
],
"name": "isValidatorFinalized",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0x8f2eabe1"
},
{
"constant": true,
"inputs": [
{
"name": "",
"type": "uint256"
}
],
"name": "currentValidators",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0x900eb5a8"
},
{
"constant": true,
"inputs": [],
"name": "getKeysManager",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0x9a573786"
},
{
"constant": true,
"inputs": [],
"name": "wasProxyStorageSet",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0xa5f8b874"
},
{
"constant": true,
"inputs": [],
"name": "getCurrentValidatorsLengthWithoutMoC",
"outputs": [
{
"name": "",
"type": "uint256"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0xa8756337"
},
{
"constant": true,
"inputs": [],
"name": "proxyStorage",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0xae4b1b5b"
},
{
"constant": true,
"inputs": [],
"name": "finalized",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0xb3f05b97"
},
{
"constant": true,
"inputs": [],
"name": "getValidators",
"outputs": [
{
"name": "",
"type": "address[]"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0xb7ab4db5"
},
{
"constant": true,
"inputs": [],
"name": "systemAddress",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0xd3e848f1"
},
{
"constant": true,
"inputs": [],
"name": "masterOfCeremonyPending",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0xec7de1e9"
},
{
"constant": false,
"inputs": [
{
"name": "_validator",
"type": "address"
},
{
"name": "_shouldFireEvent",
"type": "bool"
}
],
"name": "removeValidator",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "function",
"signature": "0xf89a77b1"
},
{
"constant": true,
"inputs": [],
"name": "masterOfCeremony",
"outputs": [
{
"name": "",
"type": "address"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0xfa81b200"
},
{
"constant": true,
"inputs": [
{
"name": "_someone",
"type": "address"
}
],
"name": "isValidator",
"outputs": [
{
"name": "",
"type": "bool"
}
],
"payable": false,
"stateMutability": "view",
"type": "function",
"signature": "0xfacd743b"
},
{
"inputs": [
{
"name": "_masterOfCeremony",
"type": "address"
},
{
"name": "validators",
"type": "address[]"
}
],
"payable": false,
"stateMutability": "nonpayable",
"type": "constructor",
"signature": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"name": "parentHash",
"type": "bytes32"
},
{
"indexed": false,
"name": "newSet",
"type": "address[]"
}
],
"name": "InitiateChange",
"type": "event",
"signature": "0x55252fa6eee4741b4e24a74a70e9c11fd2c2281df8d6ea13126ff845f7825c89"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"name": "newSet",
"type": "address[]"
}
],
"name": "ChangeFinalized",
"type": "event",
"signature": "0x8564cd629b15f47dc310d45bcbfc9bcf5420b0d51bf0659a16c67f91d2763253"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"name": "proxyStorage",
"type": "address"
}
],
"name": "MoCInitializedProxyStorage",
"type": "event",
"signature": "0x600bcf04a13e752d1e3670a5a9f1c21177ca2a93c6f5391d4f1298d098097c22"
}
]

@ -0,0 +1,48 @@
defmodule Explorer.Validator.MetadataImporterTest do
use Explorer.DataCase
require Ecto.Query
import Ecto.Query
import Explorer.Factory
alias Explorer.Chain.Address
alias Explorer.Repo
alias Explorer.Validator.MetadataImporter
describe "import_metadata/1" do
test "inserts new address names when there's none for the validators" do
address = insert(:address)
[%{address_hash: address.hash, name: "Testinit Unitorius", primary: true, metadata: %{"test" => "toast"}}]
|> MetadataImporter.import_metadata()
address_names =
from(an in Address.Name, where: an.address_hash == ^address.hash and an.primary == true)
|> Repo.all()
expected_name = %Address.Name{address_hash: address.hash, name: "Testit Unitorus", metadata: %{"test" => "toast"}}
assert length(address_names) == 1
assert expected_name = hd(address_names)
end
test "updates the primary address name if the validator already has one" do
address = insert(:address)
insert(:address_name, address: address, primary: true, name: "Nodealus Faileddi")
[%{address_hash: address.hash, name: "Testit Unitorus", primary: true, metadata: %{"test" => "toast"}}]
|> MetadataImporter.import_metadata()
address_names =
from(an in Address.Name, where: an.address_hash == ^address.hash and an.primary == true)
|> Repo.all()
expected_name = %Address.Name{address_hash: address.hash, name: "Testit Unitorus", metadata: %{"test" => "toast"}}
assert length(address_names) == 1
assert expected_name = hd(address_names)
end
end
end

@ -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
Loading…
Cancel
Save