Add last output root size counter (#9532)

pull/9638/head
Maxim Filonov 8 months ago committed by GitHub
parent 0eb7501211
commit 208192d918
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 38
      apps/block_scout_web/lib/block_scout_web/controllers/api/v2/stats_controller.ex
  3. 1
      apps/explorer/config/runtime/test.exs
  4. 1
      apps/explorer/lib/explorer/application.ex
  5. 6
      apps/explorer/lib/explorer/chain.ex
  6. 112
      apps/explorer/lib/explorer/counters/last_output_root_size_counter.ex
  7. 47
      apps/explorer/test/explorer/counters/last_output_root_size_counter_test.exs
  8. 30
      apps/explorer/test/support/factory.ex
  9. 5
      config/runtime.exs

@ -5,6 +5,7 @@
### Features
- [#9631](https://github.com/blockscout/blockscout/pull/9631) - Initial support of zksync chain type
- [#9532](https://github.com/blockscout/blockscout/pull/9532) - Add last output root size counter
- [#9490](https://github.com/blockscout/blockscout/pull/9490) - Add blob transaction counter and filter in block view
- [#9486](https://github.com/blockscout/blockscout/pull/9486) - Massive blocks fetcher
- [#9473](https://github.com/blockscout/blockscout/pull/9473) - Add user_op interpretation

@ -3,11 +3,10 @@ defmodule BlockScoutWeb.API.V2.StatsController do
alias BlockScoutWeb.API.V2.Helper
alias BlockScoutWeb.Chain.MarketHistoryChartController
alias EthereumJSONRPC.Variant
alias Explorer.{Chain, Market}
alias Explorer.Chain.Address.Counters
alias Explorer.Chain.Cache.Block, as: BlockCache
alias Explorer.Chain.Cache.{GasPriceOracle, GasUsage, RootstockLockedBTC}
alias Explorer.Chain.Cache.{GasPriceOracle, GasUsage}
alias Explorer.Chain.Cache.Transaction, as: TransactionCache
alias Explorer.Chain.Supply.RSK
alias Explorer.Chain.Transaction.History.TransactionStats
@ -78,7 +77,7 @@ defmodule BlockScoutWeb.API.V2.StatsController do
"tvl" => exchange_rate.tvl_usd,
"network_utilization_percentage" => network_utilization_percentage()
}
|> add_rootstock_locked_btc()
|> add_chain_type_fields()
|> backward_compatibility(conn)
)
end
@ -148,15 +147,6 @@ defmodule BlockScoutWeb.API.V2.StatsController do
})
end
defp add_rootstock_locked_btc(stats) do
with "rsk" <- Variant.get(),
rootstock_locked_btc when not is_nil(rootstock_locked_btc) <- RootstockLockedBTC.get_locked_value() do
stats |> Map.put("rootstock_locked_btc", rootstock_locked_btc)
else
_ -> stats
end
end
defp backward_compatibility(response, conn) do
case Conn.get_req_header(conn, "updated-gas-oracle") do
["true"] ->
@ -170,4 +160,28 @@ defmodule BlockScoutWeb.API.V2.StatsController do
end)
end
end
case Application.compile_env(:explorer, :chain_type) do
"rsk" ->
defp add_chain_type_fields(response) do
alias Explorer.Chain.Cache.RootstockLockedBTC
case RootstockLockedBTC.get_locked_value() do
rootstock_locked_btc when not is_nil(rootstock_locked_btc) ->
response |> Map.put("rootstock_locked_btc", rootstock_locked_btc)
_ ->
response
end
end
"optimism" ->
defp add_chain_type_fields(response) do
import Explorer.Counters.LastOutputRootSizeCounter, only: [fetch: 1]
response |> Map.put("last_output_root_size", fetch(@api_true))
end
_ ->
defp add_chain_type_fields(response), do: response
end
end

@ -18,6 +18,7 @@ config :explorer, Explorer.Chain.Transaction.History.Historian, enabled: false
config :explorer, Explorer.Market.History.Historian, enabled: false
config :explorer, Explorer.Counters.AddressesCounter, enabled: false, enable_consolidation: false
config :explorer, Explorer.Counters.LastOutputRootSizeCounter, enabled: false, enable_consolidation: false
config :explorer, Explorer.Chain.Cache.ContractsCounter, enabled: false, enable_consolidation: false
config :explorer, Explorer.Chain.Cache.NewContractsCounter, enabled: false, enable_consolidation: false
config :explorer, Explorer.Chain.Cache.VerifiedContractsCounter, enabled: false, enable_consolidation: false

@ -118,6 +118,7 @@ defmodule Explorer.Application do
configure(Explorer.Counters.BlockBurntFeeCounter),
configure(Explorer.Counters.BlockPriorityFeeCounter),
configure(Explorer.Counters.AverageBlockTime),
configure(Explorer.Counters.LastOutputRootSizeCounter),
configure(Explorer.Validator.MetadataProcessor),
configure(Explorer.Tags.AddressTag.Cataloger),
configure(MinMissingBlockNumber),

@ -2120,7 +2120,11 @@ defmodule Explorer.Chain do
select: last_fetched_counter.value
)
select_repo(options).one(query) || Decimal.new(0)
if options[:nullable] do
select_repo(options).one(query)
else
select_repo(options).one(query) || Decimal.new(0)
end
end
defp block_status({number, timestamp}) do

