diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex index 16b881793f..5917b953bb 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex @@ -19,6 +19,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do alias BlockScoutWeb.AccessHelper alias BlockScoutWeb.Models.TransactionStateHelper alias Explorer.Chain + alias Explorer.Chain.Zkevm.Reader alias Indexer.Fetcher.FirstTraceOnDemand action_fallback(BlockScoutWeb.API.V2.FallbackController) @@ -114,7 +115,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do def zkevm_batch(conn, %{"batch_number" => batch_number} = _params) do transactions = batch_number - |> Chain.zkevm_batch_transactions(api?: true) + |> Reader.batch_transactions(api?: true) |> Enum.map(fn tx -> tx.hash end) |> Chain.hashes_to_transactions(api?: true, necessity_by_association: @transaction_necessity_by_association) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/zkevm_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/zkevm_controller.ex index bbda1deaa5..b81cdea24b 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/zkevm_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/api/v2/zkevm_controller.ex @@ -9,6 +9,7 @@ defmodule BlockScoutWeb.API.V2.ZkevmController do ] alias Explorer.Chain + alias Explorer.Chain.Zkevm.Reader action_fallback(BlockScoutWeb.API.V2.FallbackController) @@ -24,7 +25,7 @@ defmodule BlockScoutWeb.API.V2.ZkevmController do def batch(conn, %{"batch_number" => batch_number} = _params) do {:ok, batch} = - Chain.zkevm_batch( + Reader.batch( batch_number, necessity_by_association: @batch_necessity_by_association, api?: true @@ -47,7 +48,7 @@ defmodule BlockScoutWeb.API.V2.ZkevmController do |> paging_options() |> Keyword.put(:necessity_by_association, @batches_necessity_by_association) |> Keyword.put(:api?, true) - |> Chain.zkevm_batches() + |> Reader.batches() |> split_list_by_page() next_page_params = next_page_params(next_page, batches, params) @@ -72,7 +73,7 @@ defmodule BlockScoutWeb.API.V2.ZkevmController do |> Keyword.put(:necessity_by_association, @batches_necessity_by_association) |> Keyword.put(:api?, true) |> Keyword.put(:confirmed?, true) - |> Chain.zkevm_batches() + |> Reader.batches() conn |> put_status(200) @@ -80,7 +81,7 @@ defmodule BlockScoutWeb.API.V2.ZkevmController do end defp batch_latest_number do - case Chain.zkevm_batch(:latest, api?: true) do + case Reader.batch(:latest, api?: true) do {:ok, batch} -> batch.number {:error, :not_found} -> 0 end diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 44ce782e7f..3bba8d9649 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -67,8 +67,6 @@ defmodule Explorer.Chain do Withdrawal } - alias Explorer.Chain.Zkevm.{BatchTransaction, TransactionBatch} - alias Explorer.Chain.Block.{EmissionReward, Reward} alias Explorer.Chain.Cache.{ @@ -4118,7 +4116,7 @@ defmodule Explorer.Chain do end end - defp join_associations(query, necessity_by_association) when is_map(necessity_by_association) do + def join_associations(query, necessity_by_association) when is_map(necessity_by_association) do Enum.reduce(necessity_by_association, query, fn {association, join}, acc_query -> join_association(acc_query, association, join) end) @@ -6393,32 +6391,6 @@ defmodule Explorer.Chain do ) end - def zkevm_batch(number, options \\ []) - - def zkevm_batch(:latest, options) when is_list(options) do - TransactionBatch - |> order_by(desc: :number) - |> limit(1) - |> select_repo(options).one() - |> case do - nil -> {:error, :not_found} - batch -> {:ok, batch} - end - end - - def zkevm_batch(number, options) when is_list(options) do - necessity_by_association = Keyword.get(options, :necessity_by_association, %{}) - - TransactionBatch - |> where(number: ^number) - |> join_associations(necessity_by_association) - |> select_repo(options).one() - |> case do - nil -> {:error, :not_found} - batch -> {:ok, batch} - end - end - @spec verified_contracts_top(non_neg_integer()) :: [Hash.Address.t()] def verified_contracts_top(limit) do query = diff --git a/apps/explorer/lib/explorer/chain/zkevm/reader.ex b/apps/explorer/lib/explorer/chain/zkevm/reader.ex new file mode 100644 index 0000000000..782cf3f94a --- /dev/null +++ b/apps/explorer/lib/explorer/chain/zkevm/reader.ex @@ -0,0 +1,122 @@ +defmodule Explorer.Chain.Zkevm.Reader do + @moduledoc "Contains read functions for zkevm modules." + + import Ecto.Query, + only: [ + from: 2, + limit: 2, + order_by: 2, + where: 2, + where: 3 + ] + + import Explorer.Chain, only: [select_repo: 1] + + alias Explorer.Chain.Zkevm.{BatchTransaction, LifecycleTransaction, TransactionBatch} + alias Explorer.{Chain, PagingOptions, Repo} + + def batch(number, options \\ []) + + def batch(:latest, options) when is_list(options) do + TransactionBatch + |> order_by(desc: :number) + |> limit(1) + |> select_repo(options).one() + |> case do + nil -> {:error, :not_found} + batch -> {:ok, batch} + end + end + + def batch(number, options) when is_list(options) do + necessity_by_association = Keyword.get(options, :necessity_by_association, %{}) + + TransactionBatch + |> where(number: ^number) + |> Chain.join_associations(necessity_by_association) + |> select_repo(options).one() + |> case do + nil -> {:error, :not_found} + batch -> {:ok, batch} + end + end + + def batches(options \\ []) do + necessity_by_association = Keyword.get(options, :necessity_by_association, %{}) + + base_query = + from(tb in TransactionBatch, + order_by: [desc: tb.number] + ) + + query = + if Keyword.get(options, :confirmed?, false) do + base_query + |> Chain.join_associations(necessity_by_association) + |> where([tb], not is_nil(tb.sequence_id) and tb.sequence_id > 0) + |> limit(10) + else + paging_options = Keyword.get(options, :paging_options, Chain.default_paging_options()) + + base_query + |> Chain.join_associations(necessity_by_association) + |> page_batches(paging_options) + |> limit(^paging_options.page_size) + end + + select_repo(options).all(query) + end + + def batch_transactions(batch_number, options \\ []) do + query = from(bts in BatchTransaction, where: bts.batch_number == ^batch_number) + + select_repo(options).all(query) + end + + def last_verified_batch_number do + query = + from(tb in TransactionBatch, + select: tb.number, + where: not is_nil(tb.verify_id), + order_by: [desc: tb.number], + limit: 1 + ) + + query + |> Repo.one() + |> Kernel.||(0) + end + + def lifecycle_transactions(l1_tx_hashes) do + query = + from( + lt in LifecycleTransaction, + select: {lt.hash, lt.id}, + where: lt.hash in ^l1_tx_hashes + ) + + Repo.all(query, timeout: :infinity) + end + + def next_id do + query = + from(lt in LifecycleTransaction, + select: lt.id, + order_by: [desc: lt.id], + limit: 1 + ) + + last_id = + query + |> Repo.one() + |> Kernel.||(0) + + last_id + 1 + end + + defp page_batches(query, %PagingOptions{key: nil}), do: query + + defp page_batches(query, %PagingOptions{key: {number}}) do + from(tb in query, where: tb.number < ^number) + end +end diff --git a/apps/indexer/lib/indexer/fetcher/zkevm/transaction_batch.ex b/apps/indexer/lib/indexer/fetcher/zkevm/transaction_batch.ex index e259c9313f..c396b64ea6 100644 --- a/apps/indexer/lib/indexer/fetcher/zkevm/transaction_batch.ex +++ b/apps/indexer/lib/indexer/fetcher/zkevm/transaction_batch.ex @@ -8,12 +8,10 @@ defmodule Indexer.Fetcher.Zkevm.TransactionBatch do require Logger - import Ecto.Query - import EthereumJSONRPC, only: [integer_to_quantity: 1, json_rpc: 2, quantity_to_integer: 1] - alias Explorer.{Chain, Repo} - alias Explorer.Chain.Zkevm.{LifecycleTransaction, TransactionBatch} + alias Explorer.Chain + alias Explorer.Chain.Zkevm.Reader @zero_hash "0000000000000000000000000000000000000000000000000000000000000000" @@ -71,7 +69,7 @@ defmodule Indexer.Fetcher.Zkevm.TransactionBatch do {new_state, handle_duration} = if latest_batch_number > prev_latest_batch_number or virtual_batch_number > prev_virtual_batch_number or verified_batch_number > prev_verified_batch_number do - start_batch_number = get_last_verified_batch_number() + 1 + start_batch_number = Reader.last_verified_batch_number() + 1 end_batch_number = latest_batch_number log_message = @@ -111,36 +109,6 @@ defmodule Indexer.Fetcher.Zkevm.TransactionBatch do {:noreply, state} end - defp get_last_verified_batch_number do - query = - from(tb in TransactionBatch, - select: tb.number, - where: not is_nil(tb.verify_id), - order_by: [desc: tb.number], - limit: 1 - ) - - query - |> Repo.one() - |> Kernel.||(0) - end - - defp get_next_id do - query = - from(lt in LifecycleTransaction, - select: lt.id, - order_by: [desc: lt.id], - limit: 1 - ) - - last_id = - query - |> Repo.one() - |> Kernel.||(0) - - last_id + 1 - end - defp handle_batch_range(start_batch_number, end_batch_number, json_rpc_named_arguments, chunk_size) do start_batch_number..end_batch_number |> Enum.chunk_every(chunk_size) @@ -226,24 +194,18 @@ defmodule Indexer.Fetcher.Zkevm.TransactionBatch do l1_tx_hashes = Enum.uniq(sequence_hashes ++ verify_hashes) - query = - from( - lt in LifecycleTransaction, - select: {lt.hash, lt.id}, - where: lt.hash in ^l1_tx_hashes - ) - hash_to_id = - query - |> Repo.all(timeout: :infinity) + l1_tx_hashes + |> Reader.lifecycle_transactions() |> Enum.reduce(%{}, fn {hash, id}, acc -> Map.put(acc, hash.bytes, id) end) {batches_to_import, l2_txs_to_import, l1_txs_to_import, _, _} = responses - |> Enum.reduce({[], [], [], get_next_id(), hash_to_id}, fn res, - {batches, l2_txs, l1_txs, next_id, hash_to_id} = _acc -> + |> Enum.reduce({[], [], [], Reader.next_id(), hash_to_id}, fn res, + {batches, l2_txs, l1_txs, next_id, hash_to_id} = + _acc -> number = quantity_to_integer(Map.get(res.result, "number")) {:ok, timestamp} = DateTime.from_unix(quantity_to_integer(Map.get(res.result, "timestamp"))) l2_transaction_hashes = Map.get(res.result, "transactions")