Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Commit

Permalink
fix: proper lookup of confirmed Arbitrum cross-chain messages (blocks…
Browse files Browse the repository at this point in the history
…cout#10322)

* extended types documentation

* proper query for confirmed messages
  • Loading branch information
akolotov committed Jul 4, 2024
1 parent e7fa979 commit bd09249
Show file tree
Hide file tree
Showing 12 changed files with 255 additions and 87 deletions.
29 changes: 22 additions & 7 deletions apps/explorer/lib/explorer/chain/arbitrum/batch_block.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,31 @@ defmodule Explorer.Chain.Arbitrum.BatchBlock do

@required_attrs ~w(batch_number block_number)a

@type t :: %__MODULE__{
batch_number: non_neg_integer(),
batch: %Ecto.Association.NotLoaded{} | L1Batch.t() | nil,
block_number: non_neg_integer(),
confirmation_id: non_neg_integer() | nil,
confirmation_transaction: %Ecto.Association.NotLoaded{} | LifecycleTransaction.t() | nil
@typedoc """
Descriptor of the a rollup block included in an Arbitrum batch:
* `batch_number` - The number of the Arbitrum batch.
* `block_number` - The number of the rollup block.
* `confirmation_id` - The ID of the confirmation L1 transaction from
`Explorer.Chain.LifecycleTransaction`, or `nil` if the
block is not confirmed yet.
"""
@type to_import :: %{
:batch_number => non_neg_integer(),
:block_number => non_neg_integer(),
:confirmation_id => non_neg_integer() | nil
}

@typedoc """
* `batch_number` - The number of the Arbitrum batch.
* `block_number` - The number of the rollup block.
* `confirmation_id` - The ID of the confirmation L1 transaction from
`Explorer.Chain.Arbitrum.LifecycleTransaction`, or `nil`
if the block is not confirmed yet.
* `confirmation_transaction` - An instance of `Explorer.Chain.Arbitrum.LifecycleTransaction`
referenced by `confirmation_id`.
"""
@primary_key {:block_number, :integer, autogenerate: false}
schema "arbitrum_batch_l2_blocks" do
typed_schema "arbitrum_batch_l2_blocks" do
belongs_to(:batch, L1Batch, foreign_key: :batch_number, references: :number, type: :integer)

belongs_to(:confirmation_transaction, LifecycleTransaction,
Expand Down
21 changes: 15 additions & 6 deletions apps/explorer/lib/explorer/chain/arbitrum/batch_transaction.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,24 @@ defmodule Explorer.Chain.Arbitrum.BatchTransaction do

@required_attrs ~w(batch_number tx_hash)a

@type t :: %__MODULE__{
batch_number: non_neg_integer(),
batch: %Ecto.Association.NotLoaded{} | L1Batch.t() | nil,
tx_hash: Hash.t(),
l2_transaction: %Ecto.Association.NotLoaded{} | Transaction.t() | nil
@typedoc """
Descriptor of the a rollup transaction included in an Arbitrum batch:
* `batch_number` - The number of the Arbitrum batch.
* `tx_hash` - The hash of the rollup transaction.
"""
@type to_import :: %{
:batch_number => non_neg_integer(),
:tx_hash => binary()
}

@typedoc """
* `tx_hash` - The hash of the rollup transaction.
* `l2_transaction` - An instance of `Explorer.Chain.Transaction` referenced by `tx_hash`.
* `batch_number` - The number of the Arbitrum batch.
* `batch` - An instance of `Explorer.Chain.Arbitrum.L1Batch` referenced by `batch_number`.
"""
@primary_key false
schema "arbitrum_batch_l2_transactions" do
typed_schema "arbitrum_batch_l2_transactions" do
belongs_to(:batch, L1Batch, foreign_key: :batch_number, references: :number, type: :integer)

belongs_to(:l2_transaction, Transaction,
Expand Down
40 changes: 28 additions & 12 deletions apps/explorer/lib/explorer/chain/arbitrum/l1_batch.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,44 @@ defmodule Explorer.Chain.Arbitrum.L1Batch do

use Explorer.Schema

alias Explorer.Chain.{
Block,
Hash
}
alias Explorer.Chain.Hash

alias Explorer.Chain.Arbitrum.LifecycleTransaction

@required_attrs ~w(number transactions_count start_block end_block before_acc after_acc commitment_id)a

@type t :: %__MODULE__{
@typedoc """
Descriptor of the a L1 batch for Arbitrum rollups:
* `number` - The number of the Arbitrum batch.
* `transactions_count` - The number of transactions in the batch.
* `start_block` - The number of the first block in the batch.
* `end_block` - The number of the last block in the batch.
* `before_acc` - The hash of the state before the batch.
* `after_acc` - The hash of the state after the batch.
* `commitment_id` - The ID of the commitment L1 transaction from Explorer.Chain.LifecycleTransaction.
"""
@type to_import :: %{
number: non_neg_integer(),
transactions_count: non_neg_integer(),
start_block: Block.block_number(),
end_block: Block.block_number(),
before_acc: Hash.t(),
after_acc: Hash.t(),
commitment_id: non_neg_integer(),
commitment_transaction: %Ecto.Association.NotLoaded{} | LifecycleTransaction.t() | nil
start_block: non_neg_integer(),
end_block: non_neg_integer(),
before_acc: binary(),
after_acc: binary(),
commitment_id: non_neg_integer()
}

@typedoc """
* `number` - The number of the Arbitrum batch.
* `transactions_count` - The number of transactions in the batch.
* `start_block` - The number of the first block in the batch.
* `end_block` - The number of the last block in the batch.
* `before_acc` - The hash of the state before the batch.
* `after_acc` - The hash of the state after the batch.
* `commitment_id` - The ID of the commitment L1 transaction from `Explorer.Chain.Arbitrum.LifecycleTransaction`.
* `commitment_transaction` - An instance of `Explorer.Chain.Arbitrum.LifecycleTransaction` referenced by `commitment_id`.
"""
@primary_key {:number, :integer, autogenerate: false}
schema "arbitrum_l1_batches" do
typed_schema "arbitrum_l1_batches" do
field(:transactions_count, :integer)
field(:start_block, :integer)
field(:end_block, :integer)
Expand Down
24 changes: 19 additions & 5 deletions apps/explorer/lib/explorer/chain/arbitrum/l1_execution.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,28 @@ defmodule Explorer.Chain.Arbitrum.L1Execution do

@required_attrs ~w(message_id execution_id)a

@type t :: %__MODULE__{
message_id: non_neg_integer(),
execution_id: non_neg_integer(),
execution_transaction: %Ecto.Association.NotLoaded{} | LifecycleTransaction.t() | nil
@typedoc """
Descriptor of the a L1 execution transaction related to a L2 to L1 message on Arbitrum rollups:
* `message_id` - The ID of the message from `Explorer.Chain.Arbitrum.Message`.
There could be situations when an execution of a message is
discovered, but the message itself is not indexed yet.
* `execution_id` - The ID of the execution transaction from `Explorer.Chain.Arbitrum.LifecycleTransaction`.
"""
@type to_import :: %{
:message_id => non_neg_integer(),
:execution_id => non_neg_integer()
}

@typedoc """
* `message_id` - The ID of the message from `Explorer.Chain.Arbitrum.Message`.
There could be situations when an execution of a message is
discovered, but the message itself is not indexed yet.
* `execution_id` - The ID of the execution transaction from `Explorer.Chain.Arbitrum.LifecycleTransaction`.
* `execution_transaction` - An instance of `Explorer.Chain.Arbitrum.LifecycleTransaction`
referenced by `execution_id`.
"""
@primary_key {:message_id, :integer, autogenerate: false}
schema "arbitrum_l1_executions" do
typed_schema "arbitrum_l1_executions" do
belongs_to(:execution_transaction, LifecycleTransaction,
foreign_key: :execution_id,
references: :id,
Expand Down
40 changes: 28 additions & 12 deletions apps/explorer/lib/explorer/chain/arbitrum/lifecycle_transaction.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
defmodule Explorer.Chain.Arbitrum.LifecycleTransaction do
@moduledoc """
Models an L1 lifecycle transaction for Arbitrum.
Models an L1 lifecycle transaction for Arbitrum. Lifecycle transactions are transactions that change the state of transactions and blocks on Arbitrum rollups.
Changes in the schema should be reflected in the bulk import module:
- Explorer.Chain.Import.Runner.Arbitrum.LifecycleTransactions
Expand All @@ -11,25 +11,41 @@ defmodule Explorer.Chain.Arbitrum.LifecycleTransaction do

use Explorer.Schema

alias Explorer.Chain.{
Block,
Hash
}
alias Explorer.Chain.Hash

alias Explorer.Chain.Arbitrum.{BatchBlock, L1Batch}

@required_attrs ~w(id hash block_number timestamp status)a

@type t :: %__MODULE__{
id: non_neg_integer(),
hash: Hash.t(),
block_number: Block.block_number(),
timestamp: DateTime.t(),
status: String.t()
@typedoc """
Descriptor of the a L1 transaction changing state of transactions and blocks of Arbitrum rollups:
* `id` - The ID of the transaction used for referencing.
* `hash` - The hash of the L1 transaction.
* `block_number` - The number of the L1 block where the transaction is included.
* `timestamp` - The timestamp of the block in which the transaction is included.
* `status` - The status of the transaction: `:unfinalized` or `:finalized`
"""
@type to_import :: %{
:id => non_neg_integer(),
:hash => binary(),
:block_number => non_neg_integer(),
:timestamp => DateTime.t(),
:status => :unfinalized | :finalized
}

@typedoc """
* `id` - The ID of the transaction used for referencing.
* `hash` - The hash of the L1 transaction.
* `block_number` - The number of the L1 block where the transaction is included.
* `timestamp` - The timestamp of the block in which the transaction is included.
* `status` - The status of the transaction: `:unfinalized` or `:finalized`.
* `committed_batches` - A list of `Explorer.Chain.Arbitrum.L1Batch` instances
that are committed by the transaction.
* `confirmed_blocks` - A list of `Explorer.Chain.Arbitrum.BatchBlock` instances
that are confirmed by the transaction.
"""
@primary_key {:id, :integer, autogenerate: false}
schema "arbitrum_lifecycle_l1_transactions" do
typed_schema "arbitrum_lifecycle_l1_transactions" do
field(:hash, Hash.Full)
field(:block_number, :integer)
field(:timestamp, :utc_datetime_usec)
Expand Down
47 changes: 38 additions & 9 deletions apps/explorer/lib/explorer/chain/arbitrum/message.ex
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,56 @@ defmodule Explorer.Chain.Arbitrum.Message do

use Explorer.Schema

alias Explorer.Chain.{Block, Hash}
alias Explorer.Chain.Hash

@optional_attrs ~w(originator_address originating_transaction_hash origination_timestamp originating_transaction_block_number completion_transaction_hash)a

@required_attrs ~w(direction message_id status)a

@allowed_attrs @optional_attrs ++ @required_attrs

@type t :: %__MODULE__{
direction: String.t(),
@typedoc """
Descriptor of the a L1<->L2 message on Arbitrum rollups:
* `direction` - The direction of the message: `:to_l2` or `:from_l2`.
* `message_id` - The ID of the message used for referencing.
* `originator_address` - The address of the message originator. The fields
related to the origination can be `nil` if a completion
transaction is discovered when the originating
transaction is not indexed yet.
* `originating_transaction_hash` - The hash of the originating transaction.
* `origination_timestamp` - The timestamp of the origination.
* `originating_transaction_block_number` - The number of the block where the
originating transaction is included.
* `completion_transaction_hash` - The hash of the completion transaction.
* `status` - The status of the message: `:initiated`, `:sent`, `:confirmed`, `:relayed`
"""
@type to_import :: %{
direction: :to_l2 | :from_l2,
message_id: non_neg_integer(),
originator_address: Hash.Address.t() | nil,
originating_transaction_hash: Hash.t() | nil,
originator_address: binary() | nil,
originating_transaction_hash: binary() | nil,
origination_timestamp: DateTime.t() | nil,
originating_transaction_block_number: Block.block_number() | nil,
completion_transaction_hash: Hash.t() | nil,
status: String.t()
originating_transaction_block_number: non_neg_integer() | nil,
completion_transaction_hash: binary() | nil,
status: :initiated | :sent | :confirmed | :relayed
}

@typedoc """
* `direction` - The direction of the message: `:to_l2` or `:from_l2`.
* `message_id` - The ID of the message used for referencing.
* `originator_address` - The address of the message originator. The fields
related to the origination can be `nil` if a completion
transaction is discovered when the originating
transaction is not indexed yet.
* `originating_transaction_hash` - The hash of the originating transaction.
* `origination_timestamp` - The timestamp of the origination.
* `originating_transaction_block_number` - The number of the block where the
originating transaction is included.
* `completion_transaction_hash` - The hash of the completion transaction.
* `status` - The status of the message: `:initiated`, `:sent`, `:confirmed`, `:relayed`.
"""
@primary_key false
schema "arbitrum_crosslevel_messages" do
typed_schema "arbitrum_crosslevel_messages" do
field(:direction, Ecto.Enum, values: [:to_l2, :from_l2], primary_key: true)
field(:message_id, :integer, primary_key: true)
field(:originator_address, Hash.Address)
Expand Down
30 changes: 23 additions & 7 deletions apps/explorer/lib/explorer/chain/arbitrum/reader.ex
Original file line number Diff line number Diff line change
Expand Up @@ -504,18 +504,24 @@ defmodule Explorer.Chain.Arbitrum.Reader do
end

@doc """
Retrieves all L2-to-L1 messages with the specified status that originated in rollup blocks with numbers not higher than `block_number`.
Retrieves all L2-to-L1 messages with the specified status.
If `block_number` is not `nil`, only messages originating in rollup blocks with
numbers not higher than the specified block are considered. Otherwise, all
messages are considered.
## Parameters
- `status`: The status of the messages to retrieve, such as `:initiated`, `:sent`, `:confirmed`, or `:relayed`.
- `block_number`: The number of a rollup block that limits the messages lookup.
- `status`: The status of the messages to retrieve, such as `:initiated`,
`:sent`, `:confirmed`, or `:relayed`.
- `block_number`: The number of a rollup block that limits the messages lookup,
or `nil`.
## Returns
- Instances of `Explorer.Chain.Arbitrum.Message` corresponding to the criteria, or `[]` if no messages
with the given status are found in the rollup blocks up to the specified number.
- Instances of `Explorer.Chain.Arbitrum.Message` corresponding to the criteria,
or `[]` if no messages with the given status are found.
"""
@spec l2_to_l1_messages(:confirmed | :initiated | :relayed | :sent, FullBlock.block_number()) :: [
Message
@spec l2_to_l1_messages(:confirmed | :initiated | :relayed | :sent, FullBlock.block_number() | nil) :: [
Message.t()
]
def l2_to_l1_messages(status, block_number)
when status in [:initiated, :sent, :confirmed, :relayed] and
Expand All @@ -532,6 +538,16 @@ defmodule Explorer.Chain.Arbitrum.Reader do
Repo.all(query, timeout: :infinity)
end

def l2_to_l1_messages(status, nil) when status in [:initiated, :sent, :confirmed, :relayed] do
query =
from(msg in Message,
where: msg.direction == :from_l2 and msg.status == ^status,
order_by: [desc: msg.message_id]
)

Repo.all(query, timeout: :infinity)
end

@doc """
Retrieves the numbers of the L1 blocks containing the confirmation transactions
bounding the first interval where missed confirmation transactions could be found.
Expand Down
19 changes: 5 additions & 14 deletions apps/indexer/lib/indexer/fetcher/arbitrum/utils/db.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Db do
import Indexer.Fetcher.Arbitrum.Utils.Logging, only: [log_warning: 1]

alias Explorer.{Chain, Repo}
alias Explorer.Chain.Arbitrum
alias Explorer.Chain.Arbitrum.Reader
alias Explorer.Chain.Block, as: FullBlock
alias Explorer.Chain.{Data, Hash, Log}
Expand Down Expand Up @@ -558,21 +559,10 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Db do
database import operation. If no messages with the 'confirmed' status are found by
the specified block number, an empty list is returned.
"""
@spec confirmed_l2_to_l1_messages(FullBlock.block_number()) :: [
%{
direction: :from_l2,
message_id: non_neg_integer(),
originator_address: binary(),
originating_transaction_hash: binary(),
originating_transaction_block_number: FullBlock.block_number(),
completion_transaction_hash: nil,
status: :confirmed
}
]
def confirmed_l2_to_l1_messages(block_number)
when is_integer(block_number) and block_number >= 0 do
@spec confirmed_l2_to_l1_messages() :: [Arbitrum.Message.to_import()]
def confirmed_l2_to_l1_messages do
# credo:disable-for-lines:2 Credo.Check.Refactor.PipeChainStart
Reader.l2_to_l1_messages(:confirmed, block_number)
Reader.l2_to_l1_messages(:confirmed, nil)
|> Enum.map(&message_to_map/1)
end

Expand Down Expand Up @@ -739,6 +729,7 @@ defmodule Indexer.Fetcher.Arbitrum.Utils.Db do
|> db_record_to_map(block)
end

@spec message_to_map(Arbitrum.Message.t()) :: Arbitrum.Message.to_import()
defp message_to_map(message) do
[
:direction,
Expand Down
Loading

0 comments on commit bd09249

Please sign in to comment.