@ -0,0 +1,112 @@
defmodule Explorer.Counters.LastOutputRootSizeCounter do
@moduledoc """
Caches number of transactions in last output root.
It loads the count asynchronously and in a time interval of :cache_period (default to 5 minutes).
"""
use GenServer
import Ecto.Query
alias Explorer.{Chain, Repo}
alias Explorer.Chain.Optimism.OutputRoot
alias Explorer.Chain.Transaction
@counter_type "last_output_root_size_count"
@doc """
Starts a process to periodically update the counter.
"""
@spec start_link(term()) :: GenServer.on_start()
def start_link(_) do
GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
end
@impl true
def init(_args) do
{:ok, %{consolidate?: enable_consolidation?()}, {:continue, :ok}}
end
defp schedule_next_consolidation do
Process.send_after(self(), :consolidate, cache_interval())
end
@impl true
def handle_continue(:ok, %{consolidate?: true} = state) do
consolidate()
schedule_next_consolidation()
{:noreply, state}
end
@impl true
def handle_continue(:ok, state) do
{:noreply, state}
end
@impl true
def handle_info(:consolidate, state) do
consolidate()
schedule_next_consolidation()
{:noreply, state}
end
@doc """
Fetches the value for a `#{@counter_type}` counter type from the `last_fetched_counters` table.
"""
def fetch(options) do
Chain.get_last_fetched_counter(@counter_type, options |> Keyword.put_new(:nullable, true))
end
@doc """
Consolidates the info by populating the `last_fetched_counters` table with the current database information.
"""
def consolidate do
output_root_query =
from(root in OutputRoot,
select: {root.l2_block_number},
order_by: [desc: root.l2_output_index],
limit: 2
)
count =
case output_root_query |> Repo.all() do
[{last_block_number}, {prev_block_number}] ->
query =
from(transaction in Transaction,
where:
not is_nil(transaction.block_hash) and transaction.block_number > ^prev_block_number and
transaction.block_number <= ^last_block_number,
select: count(transaction.hash)
)
Repo.one!(query, timeout: :infinity)
_ ->
nil
end
Chain.upsert_last_fetched_counter(%{
counter_type: @counter_type,
value: count
})
end
@doc """
Returns a boolean that indicates whether consolidation is enabled
In order to choose whether or not to enable the scheduler and the initial
consolidation, change the following Explorer config:
`config :explorer, #{__MODULE__}, enable_consolidation: true`
to:
`config :explorer, #{__MODULE__}, enable_consolidation: false`
"""
def enable_consolidation?, do: Application.get_env(:explorer, __MODULE__)[:enable_consolidation]
defp cache_interval, do: Application.get_env(:explorer, __MODULE__)[:cache_period]
end

