|
|
|
@ -44,6 +44,8 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do |
|
|
|
|
# keccak256("SequencerBatchDelivered(uint256,bytes32,bytes32,bytes32,uint256,(uint64,uint64,uint64,uint64),uint8)") |
|
|
|
|
@event_sequencer_batch_delivered "0x7394f4a19a13c7b92b5bb71033245305946ef78452f7b4986ac1390b5df4ebd7" |
|
|
|
|
|
|
|
|
|
@max_depth_for_safe_block 1000 |
|
|
|
|
|
|
|
|
|
@doc """ |
|
|
|
|
Discovers and imports new batches of rollup transactions within the current L1 block range. |
|
|
|
|
|
|
|
|
@ -91,7 +93,11 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do |
|
|
|
|
:node_interface_address => binary(), |
|
|
|
|
optional(any()) => any() |
|
|
|
|
}, |
|
|
|
|
:data => %{:new_batches_start_block => non_neg_integer(), optional(any()) => any()}, |
|
|
|
|
:data => %{ |
|
|
|
|
:new_batches_start_block => non_neg_integer(), |
|
|
|
|
:historical_batches_end_block => non_neg_integer(), |
|
|
|
|
optional(any()) => any() |
|
|
|
|
}, |
|
|
|
|
optional(any()) => any() |
|
|
|
|
}) :: {:ok, non_neg_integer()} |
|
|
|
|
def discover_new_batches( |
|
|
|
@ -104,7 +110,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do |
|
|
|
|
new_batches_limit: new_batches_limit, |
|
|
|
|
node_interface_address: node_interface_address |
|
|
|
|
}, |
|
|
|
|
data: %{new_batches_start_block: start_block} |
|
|
|
|
data: %{new_batches_start_block: start_block, historical_batches_end_block: historical_batches_end_block} |
|
|
|
|
} = _state |
|
|
|
|
) do |
|
|
|
|
# Requesting the "latest" block instead of "safe" allows to catch new batches |
|
|
|
@ -116,21 +122,62 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do |
|
|
|
|
Rpc.get_resend_attempts() |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
{safe_chain_block, _} = IndexerHelper.get_safe_block(l1_rpc_config.json_rpc_named_arguments) |
|
|
|
|
|
|
|
|
|
# max() cannot be used here since l1_rpc_config.logs_block_range must not |
|
|
|
|
# be taken into account to identify if it is L3 or not |
|
|
|
|
safe_block = |
|
|
|
|
if safe_chain_block < latest_block + 1 - @max_depth_for_safe_block do |
|
|
|
|
# The case of L3, the safe block is too far behind the latest block, |
|
|
|
|
# therefore it is assumed that there is no so deep re-orgs there. |
|
|
|
|
latest_block + 1 - min(@max_depth_for_safe_block, l1_rpc_config.logs_block_range) |
|
|
|
|
else |
|
|
|
|
safe_chain_block |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
# It is necessary to re-visit some amount of the previous blocks to ensure that |
|
|
|
|
# no batches are missed due to reorgs. The amount of blocks to re-visit depends |
|
|
|
|
# either on the current safe block but must not exceed @max_depth_for_safe_block |
|
|
|
|
# (or L1 RPC max block range for getting logs) since on L3 chains the safe block |
|
|
|
|
# could be too far behind the latest block. |
|
|
|
|
# At the same time it does not make sense to re-visit blocks that will be |
|
|
|
|
# re-visited by the historical batches discovery process. |
|
|
|
|
# If the new batches discovery process does not reach the chain head previously |
|
|
|
|
# no need to re-visit the blocks. |
|
|
|
|
safe_start_block = max(min(start_block, safe_block), historical_batches_end_block + 1) |
|
|
|
|
|
|
|
|
|
end_block = min(start_block + l1_rpc_config.logs_block_range - 1, latest_block) |
|
|
|
|
|
|
|
|
|
if start_block <= end_block do |
|
|
|
|
log_info("Block range for new batches discovery: #{start_block}..#{end_block}") |
|
|
|
|
if safe_start_block <= end_block do |
|
|
|
|
log_info("Block range for new batches discovery: #{safe_start_block}..#{end_block}") |
|
|
|
|
|
|
|
|
|
discover( |
|
|
|
|
sequencer_inbox_address, |
|
|
|
|
start_block, |
|
|
|
|
end_block, |
|
|
|
|
new_batches_limit, |
|
|
|
|
messages_to_blocks_shift, |
|
|
|
|
l1_rpc_config, |
|
|
|
|
node_interface_address, |
|
|
|
|
rollup_rpc_config |
|
|
|
|
) |
|
|
|
|
# Since with taking the safe block into account, the range safe_start_block..end_block |
|
|
|
|
# could be larger than L1 RPC max block range for getting logs, it is necessary to |
|
|
|
|
# divide the range into the chunks |
|
|
|
|
safe_start_block |
|
|
|
|
|> Stream.unfold(fn |
|
|
|
|
current when current > end_block -> |
|
|
|
|
nil |
|
|
|
|
|
|
|
|
|
current -> |
|
|
|
|
next = min(current + l1_rpc_config.logs_block_range - 1, end_block) |
|
|
|
|
{current, next + 1} |
|
|
|
|
end) |
|
|
|
|
|> Stream.each(fn chunk_start -> |
|
|
|
|
chunk_end = min(chunk_start + l1_rpc_config.logs_block_range - 1, end_block) |
|
|
|
|
|
|
|
|
|
discover( |
|
|
|
|
sequencer_inbox_address, |
|
|
|
|
chunk_start, |
|
|
|
|
chunk_end, |
|
|
|
|
new_batches_limit, |
|
|
|
|
messages_to_blocks_shift, |
|
|
|
|
l1_rpc_config, |
|
|
|
|
node_interface_address, |
|
|
|
|
rollup_rpc_config |
|
|
|
|
) |
|
|
|
|
end) |
|
|
|
|
|> Stream.run() |
|
|
|
|
|
|
|
|
|
{:ok, end_block} |
|
|
|
|
else |
|
|
|
@ -187,7 +234,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do |
|
|
|
|
:node_interface_address => binary(), |
|
|
|
|
optional(any()) => any() |
|
|
|
|
}, |
|
|
|
|
:data => %{:historical_batches_end_block => any(), optional(any()) => any()}, |
|
|
|
|
:data => %{:historical_batches_end_block => non_neg_integer(), optional(any()) => any()}, |
|
|
|
|
optional(any()) => any() |
|
|
|
|
}) :: {:ok, non_neg_integer()} |
|
|
|
|
def discover_historical_batches( |
|
|
|
@ -226,6 +273,107 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
@doc """ |
|
|
|
|
Inspects and imports missing batches within a specified range of batch numbers. |
|
|
|
|
|
|
|
|
|
This function first finds the missing batches, then determines their |
|
|
|
|
neighboring ranges, maps these ranges to the corresponding L1 block numbers, |
|
|
|
|
and for every such range it retrieves logs representing the |
|
|
|
|
SequencerBatchDelivered events emitted by the SequencerInbox contract. |
|
|
|
|
These logs are processed to identify the batches and their details. The |
|
|
|
|
function then constructs comprehensive data structures for batches, |
|
|
|
|
lifecycle transactions, rollup blocks, and rollup transactions. Additionally, |
|
|
|
|
it identifies L2-to-L1 messages that have been committed within these batches |
|
|
|
|
and updates their status. All discovered and processed data are then imported |
|
|
|
|
into the database. |
|
|
|
|
|
|
|
|
|
## Parameters |
|
|
|
|
- A map containing: |
|
|
|
|
- `config`: Configuration settings including the L1 rollup initialization block, |
|
|
|
|
RPC configurations, SequencerInbox address, a shift for the message |
|
|
|
|
to block number mapping, a limit for new batches discovery, and the |
|
|
|
|
max size of the range for missing batches inspection. |
|
|
|
|
- `data`: Contains the ending batch number for the missing batches inspection. |
|
|
|
|
|
|
|
|
|
## Returns |
|
|
|
|
- `{:ok, start_batch}`: On successful inspection of the given batch range, where |
|
|
|
|
`start_batch` is the calculated starting batch for the inspected range, |
|
|
|
|
indicating the need to consider another batch range in the next iteration of |
|
|
|
|
missing batch inspection. |
|
|
|
|
- `{:ok, lowest_batch}`: If the discovery process has been finished, indicating |
|
|
|
|
that all batches up to the rollup origins have been checked and no further |
|
|
|
|
action is needed. |
|
|
|
|
""" |
|
|
|
|
@spec inspect_for_missing_batches(%{ |
|
|
|
|
:config => %{ |
|
|
|
|
:l1_rollup_init_block => non_neg_integer(), |
|
|
|
|
:l1_rpc => %{ |
|
|
|
|
:json_rpc_named_arguments => EthereumJSONRPC.json_rpc_named_arguments(), |
|
|
|
|
:logs_block_range => non_neg_integer(), |
|
|
|
|
optional(any()) => any() |
|
|
|
|
}, |
|
|
|
|
:l1_sequencer_inbox_address => binary(), |
|
|
|
|
:lowest_batch => non_neg_integer(), |
|
|
|
|
:messages_to_blocks_shift => non_neg_integer(), |
|
|
|
|
:missing_batches_range => non_neg_integer(), |
|
|
|
|
:new_batches_limit => non_neg_integer(), |
|
|
|
|
:node_interface_address => binary(), |
|
|
|
|
:rollup_rpc => %{ |
|
|
|
|
:json_rpc_named_arguments => EthereumJSONRPC.json_rpc_named_arguments(), |
|
|
|
|
:chunk_size => non_neg_integer(), |
|
|
|
|
optional(any()) => any() |
|
|
|
|
}, |
|
|
|
|
optional(any()) => any() |
|
|
|
|
}, |
|
|
|
|
:data => %{:missing_batches_end_batch => non_neg_integer(), optional(any()) => any()}, |
|
|
|
|
optional(any()) => any() |
|
|
|
|
}) :: {:ok, non_neg_integer()} |
|
|
|
|
def inspect_for_missing_batches( |
|
|
|
|
%{ |
|
|
|
|
config: %{ |
|
|
|
|
l1_rpc: l1_rpc_config, |
|
|
|
|
rollup_rpc: rollup_rpc_config, |
|
|
|
|
l1_sequencer_inbox_address: sequencer_inbox_address, |
|
|
|
|
messages_to_blocks_shift: messages_to_blocks_shift, |
|
|
|
|
l1_rollup_init_block: l1_rollup_init_block, |
|
|
|
|
new_batches_limit: new_batches_limit, |
|
|
|
|
missing_batches_range: missing_batches_range, |
|
|
|
|
lowest_batch: lowest_batch, |
|
|
|
|
node_interface_address: node_interface_address |
|
|
|
|
}, |
|
|
|
|
data: %{missing_batches_end_batch: end_batch} |
|
|
|
|
} = _state |
|
|
|
|
) |
|
|
|
|
when not is_nil(lowest_batch) and not is_nil(end_batch) do |
|
|
|
|
# No need to inspect for missing batches below the lowest batch |
|
|
|
|
# since it is assumed that they are picked up by historical batches |
|
|
|
|
# discovery process |
|
|
|
|
if end_batch > lowest_batch do |
|
|
|
|
start_batch = max(lowest_batch, end_batch - missing_batches_range + 1) |
|
|
|
|
|
|
|
|
|
log_info("Batch range for missing batches inspection: #{start_batch}..#{end_batch}") |
|
|
|
|
|
|
|
|
|
l1_block_ranges_for_missing_batches = |
|
|
|
|
Db.get_l1_block_ranges_for_missing_batches(start_batch, end_batch, l1_rollup_init_block - 1) |
|
|
|
|
|
|
|
|
|
unless l1_block_ranges_for_missing_batches == [] do |
|
|
|
|
discover_missing_batches( |
|
|
|
|
sequencer_inbox_address, |
|
|
|
|
l1_block_ranges_for_missing_batches, |
|
|
|
|
new_batches_limit, |
|
|
|
|
messages_to_blocks_shift, |
|
|
|
|
l1_rpc_config, |
|
|
|
|
node_interface_address, |
|
|
|
|
rollup_rpc_config |
|
|
|
|
) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
{:ok, start_batch} |
|
|
|
|
else |
|
|
|
|
{:ok, lowest_batch} |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
# Initiates the discovery process for batches within a specified block range. |
|
|
|
|
# |
|
|
|
|
# Invokes the actual discovery process for new batches by calling `do_discover` |
|
|
|
@ -243,6 +391,24 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do |
|
|
|
|
# |
|
|
|
|
# ## Returns |
|
|
|
|
# - N/A |
|
|
|
|
@spec discover( |
|
|
|
|
binary(), |
|
|
|
|
non_neg_integer(), |
|
|
|
|
non_neg_integer(), |
|
|
|
|
non_neg_integer(), |
|
|
|
|
non_neg_integer(), |
|
|
|
|
%{ |
|
|
|
|
:json_rpc_named_arguments => EthereumJSONRPC.json_rpc_named_arguments(), |
|
|
|
|
:chunk_size => non_neg_integer(), |
|
|
|
|
optional(any()) => any() |
|
|
|
|
}, |
|
|
|
|
binary(), |
|
|
|
|
%{ |
|
|
|
|
:json_rpc_named_arguments => EthereumJSONRPC.json_rpc_named_arguments(), |
|
|
|
|
:chunk_size => non_neg_integer(), |
|
|
|
|
optional(any()) => any() |
|
|
|
|
} |
|
|
|
|
) :: any() |
|
|
|
|
defp discover( |
|
|
|
|
sequencer_inbox_address, |
|
|
|
|
start_block, |
|
|
|
@ -282,6 +448,24 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do |
|
|
|
|
# |
|
|
|
|
# ## Returns |
|
|
|
|
# - N/A |
|
|
|
|
@spec discover_historical( |
|
|
|
|
binary(), |
|
|
|
|
non_neg_integer(), |
|
|
|
|
non_neg_integer(), |
|
|
|
|
non_neg_integer(), |
|
|
|
|
non_neg_integer(), |
|
|
|
|
%{ |
|
|
|
|
:json_rpc_named_arguments => EthereumJSONRPC.json_rpc_named_arguments(), |
|
|
|
|
:chunk_size => non_neg_integer(), |
|
|
|
|
optional(any()) => any() |
|
|
|
|
}, |
|
|
|
|
binary(), |
|
|
|
|
%{ |
|
|
|
|
:json_rpc_named_arguments => EthereumJSONRPC.json_rpc_named_arguments(), |
|
|
|
|
:chunk_size => non_neg_integer(), |
|
|
|
|
optional(any()) => any() |
|
|
|
|
} |
|
|
|
|
) :: any() |
|
|
|
|
defp discover_historical( |
|
|
|
|
sequencer_inbox_address, |
|
|
|
|
start_block, |
|
|
|
@ -304,6 +488,72 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do |
|
|
|
|
) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
# Initiates the discovery process for missing batches within specified block ranges. |
|
|
|
|
# |
|
|
|
|
# This function divides each L1 block range into chunks to call `discover_historical` |
|
|
|
|
# for every chunk to discover missing batches. |
|
|
|
|
# |
|
|
|
|
# ## Parameters |
|
|
|
|
# - `sequencer_inbox_address`: The SequencerInbox contract address. |
|
|
|
|
# - `l1_block_ranges`: The L1 block ranges to look for missing batches. |
|
|
|
|
# - `new_batches_limit`: Limit of new batches to process in one iteration. |
|
|
|
|
# - `messages_to_blocks_shift`: Shift value for message to block number mapping. |
|
|
|
|
# - `l1_rpc_config`: Configuration for L1 RPC calls. |
|
|
|
|
# - `node_interface_address`: The address of the NodeInterface contract on the rollup. |
|
|
|
|
# - `rollup_rpc_config`: Configuration for rollup RPC calls. |
|
|
|
|
# |
|
|
|
|
# ## Returns |
|
|
|
|
# - N/A |
|
|
|
|
@spec discover_missing_batches( |
|
|
|
|
binary(), |
|
|
|
|
[{non_neg_integer(), non_neg_integer()}], |
|
|
|
|
non_neg_integer(), |
|
|
|
|
non_neg_integer(), |
|
|
|
|
%{ |
|
|
|
|
:json_rpc_named_arguments => EthereumJSONRPC.json_rpc_named_arguments(), |
|
|
|
|
:logs_block_range => non_neg_integer(), |
|
|
|
|
:chunk_size => non_neg_integer(), |
|
|
|
|
optional(any()) => any() |
|
|
|
|
}, |
|
|
|
|
binary(), |
|
|
|
|
%{ |
|
|
|
|
:json_rpc_named_arguments => EthereumJSONRPC.json_rpc_named_arguments(), |
|
|
|
|
:chunk_size => non_neg_integer(), |
|
|
|
|
optional(any()) => any() |
|
|
|
|
} |
|
|
|
|
) :: :ok |
|
|
|
|
defp discover_missing_batches( |
|
|
|
|
sequencer_inbox_address, |
|
|
|
|
l1_block_ranges, |
|
|
|
|
new_batches_limit, |
|
|
|
|
messages_to_blocks_shift, |
|
|
|
|
l1_rpc_config, |
|
|
|
|
node_interface_address, |
|
|
|
|
rollup_rpc_config |
|
|
|
|
) do |
|
|
|
|
Enum.each(l1_block_ranges, fn {start_block, end_block} -> |
|
|
|
|
Enum.each(0..div(end_block - start_block, l1_rpc_config.logs_block_range), fn i -> |
|
|
|
|
start_block = start_block + i * l1_rpc_config.logs_block_range |
|
|
|
|
end_block = min(start_block + l1_rpc_config.logs_block_range - 1, end_block) |
|
|
|
|
|
|
|
|
|
log_info("Block range for missing batches discovery: #{start_block}..#{end_block}") |
|
|
|
|
|
|
|
|
|
# `do_discover` is not used here to demonstrate the need to fetch batches |
|
|
|
|
# which are already historical |
|
|
|
|
discover_historical( |
|
|
|
|
sequencer_inbox_address, |
|
|
|
|
start_block, |
|
|
|
|
end_block, |
|
|
|
|
new_batches_limit, |
|
|
|
|
messages_to_blocks_shift, |
|
|
|
|
l1_rpc_config, |
|
|
|
|
node_interface_address, |
|
|
|
|
rollup_rpc_config |
|
|
|
|
) |
|
|
|
|
end) |
|
|
|
|
end) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
# Performs the discovery of new or historical batches within a specified block range, |
|
|
|
|
# processing and importing the relevant data into the database. |
|
|
|
|
# |
|
|
|
@ -346,7 +596,7 @@ defmodule Indexer.Fetcher.Arbitrum.Workers.NewBatches do |
|
|
|
|
:chunk_size => non_neg_integer(), |
|
|
|
|
optional(any()) => any() |
|
|
|
|
} |
|
|
|
|
) :: :ok |
|
|
|
|
) :: any() |
|
|
|
|
defp do_discover( |
|
|
|
|
sequencer_inbox_address, |
|
|
|
|
start_block, |
|
|
|
|