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.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,
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(MinMissingBlockNumber),
configure(TokenTransferTokenIdMigration.Supervisor),
configure(Explorer.Chain.Checker.CheckBytecodeMatchingOnDemand)
configure(Explorer.Chain.Fetcher.CheckBytecodeMatchingOnDemand),
configure(Explorer.Chain.Fetcher.FetchValidatorInfoOnDemand)
]
|> List.flatten()
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.InternalTransaction.{CallType, Type}
alias Explorer.Chain.Checker.CheckBytecodeMatchingOnDemand
alias Explorer.Chain.Fetcher.CheckBytecodeMatchingOnDemand
alias Explorer.Counters.{
AddressesCounter,

@ -5,9 +5,11 @@ defmodule Explorer.Chain.Block.Reward do
use Explorer.Schema
alias Explorer.Application.Constants
alias Explorer.Chain
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.SmartContract.Reader
@ -152,6 +154,39 @@ defmodule Explorer.Chain.Block.Reward do
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
is_validator = is_validator(mining_key)

@ -1,4 +1,4 @@
defmodule Explorer.Chain.Checker.CheckBytecodeMatchingOnDemand do
defmodule Explorer.Chain.Fetcher.CheckBytecodeMatchingOnDemand do
@moduledoc """
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.Changeset
alias Explorer.Repo
alias Explorer.Counters.Helper
require Logger
# seconds
@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
:ignore
end
@ -62,8 +56,6 @@ defmodule Explorer.Chain.Checker.CheckBytecodeMatchingOnDemand do
:error
end
end
:ets.delete(@cache_name, to_string(address.hash))
end
def start_link(_) do
@ -72,23 +64,12 @@ defmodule Explorer.Chain.Checker.CheckBytecodeMatchingOnDemand do
@impl true
def init(opts) do
Helper.create_cache_table(@cache_name)
{:ok, opts}
end
@impl true
def handle_cast({:check, address}, state) do
hash_string = to_string(address.hash)
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
check_bytecode_matching(address)
{:noreply, state}
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.Import
alias Explorer.Chain.Import.Runner.InternalTransactions
alias Explorer.Counters.Helper
require Logger
# cache needed to keep track of transactions which are already being processed
@cache_name :first_traces_processing
def trigger_fetch(transaction) do
GenServer.cast(__MODULE__, {:fetch, transaction})
end
@ -52,8 +48,6 @@ defmodule Indexer.Fetcher.FirstTraceOnDemand do
:ignore ->
:ignore
end
:ets.delete(@cache_name, hash_string)
end
def start_link([init_opts, server_opts]) do
@ -62,23 +56,12 @@ defmodule Indexer.Fetcher.FirstTraceOnDemand do
@impl true
def init(json_rpc_named_arguments) do
Helper.create_cache_table(@cache_name)
{:ok, %{json_rpc_named_arguments: json_rpc_named_arguments}}
end
@impl true
def handle_cast({:fetch, transaction}, state) do
hash_string = to_string(transaction.hash)
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
fetch_first_trace(transaction, state)
{:noreply, state}
end

Loading…
Cancel
Save