Merge branch 'mobile-menu' of github.com:YegorSan/blockscout into mobile-menu

pull/2665/head
Yegor San 5 years ago
commit 8e66349ace
  1. 3
      CHANGELOG.md
  2. 2
      apps/block_scout_web/lib/block_scout_web/templates/address_contract/index.html.eex
  3. 9
      apps/block_scout_web/lib/block_scout_web/views/address_contract_view.ex
  4. 20
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs
  5. 47
      apps/block_scout_web/test/block_scout_web/views/address_contract_view_test.exs
  6. 19
      apps/explorer/config/config.exs
  7. 4
      apps/explorer/lib/explorer/application.ex
  8. 22
      apps/explorer/lib/explorer/chain.ex
  9. 4
      apps/explorer/lib/explorer/chain/cache/block_number.ex
  10. 4
      apps/explorer/lib/explorer/chain/cache/blocks.ex
  11. 4
      apps/explorer/lib/explorer/chain/cache/transactions.ex
  12. 42
      apps/explorer/lib/explorer/chain/ordered_cache.ex
  13. 39
      apps/explorer/lib/explorer/chain/smart_contract.ex
  14. 54
      apps/explorer/lib/explorer/chain_spec/parity/importer.ex
  15. 89
      apps/explorer/lib/explorer/chain_spec/poa/importer.ex
  16. 6
      apps/explorer/test/explorer/chain/cache/block_number_test.exs
  17. 2
      docs/env-variables.md

