fix: Fix contract codes fetching for zksync chain type (#11055)

* fix: Fix contract codes fetching for zksync chain type

* Process review comments

* Fix tests

* Fix tests

* Fix tests
pull/11082/head
nikitosing 3 weeks ago committed by GitHub
parent 940a95f0e0
commit 0423c18d3a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 8
      apps/block_scout_web/lib/block_scout_web/views/address_contract_view.ex
  2. 1
      apps/explorer/config/config.exs
  3. 1
      apps/explorer/config/runtime/test.exs
  4. 3
      apps/explorer/lib/explorer/application.ex
  5. 13
      apps/explorer/lib/explorer/chain/address.ex
  6. 80
      apps/explorer/lib/explorer/migrator/refetch_contract_codes.ex
  7. 13
      apps/explorer/priv/zk_sync/migrations/20241028102853_add_contract_code_refetched.exs
  8. 8
      apps/explorer/test/explorer/migrator/sanitize_duplicated_log_index_logs_test.exs
  9. 13
      apps/explorer/test/support/factory.ex
  10. 7
      apps/indexer/lib/indexer/transform/addresses.ex
  11. 8
      config/runtime.exs
  12. 4
      docker-compose/envs/common-blockscout.env

@ -118,14 +118,14 @@ defmodule BlockScoutWeb.AddressContractView do
{:ok, contract_code} {:ok, contract_code}
end end
def creation_code(%Address{contracts_creation_internal_transaction: %InternalTransaction{}} = address) do
address.contracts_creation_internal_transaction.init
end
def creation_code(%Address{contracts_creation_transaction: %Transaction{}} = address) do def creation_code(%Address{contracts_creation_transaction: %Transaction{}} = address) do
address.contracts_creation_transaction.input address.contracts_creation_transaction.input
end end
def creation_code(%Address{contracts_creation_internal_transaction: %InternalTransaction{}} = address) do
address.contracts_creation_internal_transaction.init
end
def creation_code(%Address{contracts_creation_transaction: nil}) do def creation_code(%Address{contracts_creation_transaction: nil}) do
nil nil
end end

@ -132,6 +132,7 @@ config :explorer, Explorer.Migrator.SanitizeMissingTokenBalances, enabled: true
config :explorer, Explorer.Migrator.SanitizeReplacedTransactions, enabled: true config :explorer, Explorer.Migrator.SanitizeReplacedTransactions, enabled: true
config :explorer, Explorer.Migrator.ReindexInternalTransactionsWithIncompatibleStatus, enabled: true config :explorer, Explorer.Migrator.ReindexInternalTransactionsWithIncompatibleStatus, enabled: true
config :explorer, Explorer.Migrator.SanitizeDuplicatedLogIndexLogs, enabled: true config :explorer, Explorer.Migrator.SanitizeDuplicatedLogIndexLogs, enabled: true
config :explorer, Explorer.Migrator.RefetchContractCodes, enabled: true
config :explorer, Explorer.Chain.Fetcher.CheckBytecodeMatchingOnDemand, enabled: true config :explorer, Explorer.Chain.Fetcher.CheckBytecodeMatchingOnDemand, enabled: true

@ -53,6 +53,7 @@ config :explorer, Explorer.Migrator.SanitizeMissingTokenBalances, enabled: false
config :explorer, Explorer.Migrator.SanitizeReplacedTransactions, enabled: false config :explorer, Explorer.Migrator.SanitizeReplacedTransactions, enabled: false
config :explorer, Explorer.Migrator.ReindexInternalTransactionsWithIncompatibleStatus, enabled: false config :explorer, Explorer.Migrator.ReindexInternalTransactionsWithIncompatibleStatus, enabled: false
config :explorer, Explorer.Migrator.SanitizeDuplicatedLogIndexLogs, enabled: false config :explorer, Explorer.Migrator.SanitizeDuplicatedLogIndexLogs, enabled: false
config :explorer, Explorer.Migrator.RefetchContractCodes, enabled: false
config :explorer, config :explorer,
realtime_events_sender: Explorer.Chain.Events.SimpleSender realtime_events_sender: Explorer.Chain.Events.SimpleSender

@ -155,7 +155,8 @@ defmodule Explorer.Application do
]), ]),
configure_mode_dependent_process(Explorer.Migrator.SanitizeMissingTokenBalances, :indexer), configure_mode_dependent_process(Explorer.Migrator.SanitizeMissingTokenBalances, :indexer),
configure_mode_dependent_process(Explorer.Migrator.SanitizeReplacedTransactions, :indexer), configure_mode_dependent_process(Explorer.Migrator.SanitizeReplacedTransactions, :indexer),
configure_mode_dependent_process(Explorer.Migrator.ReindexInternalTransactionsWithIncompatibleStatus, :indexer) configure_mode_dependent_process(Explorer.Migrator.ReindexInternalTransactionsWithIncompatibleStatus, :indexer),
Explorer.Migrator.RefetchContractCodes |> configure() |> configure_chain_type_dependent_process(:zksync)
] ]
|> List.flatten() |> List.flatten()

