Add FetchValidatorInfoOnDemand fetcher

pull/6933/head
Nikita Pozdniakov 2 years ago
parent fa320dc3ec
commit 8780fdfdac
No known key found for this signature in database
GPG Key ID: F344106F9804FE5F
  1. 4
      apps/explorer/config/config.exs
  2. 3
      apps/explorer/lib/explorer/application.ex
  3. 46
      apps/explorer/lib/explorer/application/constants.ex
  4. 2
      apps/explorer/lib/explorer/chain.ex
  5. 37
      apps/explorer/lib/explorer/chain/block/reward.ex
  6. 23
      apps/explorer/lib/explorer/chain/fetcher/check_bytecode_matching_on_demand.ex
  7. 69
      apps/explorer/lib/explorer/chain/fetcher/fetch_validator_info_on_demand.ex
  8. 56
      apps/explorer/lib/explorer/chain/validator.ex
  9. 21
      apps/explorer/priv/repo/migrations/20230214104917_add_validators_and_constants_tables.exs
  10. 19
      apps/indexer/lib/indexer/fetcher/first_trace_on_demand.ex

@ -106,7 +106,9 @@ config :explorer, Explorer.Counters.BlockPriorityFeeCounter,
config :explorer, Explorer.TokenTransferTokenIdMigration.Supervisor, enabled: true config :explorer, Explorer.TokenTransferTokenIdMigration.Supervisor, enabled: true
config :explorer, Explorer.Chain.Checker.CheckBytecodeMatchingOnDemand, enabled: true config :explorer, Explorer.Chain.Fetcher.CheckBytecodeMatchingOnDemand, enabled: true
config :explorer, Explorer.Chain.Fetcher.FetchValidatorInfoOnDemand, enabled: true
config :explorer, Explorer.Chain.Cache.GasUsage, config :explorer, Explorer.Chain.Cache.GasUsage,
enabled: System.get_env("CACHE_ENABLE_TOTAL_GAS_USAGE_COUNTER") == "true" enabled: System.get_env("CACHE_ENABLE_TOTAL_GAS_USAGE_COUNTER") == "true"

@ -109,7 +109,8 @@ defmodule Explorer.Application do
configure(Explorer.Tags.AddressTag.Cataloger), configure(Explorer.Tags.AddressTag.Cataloger),
configure(MinMissingBlockNumber), configure(MinMissingBlockNumber),
configure(TokenTransferTokenIdMigration.Supervisor), configure(TokenTransferTokenIdMigration.Supervisor),
configure(Explorer.Chain.Checker.CheckBytecodeMatchingOnDemand) configure(Explorer.Chain.Fetcher.CheckBytecodeMatchingOnDemand),
configure(Explorer.Chain.Fetcher.FetchValidatorInfoOnDemand)
] ]
|> List.flatten() |> List.flatten()
end end

@ -0,0 +1,46 @@
defmodule Explorer.Application.Constants do
@moduledoc """
Tracks some kv info
"""
use Explorer.Schema
alias Explorer.Repo
@keys_manager_contract_address_key "keys_manager_contract_address"
@primary_key false
schema "constants" do
field(:key, :string, primary_key: true)
field(:value, :string)
timestamps()
end
def changeset(attrs) do
%__MODULE__{}
|> changeset(attrs)
end
@required_attrs ~w(key value)a
def changeset(%__MODULE__{} = constant, attrs) do
constant
|> cast(attrs, @required_attrs)
|> validate_required(@required_attrs)
end
def get_constant_by_key(key) do
__MODULE__
|> where([constant], constant.key == ^key)
|> Repo.one()
end
def insert_keys_manager_contract_address(value) do
%{key: @keys_manager_contract_address_key, value: value}
|> changeset()
|> Repo.insert!()
end
def get_keys_manager_contract_address do
get_constant_by_key(@keys_manager_contract_address_key)
end
end

