parent
2949f02189
commit
ce06032aba
@ -0,0 +1,141 @@ |
|||||||
|
defmodule Indexer.Fetcher.RollupL1ReorgMonitor do |
||||||
|
@moduledoc """ |
||||||
|
A module to catch L1 reorgs and notify a rollup module about it. |
||||||
|
""" |
||||||
|
|
||||||
|
use GenServer |
||||||
|
use Indexer.Fetcher |
||||||
|
|
||||||
|
require Logger |
||||||
|
|
||||||
|
alias Indexer.{BoundQueue, Helper} |
||||||
|
|
||||||
|
@fetcher_name :rollup_l1_reorg_monitor |
||||||
|
|
||||||
|
def child_spec(start_link_arguments) do |
||||||
|
spec = %{ |
||||||
|
id: __MODULE__, |
||||||
|
start: {__MODULE__, :start_link, start_link_arguments}, |
||||||
|
restart: :transient, |
||||||
|
type: :worker |
||||||
|
} |
||||||
|
|
||||||
|
Supervisor.child_spec(spec, []) |
||||||
|
end |
||||||
|
|
||||||
|
def start_link(args, gen_server_options \\ []) do |
||||||
|
GenServer.start_link(__MODULE__, args, Keyword.put_new(gen_server_options, :name, __MODULE__)) |
||||||
|
end |
||||||
|
|
||||||
|
@impl GenServer |
||||||
|
def init(_args) do |
||||||
|
Logger.metadata(fetcher: @fetcher_name) |
||||||
|
|
||||||
|
modules_can_use_reorg_monitor = [ |
||||||
|
Indexer.Fetcher.Shibarium.L1, |
||||||
|
Indexer.Fetcher.Zkevm.BridgeL1 |
||||||
|
] |
||||||
|
|
||||||
|
modules_using_reorg_monitor = |
||||||
|
modules_can_use_reorg_monitor |
||||||
|
|> Enum.reject(fn module -> |
||||||
|
is_nil(Application.get_all_env(:indexer)[module][:start_block]) |
||||||
|
end) |
||||||
|
|
||||||
|
cond do |
||||||
|
Enum.count(modules_using_reorg_monitor) > 1 -> |
||||||
|
Logger.error("#{__MODULE__} cannot work for more than one rollup module. Please, check config.") |
||||||
|
:ignore |
||||||
|
|
||||||
|
Enum.empty?(modules_using_reorg_monitor) -> |
||||||
|
# don't start reorg monitor as there is no module which would use it |
||||||
|
:ignore |
||||||
|
|
||||||
|
true -> |
||||||
|
module_using_reorg_monitor = Enum.at(modules_using_reorg_monitor, 0) |
||||||
|
|
||||||
|
l1_rpc = Application.get_all_env(:indexer)[module_using_reorg_monitor][:rpc] |
||||||
|
|
||||||
|
json_rpc_named_arguments = Helper.json_rpc_named_arguments(l1_rpc) |
||||||
|
|
||||||
|
{:ok, block_check_interval, _} = Helper.get_block_check_interval(json_rpc_named_arguments) |
||||||
|
|
||||||
|
Process.send(self(), :reorg_monitor, []) |
||||||
|
|
||||||
|
{:ok, |
||||||
|
%{ |
||||||
|
block_check_interval: block_check_interval, |
||||||
|
json_rpc_named_arguments: json_rpc_named_arguments, |
||||||
|
prev_latest: 0 |
||||||
|
}} |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
@impl GenServer |
||||||
|
def handle_info( |
||||||
|
:reorg_monitor, |
||||||
|
%{ |
||||||
|
block_check_interval: block_check_interval, |
||||||
|
json_rpc_named_arguments: json_rpc_named_arguments, |
||||||
|
prev_latest: prev_latest |
||||||
|
} = state |
||||||
|
) do |
||||||
|
{:ok, latest} = Helper.get_block_number_by_tag("latest", json_rpc_named_arguments, 100_000_000) |
||||||
|
|
||||||
|
if latest < prev_latest do |
||||||
|
Logger.warning("Reorg detected: previous latest block ##{prev_latest}, current latest block ##{latest}.") |
||||||
|
reorg_block_push(latest) |
||||||
|
end |
||||||
|
|
||||||
|
Process.send_after(self(), :reorg_monitor, block_check_interval) |
||||||
|
|
||||||
|
{:noreply, %{state | prev_latest: latest}} |
||||||
|
end |
||||||
|
|
||||||
|
@doc """ |
||||||
|
Pops the number of reorg block from the front of the queue. |
||||||
|
Returns `nil` if the reorg queue is empty. |
||||||
|
""" |
||||||
|
@spec reorg_block_pop() :: non_neg_integer() | nil |
||||||
|
def reorg_block_pop do |
||||||
|
table_name = reorg_table_name(@fetcher_name) |
||||||
|
|
||||||
|
case BoundQueue.pop_front(reorg_queue_get(table_name)) do |
||||||
|
{:ok, {block_number, updated_queue}} -> |
||||||
|
:ets.insert(table_name, {:queue, updated_queue}) |
||||||
|
block_number |
||||||
|
|
||||||
|
{:error, :empty} -> |
||||||
|
nil |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
defp reorg_block_push(block_number) do |
||||||
|
table_name = reorg_table_name(@fetcher_name) |
||||||
|
{:ok, updated_queue} = BoundQueue.push_back(reorg_queue_get(table_name), block_number) |
||||||
|
:ets.insert(table_name, {:queue, updated_queue}) |
||||||
|
end |
||||||
|
|
||||||
|
defp reorg_queue_get(table_name) do |
||||||
|
if :ets.whereis(table_name) == :undefined do |
||||||
|
:ets.new(table_name, [ |
||||||
|
:set, |
||||||
|
:named_table, |
||||||
|
:public, |
||||||
|
read_concurrency: true, |
||||||
|
write_concurrency: true |
||||||
|
]) |
||||||
|
end |
||||||
|
|
||||||
|
with info when info != :undefined <- :ets.info(table_name), |
||||||
|
[{_, value}] <- :ets.lookup(table_name, :queue) do |
||||||
|
value |
||||||
|
else |
||||||
|
_ -> %BoundQueue{} |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
defp reorg_table_name(fetcher_name) do |
||||||
|
:"#{fetcher_name}#{:_reorgs}" |
||||||
|
end |
||||||
|
end |
Loading…
Reference in new issue