@ -58,6 +58,13 @@ defmodule Explorer.Chain.Address.Schema do
] ]
end end
:zksync ->
quote do
[
field(:contract_code_refetched, :boolean)
]
end
_ -> _ ->
[] []
end) end)
@ -142,6 +149,9 @@ defmodule Explorer.Chain.Address do
:filecoin -> :filecoin ->
~w(filecoin_id filecoin_robust filecoin_actor_type)a ~w(filecoin_id filecoin_robust filecoin_actor_type)a
:zksync ->
~w(contract_code_refetched)a
_ -> _ ->
[] []
end) end)
@ -196,6 +206,9 @@ defmodule Explorer.Chain.Address do
* `filecoin_id_address` - short f0 Filecoin address that may change during chain reorgs * `filecoin_id_address` - short f0 Filecoin address that may change during chain reorgs
* `filecoin_actor_type` - type of actor associated with the Filecoin address * `filecoin_actor_type` - type of actor associated with the Filecoin address
""" """
:zksync -> """
* `contract_code_refetched` - true when Explorer.Migrator.RefetchContractCodes handled this address, or it's unnecessary (for addresses inserted after this)
"""
_ -> "" _ -> ""
end} end}
`fetched_coin_balance` and `fetched_coin_balance_block_number` may be updated when a new coin_balance row is fetched. `fetched_coin_balance` and `fetched_coin_balance_block_number` may be updated when a new coin_balance row is fetched.

@ -0,0 +1,80 @@
defmodule Explorer.Migrator.RefetchContractCodes do
@moduledoc """
Refetch contract_code for. Migration created for running on zksync chain type.
It has an issue with created contract code derived from internal transactions. Such codes are not correct.
So, this migration fetches for all current smart contracts actual bytecode from the JSON RPC node.
"""
use Explorer.Migrator.FillingMigration
import Ecto.Query
alias Explorer.Chain.{Address, Data, Import}
alias Explorer.Chain.Hash.Address, as: AddressHash
alias Explorer.Chain.Import.Runner.Addresses
alias Explorer.Migrator.FillingMigration
alias Explorer.Repo
require Logger
@migration_name "refetch_contract_codes"
@impl FillingMigration
def migration_name, do: @migration_name
@impl FillingMigration
def last_unprocessed_identifiers(state) do
limit = batch_size() * concurrency()
ids =
unprocessed_data_query()
|> select([address], address.hash)
|> limit(^limit)
|> Repo.all(timeout: :infinity)
{ids, state}
end
@impl FillingMigration
def unprocessed_data_query do
Address
|> where([address], not is_nil(address.contract_code) and not address.contract_code_refetched)
end
@impl FillingMigration
def update_batch(address_hashes) do
json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments)
address_hashes
|> Enum.map(&address_to_fetch_code_params/1)
|> EthereumJSONRPC.fetch_codes(json_rpc_named_arguments)
|> case do
{:ok, create_address_codes} ->
addresses_params = create_address_codes.params_list |> Enum.map(&param_to_address/1) |> Enum.sort_by(& &1.hash)
Addresses.insert(Repo, addresses_params, %{
timeout: :infinity,
on_conflict: {:replace, [:contract_code, :contract_code_refetched, :updated_at]},
timestamps: Import.timestamps()
})
{:error, reason} ->
Logger.error(fn -> ["failed to fetch contract codes: ", inspect(reason)] end,
error_count: Enum.count(address_hashes)
)
end
end
@impl FillingMigration
def update_cache, do: :ok
defp address_to_fetch_code_params(address_hash) do
%{block_quantity: "latest", address: to_string(address_hash)}
end
defp param_to_address(%{code: bytecode, address: address_hash}) do
{:ok, address_hash} = AddressHash.cast(address_hash)
{:ok, bytecode} = Data.cast(bytecode)
%{hash: address_hash, contract_code: bytecode, contract_code_refetched: true}
end
end