@ -80,7 +80,7 @@ defmodule Explorer.Chain do
alias Explorer.Chain.Import.Runner alias Explorer.Chain.Import.Runner
alias Explorer.Chain.InternalTransaction.{CallType, Type} alias Explorer.Chain.InternalTransaction.{CallType, Type}
alias Explorer.Chain.Checker.CheckBytecodeMatchingOnDemand alias Explorer.Chain.Fetcher.CheckBytecodeMatchingOnDemand
alias Explorer.Counters.{ alias Explorer.Counters.{
AddressesCounter, AddressesCounter,

@ -5,9 +5,11 @@ defmodule Explorer.Chain.Block.Reward do
use Explorer.Schema use Explorer.Schema
alias Explorer.Application.Constants
alias Explorer.Chain alias Explorer.Chain
alias Explorer.Chain.Block.Reward.AddressType alias Explorer.Chain.Block.Reward.AddressType
alias Explorer.Chain.{Address, Block, Hash, Wei} alias Explorer.Chain.{Address, Block, Hash, Wei, Validator}
alias Explorer.Chain.Fetcher.FetchValidatorInfoOnDemand
alias Explorer.{PagingOptions, Repo} alias Explorer.{PagingOptions, Repo}
alias Explorer.SmartContract.Reader alias Explorer.SmartContract.Reader
@ -152,6 +154,39 @@ defmodule Explorer.Chain.Block.Reward do
end end
end end
def get_validator_payout_key_by_mining_from_db(mining_key) do
contract_address_from_db = Constants.get_keys_manager_contract_address()
contract_address_from_env =
Application.get_env(:explorer, Explorer.Chain.Block.Reward, %{})[:keys_manager_contract_address]
cond do
is_nil(contract_address_from_env) ->
%{is_validator: nil, payout_key: mining_key}
is_nil(contract_address_from_db) ->
FetchValidatorInfoOnDemand.trigger_fetch(mining_key)
%{is_validator: nil, payout_key: mining_key}
contract_address_from_db.value |> String.downcase() == contract_address_from_env |> String.downcase() ->
FetchValidatorInfoOnDemand.trigger_fetch(mining_key)
validator = Validator.get_validator_by_address_hash(mining_key)
is_validator = validator && validator.is_validator
with {:is_validator, true} <- {:is_validator, is_validator},
false <- is_nil(validator.payout_key_hash) do
%{is_validator: is_validator, payout_key: validator.payout_key_hash}
else
_ ->
%{is_validator: is_validator, payout_key: mining_key}
end
true ->
FetchValidatorInfoOnDemand.trigger_fetch(mining_key)
%{is_validator: nil, payout_key: mining_key}
end
end
def get_validator_payout_key_by_mining(mining_key) do def get_validator_payout_key_by_mining(mining_key) do
is_validator = is_validator(mining_key) is_validator = is_validator(mining_key)

@ -1,4 +1,4 @@
defmodule Explorer.Chain.Checker.CheckBytecodeMatchingOnDemand do defmodule Explorer.Chain.Fetcher.CheckBytecodeMatchingOnDemand do
@moduledoc """ @moduledoc """
On demand checker if bytecode written in BlockScout's DB equals to bytecode stored on node (only for verified contracts) On demand checker if bytecode written in BlockScout's DB equals to bytecode stored on node (only for verified contracts)
""" """
@ -8,16 +8,10 @@ defmodule Explorer.Chain.Checker.CheckBytecodeMatchingOnDemand do
alias Ecto.Association.NotLoaded alias Ecto.Association.NotLoaded
alias Ecto.Changeset alias Ecto.Changeset
alias Explorer.Repo alias Explorer.Repo
alias Explorer.Counters.Helper
require Logger
# seconds # seconds
@check_bytecode_interval 86_400 @check_bytecode_interval 86_400
# cache needed to keep track of transactions which are already being processed
@cache_name :bytecode_matching_processing
def trigger_check(_address, %NotLoaded{}) do def trigger_check(_address, %NotLoaded{}) do
:ignore :ignore
end end
@ -62,8 +56,6 @@ defmodule Explorer.Chain.Checker.CheckBytecodeMatchingOnDemand do
:error :error
end end
end end
:ets.delete(@cache_name, to_string(address.hash))
end end
def start_link(_) do def start_link(_) do
@ -72,23 +64,12 @@ defmodule Explorer.Chain.Checker.CheckBytecodeMatchingOnDemand do
@impl true @impl true
def init(opts) do def init(opts) do
Helper.create_cache_table(@cache_name)
{:ok, opts} {:ok, opts}
end end
@impl true @impl true
def handle_cast({:check, address}, state) do def handle_cast({:check, address}, state) do
hash_string = to_string(address.hash) check_bytecode_matching(address)
case Helper.fetch_from_cache(hash_string, @cache_name) do
0 ->
:ets.insert(@cache_name, {hash_string, 1})
check_bytecode_matching(address)
1 ->
:ignore
end
{:noreply, state} {:noreply, state}
end end

@ -0,0 +1,69 @@
defmodule Explorer.Chain.Fetcher.FetchValidatorInfoOnDemand do
@moduledoc """
On demand fetcher info about validator
"""
use GenServer
alias Explorer.Application.Constants
alias Explorer.Chain.Block.Reward
alias Explorer.Chain.Cache.BlockNumber
alias Explorer.Chain.Validator
def trigger_fetch(address_hash) do
GenServer.cast(__MODULE__, {:fetch_or_update, address_hash})
end
defp actualize_validator_info(address_hash) do
contract_address_from_db = Constants.get_keys_manager_contract_address()
contract_address_from_env =
Application.get_env(:explorer, Explorer.Chain.Block.Reward, %{})[:keys_manager_contract_address]
cond do
is_nil(contract_address_from_env) ->
:ignore
is_nil(contract_address_from_db) ->
Validator.drop_all_validators()
Constants.insert_keys_manager_contract_address(contract_address_from_env)
fetch_and_store_validator_info(address_hash)
String.downcase(contract_address_from_db.value) == contract_address_from_env |> String.downcase() ->
fetch_and_store_validator_info(address_hash)
true ->
Validator.drop_all_validators()
Constants.insert_keys_manager_contract_address(contract_address_from_env)
fetch_and_store_validator_info(address_hash)
end
end
defp fetch_and_store_validator_info(validator_address) do
validator = Validator.get_validator_by_address_hash(validator_address)
%{is_validator: is_validator, payout_key: payout_key} = Reward.get_validator_payout_key_by_mining(validator_address)
Validator.insert_or_update(validator, %{
address_hash: validator_address,
is_validator: is_validator,
payout_key_hash: payout_key,
last_block_updated_at: BlockNumber.get_max()
})
end
def start_link(_) do
GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
end
@impl true
def init(opts) do
{:ok, opts}
end
@impl true
def handle_cast({:fetch_or_update, address_hash}, state) do
actualize_validator_info(address_hash)
{:noreply, state}
end
end

@ -0,0 +1,56 @@
defmodule Explorer.Chain.Validator do
@moduledoc """
Tracks info about POA validator
"""
use Explorer.Schema
alias Explorer.Chain.Hash.Address
alias Explorer.Repo
@primary_key false
schema "validators" do
field(:address_hash, Address, primary_key: true)
field(:is_validator, :boolean)
field(:payout_key_hash, Address)
field(:last_block_updated_at, :integer)
timestamps()
end
def insert_or_update(nil, attrs) do
attrs
|> changeset()
|> Repo.insert()
end
def insert_or_update(validator, attrs) do
validator
|> changeset(attrs)
|> Repo.update()
end
def changeset(attrs) do
%__MODULE__{}
|> changeset(attrs)
end
@required_attrs ~w(address_hash)a
@optional_attrs ~w(is_validator payout_key_hash last_block_updated_at)a
def changeset(%__MODULE__{} = constant, attrs) do
constant
|> cast(attrs, @required_attrs ++ @optional_attrs)
|> validate_required(@required_attrs)
|> unique_constraint(:address_hash)
end
def get_validator_by_address_hash(address_hash) do
__MODULE__
|> where([validator], validator.address_hash == ^address_hash)
|> Repo.one()
end
def drop_all_validators() do
__MODULE__
|> Repo.delete_all()
end
end

@ -0,0 +1,21 @@
defmodule Explorer.Repo.Migrations.AddValidatorsAndConstantsTables do
use Ecto.Migration
def change do
create table(:constants, primary_key: false) do
add(:key, :string, primary_key: true, null: false)
add(:value, :string)
timestamps()
end
create table(:validators, primary_key: false) do
add(:address_hash, :bytea, primary_key: true, null: false)
add(:is_validator, :boolean)
add(:payout_key_hash, :bytea)
add(:last_block_updated_at, :bigint)
timestamps()
end
end
end

@ -9,13 +9,9 @@ defmodule Indexer.Fetcher.FirstTraceOnDemand do
alias Explorer.Chain alias Explorer.Chain
alias Explorer.Chain.Import alias Explorer.Chain.Import
alias Explorer.Chain.Import.Runner.InternalTransactions alias Explorer.Chain.Import.Runner.InternalTransactions
alias Explorer.Counters.Helper
require Logger require Logger
# cache needed to keep track of transactions which are already being processed
@cache_name :first_traces_processing
def trigger_fetch(transaction) do def trigger_fetch(transaction) do
GenServer.cast(__MODULE__, {:fetch, transaction}) GenServer.cast(__MODULE__, {:fetch, transaction})
end end
@ -52,8 +48,6 @@ defmodule Indexer.Fetcher.FirstTraceOnDemand do
:ignore -> :ignore ->
:ignore :ignore
end end
:ets.delete(@cache_name, hash_string)
end end
def start_link([init_opts, server_opts]) do def start_link([init_opts, server_opts]) do
@ -62,23 +56,12 @@ defmodule Indexer.Fetcher.FirstTraceOnDemand do
@impl true @impl true
def init(json_rpc_named_arguments) do def init(json_rpc_named_arguments) do
Helper.create_cache_table(@cache_name)
{:ok, %{json_rpc_named_arguments: json_rpc_named_arguments}} {:ok, %{json_rpc_named_arguments: json_rpc_named_arguments}}
end end
@impl true @impl true
def handle_cast({:fetch, transaction}, state) do def handle_cast({:fetch, transaction}, state) do
hash_string = to_string(transaction.hash) fetch_first_trace(transaction, state)
case Helper.fetch_from_cache(hash_string, @cache_name) do
0 ->
:ets.insert(@cache_name, {hash_string, 1})
fetch_first_trace(transaction, state)
1 ->
:ignore
end
{:noreply, state} {:noreply, state}
end end

Loading…
Cancel
Save