@ -0,0 +1,47 @@
defmodule Explorer.Counters.LastOutputRootSizeCounterTest do
use Explorer.DataCase
alias Explorer.Counters.LastOutputRootSizeCounter
if Application.compile_env(:explorer, :chain_type) == "optimism" do
test "populates the cache with the number of transactions in last output root" do
first_block = insert(:block)
insert(:op_output_root, l2_block_number: first_block.number)
second_block = insert(:block, number: first_block.number + 10)
insert(:op_output_root, l2_block_number: second_block.number)
insert(:transaction) |> with_block(first_block)
insert(:transaction) |> with_block(second_block)
insert(:transaction) |> with_block(second_block)
start_supervised!(LastOutputRootSizeCounter)
LastOutputRootSizeCounter.consolidate()
assert LastOutputRootSizeCounter.fetch([]) == Decimal.new("2")
end
test "does not count transactions that are not in output root yet" do
first_block = insert(:block)
insert(:op_output_root, l2_block_number: first_block.number)
second_block = insert(:block, number: first_block.number + 10)
insert(:op_output_root, l2_block_number: second_block.number)
insert(:transaction) |> with_block(first_block)
insert(:transaction) |> with_block(second_block)
insert(:transaction) |> with_block(second_block)
third_block = insert(:block, number: second_block.number + 1)
insert(:transaction) |> with_block(third_block)
insert(:transaction) |> with_block(third_block)
start_supervised!(LastOutputRootSizeCounter)
LastOutputRootSizeCounter.consolidate()
assert LastOutputRootSizeCounter.fetch([]) == Decimal.new("2")
end
end
end

@ -48,6 +48,8 @@ defmodule Explorer.Factory do
Withdrawal
}
alias Explorer.Chain.Optimism.OutputRoot
alias Explorer.SmartContract.Helper
alias Explorer.Tags.{AddressTag, AddressToTag}
alias Explorer.Market.MarketHistory
@ -1116,6 +1118,34 @@ defmodule Explorer.Factory do
}
end
def op_output_root_factory do
%OutputRoot{
l2_output_index: op_output_root_l2_output_index(),
l2_block_number: insert(:block) |> Map.get(:number),
l1_transaction_hash: transaction_hash(),
l1_timestamp: DateTime.utc_now(),
l1_block_number: op_output_root_l1_block_number(),
output_root: op_output_root_hash()
}
end
defp op_output_root_l2_output_index do
sequence("op_output_root_l2_output_index", & &1)
end
defp op_output_root_l1_block_number do
sequence("op_output_root_l1_block_number", & &1)
end
defp op_output_root_hash do
{:ok, hash} =
"op_output_root_hash"
|> sequence(& &1)
|> Hash.Full.cast()
hash
end
def random_bool, do: Enum.random([true, false])
def validator_stability_factory do

@ -291,6 +291,11 @@ config :explorer, Explorer.Counters.AddressTokenUsdSum,
config :explorer, Explorer.Counters.AddressTokenTransfersCounter,
cache_period: ConfigHelper.parse_time_env_var("CACHE_ADDRESS_TOKEN_TRANSFERS_COUNTER_PERIOD", "1h")
config :explorer, Explorer.Counters.LastOutputRootSizeCounter,
enabled: true,
enable_consolidation: true,
cache_period: ConfigHelper.parse_time_env_var("CACHE_OPTIMISM_LAST_OUTPUT_ROOT_SIZE_COUNTER_PERIOD", "5m")
config :explorer, Explorer.ExchangeRates,
store: :ets,
enabled: !disable_exchange_rates?,

Loading…
Cancel
Save