@ -0,0 +1,13 @@
defmodule Explorer.Repo.ZkSync.Migrations.AddContractCodeRefetched do
use Ecto.Migration
def change do
alter table(:addresses) do
add(:contract_code_refetched, :boolean, default: false)
end
execute("""
ALTER TABLE addresses ALTER COLUMN contract_code_refetched SET DEFAULT true;
""")
end
end

@ -7,7 +7,7 @@ defmodule Explorer.Migrator.SanitizeDuplicatedLogIndexLogsTest do
alias Explorer.Chain.Token.Instance alias Explorer.Chain.Token.Instance
alias Explorer.Migrator.{SanitizeDuplicatedLogIndexLogs, MigrationStatus} alias Explorer.Migrator.{SanitizeDuplicatedLogIndexLogs, MigrationStatus}
if Application.compile_env(:explorer, :chain_type) != :celo do if Application.compile_env(:explorer, :chain_type) in [:polygon_zkevm, :rsk, :filecoin] do
describe "Sanitize duplicated log index logs" do describe "Sanitize duplicated log index logs" do
test "correctly identifies and updates duplicated log index logs" do test "correctly identifies and updates duplicated log index logs" do
block = insert(:block) block = insert(:block)
@ -24,7 +24,7 @@ defmodule Explorer.Migrator.SanitizeDuplicatedLogIndexLogsTest do
assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == nil assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == nil
SanitizeDuplicatedLogIndexLogs.start_link([]) SanitizeDuplicatedLogIndexLogs.start_link([])
Process.sleep(300) :timer.sleep(500)
assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == "completed" assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == "completed"
assert BackgroundMigrations.get_sanitize_duplicated_log_index_logs_finished() == true assert BackgroundMigrations.get_sanitize_duplicated_log_index_logs_finished() == true
@ -103,7 +103,7 @@ defmodule Explorer.Migrator.SanitizeDuplicatedLogIndexLogsTest do
assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == nil assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == nil
SanitizeDuplicatedLogIndexLogs.start_link([]) SanitizeDuplicatedLogIndexLogs.start_link([])
Process.sleep(300) :timer.sleep(500)
assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == "completed" assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == "completed"
assert BackgroundMigrations.get_sanitize_duplicated_log_index_logs_finished() == true assert BackgroundMigrations.get_sanitize_duplicated_log_index_logs_finished() == true
@ -138,7 +138,7 @@ defmodule Explorer.Migrator.SanitizeDuplicatedLogIndexLogsTest do
assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == nil assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == nil
SanitizeDuplicatedLogIndexLogs.start_link([]) SanitizeDuplicatedLogIndexLogs.start_link([])
Process.sleep(100) :timer.sleep(100)
assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == "completed" assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == "completed"
assert BackgroundMigrations.get_sanitize_duplicated_log_index_logs_finished() == true assert BackgroundMigrations.get_sanitize_duplicated_log_index_logs_finished() == true

@ -265,6 +265,19 @@ defmodule Explorer.Factory do
%Address{ %Address{
hash: address_hash() hash: address_hash()
} }
|> Map.merge(address_factory_chain_type_fields())
end
case Application.compile_env(:explorer, :chain_type) do
:zksync ->
defp address_factory_chain_type_fields() do
%{
contract_code_refetched: true
}
end
_ ->
defp address_factory_chain_type_fields(), do: %{}
end end
def address_name_factory do def address_name_factory do