@ -2,6 +2,8 @@
### Features ### Features
- [#2665](https://github.com/poanetwork/blockscout/pull/2665) - New layout for mobile menu - [#2665](https://github.com/poanetwork/blockscout/pull/2665) - New layout for mobile menu
- [#2588](https://github.com/poanetwork/blockscout/pull/2588) - add verification submission comment
- [#2505](https://github.com/poanetwork/blockscout/pull/2505) - support POA Network emission rewards
- [#2636](https://github.com/poanetwork/blockscout/pull/2636) - Execute all address' transactions page queries in parallel - [#2636](https://github.com/poanetwork/blockscout/pull/2636) - Execute all address' transactions page queries in parallel
- [#2596](https://github.com/poanetwork/blockscout/pull/2596) - support AuRa's empty step reward type - [#2596](https://github.com/poanetwork/blockscout/pull/2596) - support AuRa's empty step reward type
- [#2581](https://github.com/poanetwork/blockscout/pull/2581) - Add generic Map-like Cache behaviour and implementation - [#2581](https://github.com/poanetwork/blockscout/pull/2581) - Add generic Map-like Cache behaviour and implementation
@ -11,6 +13,7 @@
- [#2497](https://github.com/poanetwork/blockscout/pull/2497) - Add generic Ordered Cache behaviour and implementation - [#2497](https://github.com/poanetwork/blockscout/pull/2497) - Add generic Ordered Cache behaviour and implementation
### Fixes ### Fixes
- [#2612](https://github.com/poanetwork/blockscout/pull/2612) - Add cache updating independently from Indexer
- [#2659](https://github.com/poanetwork/blockscout/pull/2659) - Multipurpose front-end part update - [#2659](https://github.com/poanetwork/blockscout/pull/2659) - Multipurpose front-end part update
- [#2468](https://github.com/poanetwork/blockscout/pull/2468) - fix confirmations for non consensus blocks - [#2468](https://github.com/poanetwork/blockscout/pull/2468) - fix confirmations for non consensus blocks
- [#2610](https://github.com/poanetwork/blockscout/pull/2610) - use CoinGecko instead of CoinMarketcap for exchange rates - [#2610](https://github.com/poanetwork/blockscout/pull/2610) - use CoinGecko instead of CoinMarketcap for exchange rates

@ -66,7 +66,7 @@
</button> </button>
</div> </div>
<div class="tile tile-muted mb-4"> <div class="tile tile-muted mb-4">
<pre class="pre-scrollable line-numbers" data-activate-highlight><code class="solidity"><%= for {line, number} <- contract_lines_with_index(@address.smart_contract.contract_source_code) do %><div data-line-number="<%= number %>"><%= line %></div><% end %></code></pre> <pre class="pre-scrollable line-numbers" data-activate-highlight><code class="solidity"><%= for {line, number} <- contract_lines_with_index(@address.smart_contract.contract_source_code, @address.smart_contract.inserted_at) do %><div data-line-number="<%= number %>"><%= line %></div><% end %></code></pre>
</div> </div>
</section> </section>

@ -2,7 +2,7 @@ defmodule BlockScoutWeb.AddressContractView do
use BlockScoutWeb, :view use BlockScoutWeb, :view
alias ABI.{FunctionSelector, TypeDecoder} alias ABI.{FunctionSelector, TypeDecoder}
alias Explorer.Chain.{Address, Data, InternalTransaction} alias Explorer.Chain.{Address, Data, InternalTransaction, SmartContract}
def render("scripts.html", %{conn: conn}) do def render("scripts.html", %{conn: conn}) do
render_scripts(conn, "address_contract/code_highlighting.js") render_scripts(conn, "address_contract/code_highlighting.js")
@ -63,8 +63,11 @@ defmodule BlockScoutWeb.AddressContractView do
end) end)
end end
def contract_lines_with_index(contract_source_code) do def contract_lines_with_index(source_code, inserted_at \\ nil) do
contract_lines = String.split(contract_source_code, "\n") contract_lines =
source_code
|> String.split("\n")
|> SmartContract.add_submitted_comment(inserted_at)
max_digits = max_digits =
contract_lines contract_lines

@ -1,6 +1,6 @@
defmodule BlockScoutWeb.API.RPC.ContractControllerTest do defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
use BlockScoutWeb.ConnCase use BlockScoutWeb.ConnCase
alias Explorer.Factory alias Explorer.{Chain, Factory}
describe "listcontracts" do describe "listcontracts" do
setup do setup do
@ -450,7 +450,9 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
expected_result = [ expected_result = [
%{ %{
"Address" => to_string(contract.address_hash), "Address" => to_string(contract.address_hash),
"SourceCode" => contract.contract_source_code, "SourceCode" =>
"/**\n* Submitted for verification at blockscout.com on #{contract.inserted_at}\n*/\n" <>
contract.contract_source_code,
"ABI" => Jason.encode!(contract.abi), "ABI" => Jason.encode!(contract.abi),
"ContractName" => contract.name, "ContractName" => contract.name,
"CompilerVersion" => contract.compiler_version, "CompilerVersion" => contract.compiler_version,
@ -496,9 +498,13 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
|> get("/api", params) |> get("/api", params)
|> json_response(200) |> json_response(200)
verified_contract = Chain.address_hash_to_smart_contract(contract_address.hash)
expected_result = %{ expected_result = %{
"Address" => to_string(contract_address.hash), "Address" => to_string(contract_address.hash),
"SourceCode" => contract_code_info.source_code, "SourceCode" =>
"/**\n* Submitted for verification at blockscout.com on #{verified_contract.inserted_at}\n*/\n" <>
contract_code_info.source_code,
"ABI" => Jason.encode!(contract_code_info.abi), "ABI" => Jason.encode!(contract_code_info.abi),
"ContractName" => contract_code_info.name, "ContractName" => contract_code_info.name,
"CompilerVersion" => contract_code_info.version, "CompilerVersion" => contract_code_info.version,
@ -563,8 +569,14 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do
result = response["result"] result = response["result"]
verified_contract = Chain.address_hash_to_smart_contract(contract_address.hash)
assert result["Address"] == to_string(contract_address.hash) assert result["Address"] == to_string(contract_address.hash)
assert result["SourceCode"] == contract_source_code
assert result["SourceCode"] ==
"/**\n* Submitted for verification at blockscout.com on #{verified_contract.inserted_at}\n*/\n" <>
contract_source_code
assert result["ContractName"] == name assert result["ContractName"] == name
assert result["DecompiledSourceCode"] == "Contract source code not decompiled." assert result["DecompiledSourceCode"] == "Contract source code not decompiled."
assert result["DecompilerVersion"] == "" assert result["DecompilerVersion"] == ""

@ -38,22 +38,25 @@ defmodule BlockScoutWeb.AddressContractViewTest do
result = AddressContractView.contract_lines_with_index(code) result = AddressContractView.contract_lines_with_index(code)
assert result == [ assert result == [
{"pragma solidity >=0.4.22 <0.6.0;", " 1"}, {"/**", " 1"},
{"", " 2"}, {"* Submitted for verification at blockscout.com on ", " 2"},
{"struct Proposal {", " 3"}, {"*/", " 3"},
{" uint voteCount;", " 4"}, {"pragma solidity >=0.4.22 <0.6.0;", " 4"},
{"}", " 5"}, {"", " 5"},
{"", " 6"}, {"struct Proposal {", " 6"},
{"address chairperson;", " 7"}, {" uint voteCount;", " 7"},
{"mapping(address => Voter) voters;", " 8"}, {"}", " 8"},
{"Proposal[] proposals;", " 9"}, {"", " 9"},
{"", "10"}, {"address chairperson;", "10"},
{"constructor(uint8 _numProposals) public {", "11"}, {"mapping(address => Voter) voters;", "11"},
{" chairperson = msg.sender;", "12"}, {"Proposal[] proposals;", "12"},
{" voters[chairperson].weight = 1;", "13"}, {"", "13"},
{" proposals.length = _numProposals;", "14"}, {"constructor(uint8 _numProposals) public {", "14"},
{"}", "15"}, {" chairperson = msg.sender;", "15"},
{"", "16"} {" voters[chairperson].weight = 1;", "16"},
{" proposals.length = _numProposals;", "17"},
{"}", "18"},
{"", "19"}
] ]
end end
@ -66,7 +69,17 @@ defmodule BlockScoutWeb.AddressContractViewTest do
test "returns a list of tuples and the first element is just a line from the original string" do test "returns a list of tuples and the first element is just a line from the original string" do
result = AddressContractView.contract_lines_with_index("a\nb\nc\nd\ne") result = AddressContractView.contract_lines_with_index("a\nb\nc\nd\ne")
assert Enum.map(result, fn {line, _number} -> line end) == ["a", "b", "c", "d", "e"]
assert Enum.map(result, fn {line, _number} -> line end) == [
"/**",
"* Submitted for verification at blockscout.com on ",
"*/",
"a",
"b",
"c",
"d",
"e"
]
end end
end end
end end

@ -27,9 +27,16 @@ config :explorer, Explorer.Counters.AverageBlockTime,
enabled: true, enabled: true,
period: average_block_period period: average_block_period
config :explorer, Explorer.ChainSpec.GenesisData, enabled: false, chain_spec_path: System.get_env("CHAIN_SPEC_PATH") config :explorer, Explorer.ChainSpec.GenesisData,
enabled: true,
chain_spec_path: System.get_env("CHAIN_SPEC_PATH"),
emission_format: System.get_env("EMISSION_FORMAT", "DEFAULT"),
rewards_contract_address: System.get_env("REWARDS_CONTRACT_ADDRESS", "0xeca443e8e1ab29971a45a9c57a6a9875701698a5")
config :explorer, Explorer.Chain.Cache.BlockNumber, enabled: true config :explorer, Explorer.Chain.Cache.BlockNumber,
enabled: true,
ttl_check_interval: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(1), else: false),
global_ttl: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(5))
config :explorer, Explorer.ExchangeRates.Source.CoinGecko, coin_id: System.get_env("COIN_GECKO_ID", "poa-network") config :explorer, Explorer.ExchangeRates.Source.CoinGecko, coin_id: System.get_env("COIN_GECKO_ID", "poa-network")
@ -123,6 +130,14 @@ market_history_cache_period =
config :explorer, Explorer.Market.MarketHistoryCache, period: market_history_cache_period config :explorer, Explorer.Market.MarketHistoryCache, period: market_history_cache_period
config :explorer, Explorer.Chain.Cache.Blocks,
ttl_check_interval: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(1), else: false),
global_ttl: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(5))
config :explorer, Explorer.Chain.Cache.Transactions,
ttl_check_interval: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(1), else: false),
global_ttl: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(5))
# Import environment specific config. This must remain at the bottom # Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above. # of this file so it overrides the configuration defined above.
import_config "#{Mix.env()}.exs" import_config "#{Mix.env()}.exs"

@ -56,9 +56,7 @@ defmodule Explorer.Application do
opts = [strategy: :one_for_one, name: Explorer.Supervisor] opts = [strategy: :one_for_one, name: Explorer.Supervisor]
res = Supervisor.start_link(children, opts) Supervisor.start_link(children, opts)
res
end end
defp configurable_children do defp configurable_children do

@ -2592,8 +2592,26 @@ defmodule Explorer.Chain do
@spec address_hash_to_address_with_source_code(Hash.Address.t()) :: Address.t() | nil @spec address_hash_to_address_with_source_code(Hash.Address.t()) :: Address.t() | nil
def address_hash_to_address_with_source_code(address_hash) do def address_hash_to_address_with_source_code(address_hash) do
case Repo.get(Address, address_hash) do case Repo.get(Address, address_hash) do
nil -> nil nil ->
address -> Repo.preload(address, [:smart_contract, :decompiled_smart_contracts]) nil
address ->
address_with_smart_contract = Repo.preload(address, [:smart_contract, :decompiled_smart_contracts])
if address_with_smart_contract.smart_contract do
formatted_code =
SmartContract.add_submitted_comment(
address_with_smart_contract.smart_contract.contract_source_code,
address_with_smart_contract.smart_contract.inserted_at
)
%{
address_with_smart_contract
| smart_contract: %{address_with_smart_contract.smart_contract | contract_source_code: formatted_code}
}
else
address_with_smart_contract
end
end end
end end

@ -7,7 +7,9 @@ defmodule Explorer.Chain.Cache.BlockNumber do
use Explorer.Chain.MapCache, use Explorer.Chain.MapCache,
name: :block_number, name: :block_number,
keys: [:min, :max] keys: [:min, :max],
ttl_check_interval: Application.get_env(:explorer, __MODULE__)[:ttl_check_interval],
global_ttl: Application.get_env(:explorer, __MODULE__)[:global_ttl]
alias Explorer.Chain alias Explorer.Chain

@ -11,7 +11,9 @@ defmodule Explorer.Chain.Cache.Blocks do
ids_list_key: "block_numbers", ids_list_key: "block_numbers",
preload: :transactions, preload: :transactions,
preload: [miner: :names], preload: [miner: :names],
preload: :rewards preload: :rewards,
ttl_check_interval: Application.get_env(:explorer, __MODULE__)[:ttl_check_interval],
global_ttl: Application.get_env(:explorer, __MODULE__)[:global_ttl]
@type element :: Block.t() @type element :: Block.t()

@ -16,7 +16,9 @@ defmodule Explorer.Chain.Cache.Transactions do
token_transfers: :token, token_transfers: :token,
token_transfers: :from_address, token_transfers: :from_address,
token_transfers: :to_address token_transfers: :to_address
] ],
ttl_check_interval: Application.get_env(:explorer, __MODULE__)[:ttl_check_interval],
global_ttl: Application.get_env(:explorer, __MODULE__)[:global_ttl]
@type element :: Transaction.t() @type element :: Transaction.t()

@ -26,7 +26,10 @@ defmodule Explorer.Chain.OrderedCache do
preload: [transaction: :hash] preload: [transaction: :hash]
``` ```
Additionally all of the options accepted by `ConCache.start_link/1` can be Additionally all of the options accepted by `ConCache.start_link/1` can be
provided as well. By default only `ttl_check_interval:` is set (to `false`). provided as well. Unless specified, only these values have defaults:
- `:ttl_check_interval` is set (to `false`).
- `:callback` is only set if `:ttl_check_interval` is not `false` to call the
`remove_deleted_from_index` function, that removes expired values from the index.
It's also possible, and advised, to override the implementation of the `c:prevails?/2` It's also possible, and advised, to override the implementation of the `c:prevails?/2`
and `c:element_to_id/1` callbacks. and `c:element_to_id/1` callbacks.
@ -131,10 +134,7 @@ defmodule Explorer.Chain.OrderedCache do
max_size = Keyword.get(opts, :max_size, 100) max_size = Keyword.get(opts, :max_size, 100)
preloads = Keyword.get(opts, :preloads) || Keyword.get_values(opts, :preload) preloads = Keyword.get(opts, :preloads) || Keyword.get_values(opts, :preload)
concache_params = concache_params = Keyword.drop(opts, [:ids_list_key, :max_size, :preloads, :preload])
opts
|> Keyword.drop([:ids_list_key, :max_size, :preloads, :preload])
|> Keyword.put_new(:ttl_check_interval, false)
# credo:disable-for-next-line Credo.Check.Refactor.LongQuoteBlocks # credo:disable-for-next-line Credo.Check.Refactor.LongQuoteBlocks
quote do quote do
@ -206,6 +206,19 @@ defmodule Explorer.Chain.OrderedCache do
### Updating function ### Updating function
def remove_deleted_from_index({:delete, _cache_pid, id}) do
# simply check with `ConCache.get` because it is faster
if Enum.member?(ids_list(), id) do
ConCache.update(cache_name(), ids_list_key(), fn ids ->
updated_list = List.delete(ids || [], id)
# ids_list is set to never expire
{:ok, %ConCache.Item{value: updated_list, ttl: :infinity}}
end)
end
end
def remove_deleted_from_index(_), do: nil
@impl OrderedCache @impl OrderedCache
def update(elements) when is_nil(elements), do: :ok def update(elements) when is_nil(elements), do: :ok
@ -217,7 +230,8 @@ defmodule Explorer.Chain.OrderedCache do
|> Enum.sort(&prevails?(&1, &2)) |> Enum.sort(&prevails?(&1, &2))
|> merge_and_update(ids || [], max_size()) |> merge_and_update(ids || [], max_size())
{:ok, updated_list} # ids_list is set to never expire
{:ok, %ConCache.Item{value: updated_list, ttl: :infinity}}
end) end)
end end
@ -308,7 +322,21 @@ defmodule Explorer.Chain.OrderedCache do
provided to this function will override the ones set by using the macro provided to this function will override the ones set by using the macro
""" """
def child_spec(params) do def child_spec(params) do
params = Keyword.merge(unquote(concache_params), params) # params specified in `use`
merged_params =
unquote(concache_params)
# params specified in `child_spec`
|> Keyword.merge(params)
# `:ttl_check_interval` needs to be specified, defaults to `false`
|> Keyword.put_new(:ttl_check_interval, false)
# if `:ttl_check_interval` is not `false` the expired values need to be
# removed from the cache's index
params =
case merged_params[:ttl_check_interval] do
false -> merged_params
_ -> Keyword.put_new(merged_params, :callback, &remove_deleted_from_index/1)
end
Supervisor.child_spec({ConCache, params}, id: child_id()) Supervisor.child_spec({ConCache, params}, id: child_id())
end end

@ -271,6 +271,45 @@ defmodule Explorer.Chain.SmartContract do
|> add_error(:contract_source_code, error_message(error)) |> add_error(:contract_source_code, error_message(error))
end end
def add_submitted_comment(code, inserted_at) when is_binary(code) do
code
|> String.split("\n")
|> add_submitted_comment(inserted_at)
|> Enum.join("\n")
end
def add_submitted_comment(contract_lines, inserted_at) when is_list(contract_lines) do
etherscan_index =
Enum.find_index(contract_lines, fn line ->
String.contains?(line, "Submitted for verification at Etherscan.io")
end)
blockscout_index =
Enum.find_index(contract_lines, fn line ->
String.contains?(line, "Submitted for verification at blockscout.com")
end)
cond do
etherscan_index && blockscout_index ->
List.replace_at(contract_lines, etherscan_index, "*")
etherscan_index && !blockscout_index ->
List.replace_at(
contract_lines,
etherscan_index,
"* Submitted for verification at blockscout.com on #{inserted_at}"
)
!etherscan_index && !blockscout_index ->
header = ["/**", "* Submitted for verification at blockscout.com on #{inserted_at}", "*/"]
header ++ contract_lines
true ->
contract_lines
end
end
defp upsert_contract_methods(%Ecto.Changeset{changes: %{abi: abi}} = changeset) do defp upsert_contract_methods(%Ecto.Changeset{changes: %{abi: abi}} = changeset) do
ContractMethod.upsert_from_abi(abi, get_field(changeset, :address_hash)) ContractMethod.upsert_from_abi(abi, get_field(changeset, :address_hash))

@ -3,18 +3,23 @@ defmodule Explorer.ChainSpec.Parity.Importer do
Imports data from parity chain spec. Imports data from parity chain spec.
""" """
require Logger
alias Explorer.{Chain, Repo} alias Explorer.{Chain, Repo}
alias Explorer.Chain.Block.{EmissionReward, Range} alias Explorer.Chain.Block.{EmissionReward, Range}
alias Explorer.Chain.Hash.Address, as: AddressHash alias Explorer.Chain.Hash.Address, as: AddressHash
alias Explorer.Chain.Wei alias Explorer.Chain.Wei
alias Explorer.ChainSpec.GenesisData
alias Explorer.ChainSpec.POA.Importer, as: PoaEmissionImporter
@max_block_number :infinity @max_block_number :infinity
def import_emission_rewards(chain_spec) do def import_emission_rewards(chain_spec) do
rewards = emission_rewards(chain_spec) if Application.get_env(:explorer, GenesisData)[:emission_format] == "POA" do
PoaEmissionImporter.import_emission_rewards()
{_, nil} = Repo.delete_all(EmissionReward) else
{_, nil} = Repo.insert_all(EmissionReward, rewards) import_rewards_from_chain_spec(chain_spec)
end
end end
def import_genesis_coin_balances(chain_spec) do def import_genesis_coin_balances(chain_spec) do
@ -38,18 +43,37 @@ defmodule Explorer.ChainSpec.Parity.Importer do
Chain.import(params) Chain.import(params)
end end
defp import_rewards_from_chain_spec(chain_spec) do
rewards = emission_rewards(chain_spec)
{_, nil} = Repo.delete_all(EmissionReward)
{_, nil} = Repo.insert_all(EmissionReward, rewards)
end
def genesis_coin_balances(chain_spec) do def genesis_coin_balances(chain_spec) do
accounts = chain_spec["accounts"] accounts = chain_spec["accounts"]
parse_accounts(accounts) if accounts do
parse_accounts(accounts)
else
Logger.warn(fn -> "No accounts are defined in chain spec" end)
[]
end
end end
def emission_rewards(chain_spec) do def emission_rewards(chain_spec) do
rewards = chain_spec["engine"]["Ethash"]["params"]["blockReward"] rewards = chain_spec["engine"]["Ethash"]["params"]["blockReward"]
rewards if rewards do
|> parse_hex_numbers() rewards
|> format_ranges() |> parse_hex_numbers()
|> format_ranges()
else
Logger.warn(fn -> "No rewards are defined in chain spec" end)
[]
end
end end
defp parse_accounts(accounts) do defp parse_accounts(accounts) do
@ -59,7 +83,7 @@ defmodule Explorer.ChainSpec.Parity.Importer do
end) end)
|> Stream.map(fn {address, %{"balance" => value}} -> |> Stream.map(fn {address, %{"balance" => value}} ->
{:ok, address_hash} = AddressHash.cast(address) {:ok, address_hash} = AddressHash.cast(address)
balance = parse_hex_number(value) balance = parse_number(value)
%{address_hash: address_hash, value: balance} %{address_hash: address_hash, value: balance}
end) end)
@ -92,16 +116,22 @@ defmodule Explorer.ChainSpec.Parity.Importer do
defp parse_hex_numbers(rewards) do defp parse_hex_numbers(rewards) do
Enum.map(rewards, fn {hex_block_number, hex_reward} -> Enum.map(rewards, fn {hex_block_number, hex_reward} ->
block_number = parse_hex_number(hex_block_number) block_number = parse_number(hex_block_number)
{:ok, reward} = hex_reward |> parse_hex_number() |> Wei.cast() {:ok, reward} = hex_reward |> parse_number() |> Wei.cast()
{block_number, reward} {block_number, reward}
end) end)
end end
defp parse_hex_number("0x" <> hex_number) do defp parse_number("0x" <> hex_number) do
{number, ""} = Integer.parse(hex_number, 16) {number, ""} = Integer.parse(hex_number, 16)
number number
end end
defp parse_number(string_number) do
{number, ""} = Integer.parse(string_number, 10)
number
end
end end

@ -0,0 +1,89 @@
defmodule Explorer.ChainSpec.POA.Importer do
@moduledoc """
Imports emission reward range for POA chain.
"""
require Logger
alias Explorer.Chain.Wei
alias Explorer.Repo
alias Explorer.SmartContract.Reader
alias Explorer.Chain.Block.{EmissionReward, Range}
alias Explorer.ChainSpec.GenesisData
@block_reward_amount_abi %{
"type" => "function",
"stateMutability" => "view",
"payable" => false,
"outputs" => [%{"type" => "uint256", "name" => ""}],
"name" => "blockRewardAmount",
"inputs" => [],
"constant" => true
}
@block_reward_amount_params %{"blockRewardAmount" => []}
@emission_funds_amount_abi %{
"type" => "function",
"stateMutability" => "view",
"payable" => false,
"outputs" => [%{"type" => "uint256", "name" => ""}],
"name" => "emissionFundsAmount",
"inputs" => [],
"constant" => true
}
@emission_funds_amount_params %{"emissionFundsAmount" => []}
@emission_funds_block_start 5_098_087
def import_emission_rewards do
if is_nil(rewards_contract_address()) do
Logger.warn(fn -> "No rewards contract address is defined" end)
else
block_reward = block_reward_amount()
emission_funds = emission_funds_amount()
rewards = [
%{
block_range: %Range{from: 0, to: @emission_funds_block_start},
reward: %Wei{value: block_reward}
},
%{
block_range: %Range{from: @emission_funds_block_start + 1, to: :infinity},
reward: %Wei{value: Decimal.add(block_reward, emission_funds)}
}
]
{_, nil} = Repo.delete_all(EmissionReward)
{_, nil} = Repo.insert_all(EmissionReward, rewards)
end
end
def block_reward_amount do
call_contract(rewards_contract_address(), @block_reward_amount_abi, @block_reward_amount_params)
end
def emission_funds_amount do
call_contract(rewards_contract_address(), @emission_funds_amount_abi, @emission_funds_amount_params)
end
defp rewards_contract_address do
Application.get_env(:explorer, GenesisData)[:rewards_contract_address]
end
defp call_contract(address, abi, params) do
abi = [abi]
method_name =
params
|> Enum.map(fn {key, _value} -> key end)
|> List.first()
Reader.query_contract(address, abi, params)
value =
case Reader.query_contract(address, abi, params) do
%{^method_name => {:ok, [result]}} -> result
_ -> 0
end
Decimal.new(value)
end
end

@ -11,7 +11,7 @@ defmodule Explorer.Chain.Cache.BlockNumberTest do
end) end)
end end
describe "get_max/1" do describe "get_max/0" do
test "returns max number" do test "returns max number" do
insert(:block, number: 5) insert(:block, number: 5)
@ -19,7 +19,7 @@ defmodule Explorer.Chain.Cache.BlockNumberTest do
end end
end end
describe "get_min/1" do describe "get_min/0" do
test "returns min number" do test "returns min number" do
insert(:block, number: 2) insert(:block, number: 2)
@ -27,7 +27,7 @@ defmodule Explorer.Chain.Cache.BlockNumberTest do
end end
end end
describe "get_all/1" do describe "get_all/0" do
test "returns min and max number" do test "returns min and max number" do
insert(:block, number: 6) insert(:block, number: 6)

@ -66,3 +66,5 @@ $ export NETWORK=POA
| `API_URL` | | Link to API instance, e.g. `http://host/path` | (empty) | v2.0.3+ | | | | `API_URL` | | Link to API instance, e.g. `http://host/path` | (empty) | v2.0.3+ | | |
| `CHAIN_SPEC_PATH` | | Chain specification path (absolute file system path or url) to import block emission reward ranges and genesis account balances from | (empty) | master | | | | `CHAIN_SPEC_PATH` | | Chain specification path (absolute file system path or url) to import block emission reward ranges and genesis account balances from | (empty) | master | | |
| `COIN_GECKO_ID` | | CoinGecko coin id required for fetching an exchange rate | poa-network | master | | | | `COIN_GECKO_ID` | | CoinGecko coin id required for fetching an exchange rate | poa-network | master | | |
| `EMISSION_FORMAT` | | Should be set to `POA` if you have block emission indentical to POA Network. This env var is used only if `CHAIN_SPEC_PATH` is set | `STANDARD` | master | | |
| `REWARDS_CONTRACT_ADDRESS` | | Emission rewards contract address. This env var is used only if `EMISSION_FORMAT` is set to `POA` | `0xeca443e8e1ab29971a45a9c57a6a9875701698a5` | master | | |

Loading…
Cancel
Save