diff --git a/apps/explorer/lib/explorer/chain/beacon/reader.ex b/apps/explorer/lib/explorer/chain/beacon/reader.ex index b40ab894ab..c90e2f0847 100644 --- a/apps/explorer/lib/explorer/chain/beacon/reader.ex +++ b/apps/explorer/lib/explorer/chain/beacon/reader.ex @@ -101,7 +101,7 @@ defmodule Explorer.Chain.Beacon.Reader do """ @spec stream_missed_blob_transactions_timestamps( initial :: accumulator, - reducer :: (entry :: Hash.Address.t(), accumulator -> accumulator), + reducer :: (entry :: DateTime.t(), accumulator -> accumulator), min_block :: integer() | nil, max_block :: integer() | nil, options :: [] diff --git a/apps/explorer/test/explorer/chain/beacon/reader_test.exs b/apps/explorer/test/explorer/chain/beacon/reader_test.exs new file mode 100644 index 0000000000..e3f9d07baa --- /dev/null +++ b/apps/explorer/test/explorer/chain/beacon/reader_test.exs @@ -0,0 +1,7 @@ +defmodule Explorer.Chain.Beacon.ReaderTest do + use Explorer.DataCase + + alias Explorer.Chain.Beacon.Reader + + doctest Reader +end diff --git a/apps/explorer/test/support/factory.ex b/apps/explorer/test/support/factory.ex index 5ab4e0dad0..547b568311 100644 --- a/apps/explorer/test/support/factory.ex +++ b/apps/explorer/test/support/factory.ex @@ -1099,16 +1099,12 @@ defmodule Explorer.Factory do end def blob_transaction_factory do - blob = build(:blob) - transaction = build(:transaction) - %BlobTransaction{ - hash: transaction.hash, - transaction: transaction, + hash: insert(:transaction) |> with_block() |> Map.get(:hash), max_fee_per_blob_gas: Decimal.new(1_000_000_000), blob_gas_price: Decimal.new(1_000_000_000), blob_gas_used: Decimal.new(131_072), - blob_versioned_hashes: [blob.hash] + blob_versioned_hashes: [] } end diff --git a/apps/indexer/config/runtime/test.exs b/apps/indexer/config/runtime/test.exs index e2043f6c14..7c9daee034 100644 --- a/apps/indexer/config/runtime/test.exs +++ b/apps/indexer/config/runtime/test.exs @@ -2,6 +2,9 @@ import Config alias EthereumJSONRPC.Variant +config :indexer, Indexer.Fetcher.Beacon.Blob.Supervisor, disabled?: true +config :indexer, Indexer.Fetcher.Beacon.Blob, start_block: 0 + variant = Variant.get() Code.require_file("#{variant}.exs", "#{__DIR__}/../../../explorer/config/test") diff --git a/apps/indexer/lib/indexer/fetcher/beacon/client.ex b/apps/indexer/lib/indexer/fetcher/beacon/client.ex index 4c97d20089..92ac93c60f 100644 --- a/apps/indexer/lib/indexer/fetcher/beacon/client.ex +++ b/apps/indexer/lib/indexer/fetcher/beacon/client.ex @@ -8,7 +8,7 @@ defmodule Indexer.Fetcher.Beacon.Client do @request_error_msg "Error while sending request to beacon rpc" def http_get_request(url) do - case HTTPoison.get(url) do + case Application.get_env(:explorer, :http_adapter).get(url) do {:ok, %Response{body: body, status_code: 200}} -> Jason.decode(body) diff --git a/apps/indexer/test/indexer/block/catchup/bound_interval_supervisor_test.exs b/apps/indexer/test/indexer/block/catchup/bound_interval_supervisor_test.exs index 0d33ad553f..774ebfe3a5 100644 --- a/apps/indexer/test/indexer/block/catchup/bound_interval_supervisor_test.exs +++ b/apps/indexer/test/indexer/block/catchup/bound_interval_supervisor_test.exs @@ -11,7 +11,6 @@ defmodule Indexer.Block.Catchup.BoundIntervalSupervisorTest do alias Indexer.BoundInterval alias Indexer.Block.Catchup alias Indexer.Block.Catchup.MissingRangesCollector - alias Indexer.Fetcher.Beacon.Blob alias Indexer.Fetcher.{ CoinBalance, @@ -33,14 +32,8 @@ defmodule Indexer.Block.Catchup.BoundIntervalSupervisorTest do describe "start_link/1" do setup do - initial_block_ranges = Application.get_env(:indexer, :block_ranges) - initial_blob_disabled = Application.get_env(:indexer, Blob.Supervisor)[:disabled?] - Application.put_env(:indexer, Blob.Supervisor, disabled?: true) - - on_exit(fn -> - Application.put_env(:indexer, :block_ranges, initial_block_ranges) - Application.put_env(:indexer, Blob.Supervisor, disabled?: initial_blob_disabled) - end) + initial_env = Application.get_env(:indexer, :block_ranges) + on_exit(fn -> Application.put_env(:indexer, :block_ranges, initial_env) end) end # See https://github.com/poanetwork/blockscout/issues/597 diff --git a/apps/indexer/test/indexer/block/catchup/fetcher_test.exs b/apps/indexer/test/indexer/block/catchup/fetcher_test.exs index 312304dbd4..93f687907c 100644 --- a/apps/indexer/test/indexer/block/catchup/fetcher_test.exs +++ b/apps/indexer/test/indexer/block/catchup/fetcher_test.exs @@ -13,7 +13,6 @@ defmodule Indexer.Block.Catchup.FetcherTest do alias Indexer.Block alias Indexer.Block.Catchup.Fetcher alias Indexer.Block.Catchup.MissingRangesCollector - alias Indexer.Fetcher.Beacon.Blob alias Indexer.Fetcher.{BlockReward, CoinBalance, InternalTransaction, Token, TokenBalance, UncleBlock} @moduletag capture_log: true @@ -38,14 +37,11 @@ defmodule Indexer.Block.Catchup.FetcherTest do describe "import/1" do setup do - initial_last_block = Application.get_env(:indexer, :last_block) - initial_blob_disabled = Application.get_env(:indexer, Blob.Supervisor)[:disabled?] + configuration = Application.get_env(:indexer, :last_block) Application.put_env(:indexer, :last_block, 0) - Application.put_env(:indexer, Blob.Supervisor, disabled?: true) on_exit(fn -> - Application.put_env(:indexer, :last_block, initial_last_block) - Application.put_env(:indexer, Blob.Supervisor, disabled?: initial_blob_disabled) + Application.put_env(:indexer, :last_block, configuration) end) end @@ -143,13 +139,7 @@ defmodule Indexer.Block.Catchup.FetcherTest do describe "task/1" do setup do initial_env = Application.get_env(:indexer, :block_ranges) - initial_blob_disabled = Application.get_env(:indexer, Blob.Supervisor)[:disabled?] - Application.put_env(:indexer, Blob.Supervisor, disabled?: true) - - on_exit(fn -> - Application.put_env(:indexer, :block_ranges, initial_env) - Application.put_env(:indexer, Blob.Supervisor, disabled?: initial_blob_disabled) - end) + on_exit(fn -> Application.put_env(:indexer, :block_ranges, initial_env) end) end test "ignores fetched beneficiaries with different hash for same number", %{ diff --git a/apps/indexer/test/indexer/block/fetcher_test.exs b/apps/indexer/test/indexer/block/fetcher_test.exs index 54124a1025..0498cfd37e 100644 --- a/apps/indexer/test/indexer/block/fetcher_test.exs +++ b/apps/indexer/test/indexer/block/fetcher_test.exs @@ -11,8 +11,6 @@ defmodule Indexer.Block.FetcherTest do alias Indexer.Block.Fetcher alias Indexer.BufferedTask - alias Indexer.Fetcher.Beacon.Blob - alias Indexer.Fetcher.{ CoinBalance, ContractCode, @@ -62,13 +60,6 @@ defmodule Indexer.Block.FetcherTest do block_fetcher: %Fetcher{json_rpc_named_arguments: json_rpc_named_arguments} ) - initial_blob_disabled = Application.get_env(:indexer, Blob.Supervisor)[:disabled?] - Application.put_env(:indexer, Blob.Supervisor, disabled?: true) - - on_exit(fn -> - Application.put_env(:indexer, Blob.Supervisor, disabled?: initial_blob_disabled) - end) - %{ block_fetcher: %Fetcher{ broadcast: false, diff --git a/apps/indexer/test/indexer/block/realtime/fetcher_test.exs b/apps/indexer/test/indexer/block/realtime/fetcher_test.exs index 9685788d78..9d0459e915 100644 --- a/apps/indexer/test/indexer/block/realtime/fetcher_test.exs +++ b/apps/indexer/test/indexer/block/realtime/fetcher_test.exs @@ -8,7 +8,6 @@ defmodule Indexer.Block.Realtime.FetcherTest do alias Explorer.Chain.{Address, Transaction, Wei} alias Indexer.Block.Catchup.Sequence alias Indexer.Block.Realtime - alias Indexer.Fetcher.Beacon.Blob alias Indexer.Fetcher.{ContractCode, InternalTransaction, ReplacedTransaction, Token, TokenBalance, UncleBlock} @moduletag capture_log: true @@ -42,15 +41,6 @@ defmodule Indexer.Block.Realtime.FetcherTest do end describe "Indexer.Block.Fetcher.fetch_and_import_range/1" do - setup do - initial_blob_disabled = Application.get_env(:indexer, Blob.Supervisor)[:disabled?] - Application.put_env(:indexer, Blob.Supervisor, disabled?: true) - - on_exit(fn -> - Application.put_env(:indexer, Blob.Supervisor, disabled?: initial_blob_disabled) - end) - end - @tag :no_geth test "in range with internal transactions", %{ block_fetcher: %Indexer.Block.Fetcher{} = block_fetcher, @@ -1067,15 +1057,6 @@ defmodule Indexer.Block.Realtime.FetcherTest do end describe "start_fetch_and_import" do - setup do - initial_blob_disabled = Application.get_env(:indexer, Blob.Supervisor)[:disabled?] - Application.put_env(:indexer, Blob.Supervisor, disabled?: true) - - on_exit(fn -> - Application.put_env(:indexer, Blob.Supervisor, disabled?: initial_blob_disabled) - end) - end - @tag :no_geth test "reorg", %{ block_fetcher: block_fetcher, diff --git a/apps/indexer/test/indexer/fetcher/beacon/blob_test.exs b/apps/indexer/test/indexer/fetcher/beacon/blob_test.exs new file mode 100644 index 0000000000..bf51708221 --- /dev/null +++ b/apps/indexer/test/indexer/fetcher/beacon/blob_test.exs @@ -0,0 +1,170 @@ +defmodule Indexer.Fetcher.Beacon.BlobTest do + use Explorer.DataCase, async: false + + import Mox + + alias Explorer.Chain.Transaction + alias Explorer.Chain.Beacon.{Blob, Reader} + alias Indexer.Fetcher.Beacon.Blob.Supervisor, as: BlobSupervisor + + setup :verify_on_exit! + setup :set_mox_global + + if Application.compile_env(:explorer, :chain_type) == "ethereum" do + describe "init/1" do + setup do + initial_env = Application.get_env(:indexer, BlobSupervisor) + Application.put_env(:indexer, BlobSupervisor, initial_env |> Keyword.put(:disabled?, false)) + + on_exit(fn -> + Application.put_env(:indexer, BlobSupervisor, initial_env) + end) + end + + test "fetches all missed blob transactions" do + {:ok, now, _} = DateTime.from_iso8601("2024-01-24 00:00:00Z") + block_a = insert(:block, timestamp: now) + block_b = insert(:block, timestamp: now |> Timex.shift(seconds: -120)) + block_c = insert(:block, timestamp: now |> Timex.shift(seconds: -240)) + + blob_a = build(:blob) + blob_b = build(:blob) + blob_c = build(:blob) + blob_d = insert(:blob) + + %Transaction{hash: transaction_a_hash} = insert(:transaction, type: 3) |> with_block(block_a) + %Transaction{hash: transaction_b_hash} = insert(:transaction, type: 3) |> with_block(block_b) + %Transaction{hash: transaction_c_hash} = insert(:transaction, type: 3) |> with_block(block_c) + + insert(:blob_transaction, hash: transaction_a_hash, blob_versioned_hashes: [blob_a.hash, blob_b.hash]) + insert(:blob_transaction, hash: transaction_b_hash, blob_versioned_hashes: [blob_c.hash]) + insert(:blob_transaction, hash: transaction_c_hash, blob_versioned_hashes: [blob_d.hash]) + + assert {:error, :not_found} = Reader.blob(blob_a.hash) + assert {:error, :not_found} = Reader.blob(blob_b.hash) + assert {:error, :not_found} = Reader.blob(blob_c.hash) + assert {:ok, _} = Reader.blob(blob_d.hash) + + Application.put_env(:explorer, :http_adapter, Explorer.Mox.HTTPoison) + + result_ab = """ + { + "data": [ + { + "index": "0", + "blob": "#{to_string(blob_a.blob_data)}", + "kzg_commitment": "#{to_string(blob_a.kzg_commitment)}", + "kzg_proof": "#{to_string(blob_a.kzg_proof)}" + }, + { + "index": "1", + "blob": "#{to_string(blob_b.blob_data)}", + "kzg_commitment": "#{to_string(blob_b.kzg_commitment)}", + "kzg_proof": "#{to_string(blob_b.kzg_proof)}" + } + ] + } + """ + + result_c = """ + { + "data": [ + { + "index": "0", + "blob": "#{to_string(blob_c.blob_data)}", + "kzg_commitment": "#{to_string(blob_c.kzg_commitment)}", + "kzg_proof": "#{to_string(blob_c.kzg_proof)}" + } + ] + } + """ + + Explorer.Mox.HTTPoison + |> expect(:get, 2, fn url -> + case url do + "http://localhost:5052/eth/v1/beacon/blob_sidecars/8269188" -> + {:ok, %HTTPoison.Response{status_code: 200, body: result_c}} + + "http://localhost:5052/eth/v1/beacon/blob_sidecars/8269198" -> + {:ok, %HTTPoison.Response{status_code: 200, body: result_ab}} + end + end) + + BlobSupervisor.Case.start_supervised!() + + wait_for_results(fn -> + Repo.one!(from(blob in Blob, where: blob.hash == ^blob_a.hash)) + end) + + assert {:ok, _} = Reader.blob(blob_a.hash) + assert {:ok, _} = Reader.blob(blob_b.hash) + assert {:ok, _} = Reader.blob(blob_c.hash) + assert {:ok, _} = Reader.blob(blob_d.hash) + + Application.put_env(:explorer, :http_adapter, HTTPoison) + end + end + + describe "async_fetch/1" do + setup do + initial_env = Application.get_env(:indexer, BlobSupervisor) + Application.put_env(:indexer, BlobSupervisor, initial_env |> Keyword.put(:disabled?, false)) + + on_exit(fn -> + Application.put_env(:indexer, BlobSupervisor, initial_env) + end) + end + + test "fetches blobs for block timestamp" do + Application.put_env(:explorer, :http_adapter, Explorer.Mox.HTTPoison) + + {:ok, now, _} = DateTime.from_iso8601("2024-01-24 00:00:00Z") + block_a = insert(:block, timestamp: now) + + %Blob{ + hash: blob_hash_a, + blob_data: blob_data_a, + kzg_commitment: kzg_commitment_a, + kzg_proof: kzg_proof_a + } = build(:blob) + + result_a = """ + { + "data": [ + { + "index": "0", + "blob": "#{to_string(blob_data_a)}", + "kzg_commitment": "#{to_string(kzg_commitment_a)}", + "kzg_proof": "#{to_string(kzg_proof_a)}" + } + ] + } + """ + + Explorer.Mox.HTTPoison + |> expect(:get, fn "http://localhost:5052/eth/v1/beacon/blob_sidecars/8269198" -> + {:ok, %HTTPoison.Response{status_code: 200, body: result_a}} + end) + + BlobSupervisor.Case.start_supervised!() + + assert :ok = Indexer.Fetcher.Beacon.Blob.async_fetch([block_a.timestamp]) + + wait_for_results(fn -> + Repo.one!(from(blob in Blob, where: blob.hash == ^blob_hash_a)) + end) + + assert {:ok, blob} = Reader.blob(blob_hash_a) + + assert %{ + hash: ^blob_hash_a, + blob_data: ^blob_data_a, + kzg_commitment: ^kzg_commitment_a, + kzg_proof: ^kzg_proof_a + } = blob + + Application.put_env(:explorer, :http_adapter, HTTPoison) + end + end + end +end