@ -73,11 +73,18 @@ defmodule Indexer.Transform.Addresses do
%{from: :block_number, to: :fetched_coin_balance_block_number}, %{from: :block_number, to: :fetched_coin_balance_block_number},
%{from: :to_address_hash, to: :hash} %{from: :to_address_hash, to: :hash}
], ],
if Application.compile_env(:explorer, :chain_type) == :zksync do
[
%{from: :block_number, to: :fetched_coin_balance_block_number},
%{from: :created_contract_address_hash, to: :hash}
]
else
[ [
%{from: :block_number, to: :fetched_coin_balance_block_number}, %{from: :block_number, to: :fetched_coin_balance_block_number},
%{from: :created_contract_address_hash, to: :hash}, %{from: :created_contract_address_hash, to: :hash},
%{from: :created_contract_code, to: :contract_code} %{from: :created_contract_code, to: :contract_code}
] ]
end
], ],
codes: [ codes: [
[ [

@ -619,6 +619,14 @@ config :explorer, Explorer.Migrator.RestoreOmittedWETHTransfers,
batch_size: ConfigHelper.parse_integer_env_var("MIGRATION_RESTORE_OMITTED_WETH_TOKEN_TRANSFERS_BATCH_SIZE", 50), batch_size: ConfigHelper.parse_integer_env_var("MIGRATION_RESTORE_OMITTED_WETH_TOKEN_TRANSFERS_BATCH_SIZE", 50),
timeout: ConfigHelper.parse_time_env_var("MIGRATION_RESTORE_OMITTED_WETH_TOKEN_TRANSFERS_TIMEOUT", "250ms") timeout: ConfigHelper.parse_time_env_var("MIGRATION_RESTORE_OMITTED_WETH_TOKEN_TRANSFERS_TIMEOUT", "250ms")
config :explorer, Explorer.Migrator.SanitizeDuplicatedLogIndexLogs,
concurrency: ConfigHelper.parse_integer_env_var("MIGRATION_SANITIZE_DUPLICATED_LOG_INDEX_LOGS_CONCURRENCY", 10),
batch_size: ConfigHelper.parse_integer_env_var("MIGRATION_SANITIZE_DUPLICATED_LOG_INDEX_LOGS_BATCH_SIZE", 500)
config :explorer, Explorer.Migrator.RefetchContractCodes,
concurrency: ConfigHelper.parse_integer_env_var("MIGRATION_REFETCH_CONTRACT_CODES_CONCURRENCY", 5),
batch_size: ConfigHelper.parse_integer_env_var("MIGRATION_REFETCH_CONTRACT_CODES_BATCH_SIZE", 100)
config :explorer, Explorer.Migrator.ShrinkInternalTransactions, config :explorer, Explorer.Migrator.ShrinkInternalTransactions,
enabled: ConfigHelper.parse_bool_env_var("SHRINK_INTERNAL_TRANSACTIONS_ENABLED"), enabled: ConfigHelper.parse_bool_env_var("SHRINK_INTERNAL_TRANSACTIONS_ENABLED"),
batch_size: ConfigHelper.parse_integer_env_var("SHRINK_INTERNAL_TRANSACTIONS_BATCH_SIZE", 100), batch_size: ConfigHelper.parse_integer_env_var("SHRINK_INTERNAL_TRANSACTIONS_BATCH_SIZE", 100),

@ -430,6 +430,10 @@ EIP_1559_ELASTICITY_MULTIPLIER=2
# MIGRATION_RESTORE_OMITTED_WETH_TOKEN_TRANSFERS_CONCURRENCY= # MIGRATION_RESTORE_OMITTED_WETH_TOKEN_TRANSFERS_CONCURRENCY=
# MIGRATION_RESTORE_OMITTED_WETH_TOKEN_TRANSFERS_BATCH_SIZE= # MIGRATION_RESTORE_OMITTED_WETH_TOKEN_TRANSFERS_BATCH_SIZE=
# MIGRATION_RESTORE_OMITTED_WETH_TOKEN_TRANSFERS_TIMEOUT= # MIGRATION_RESTORE_OMITTED_WETH_TOKEN_TRANSFERS_TIMEOUT=
# MIGRATION_SANITIZE_DUPLICATED_LOG_INDEX_LOGS_CONCURRENCY=
# MIGRATION_SANITIZE_DUPLICATED_LOG_INDEX_LOGS_BATCH_SIZE=
# MIGRATION_REFETCH_CONTRACT_CODES_BATCH_SIZE=
# MIGRATION_REFETCH_CONTRACT_CODES_CONCURRENCY=
SOURCIFY_INTEGRATION_ENABLED=false SOURCIFY_INTEGRATION_ENABLED=false
SOURCIFY_SERVER_URL= SOURCIFY_SERVER_URL=
SOURCIFY_REPO_URL= SOURCIFY_REPO_URL=

Loading…
Cancel
Save