Prepare for filling and scanning zkevm_bridge_l1_tokens table

pull/9098/head
POA 11 months ago
parent a285285b82
commit 19ef37c398
  1. 2
      apps/explorer/lib/explorer/chain/import/runner/zkevm/bridge_l1_tokens.ex
  2. 138
      apps/indexer/lib/indexer/fetcher/zkevm/bridge_l1.ex
  3. 117
      apps/indexer/lib/indexer/helper.ex
  4. 6
      apps/indexer/lib/indexer/supervisor.ex
  5. 2
      config/runtime.exs

@ -62,7 +62,7 @@ defmodule Explorer.Chain.Import.Runner.Zkevm.BridgeL1Tokens do
on_conflict = Map.get_lazy(options, :on_conflict, &default_on_conflict/0) on_conflict = Map.get_lazy(options, :on_conflict, &default_on_conflict/0)
# Enforce BridgeL1Token ShareLocks order (see docs: sharelock.md) # Enforce BridgeL1Token ShareLocks order (see docs: sharelock.md)
ordered_changes_list = Enum.sort_by(changes_list, &{&1.id}) ordered_changes_list = Enum.sort_by(changes_list, &{&1.address})
{:ok, inserted} = {:ok, inserted} =
Import.insert_changes_list( Import.insert_changes_list(

@ -18,6 +18,8 @@ defmodule Indexer.Fetcher.Zkevm.BridgeL1 do
request: 1 request: 1
] ]
import Explorer.Chain.SmartContract, only: [burn_address_hash_string: 0]
import Explorer.Helper, only: [parse_integer: 1, decode_data: 2] import Explorer.Helper, only: [parse_integer: 1, decode_data: 2]
alias EthereumJSONRPC.Block.ByNumber alias EthereumJSONRPC.Block.ByNumber
@ -31,7 +33,7 @@ defmodule Indexer.Fetcher.Zkevm.BridgeL1 do
@block_check_interval_range_size 100 @block_check_interval_range_size 100
@eth_get_logs_range_size 1000 @eth_get_logs_range_size 1000
@fetcher_name :zkevm_bridge_l1 @fetcher_name :zkevm_bridge_l1
@empty_hash "0x0000000000000000000000000000000000000000000000000000000000000000" # @empty_hash "0x0000000000000000000000000000000000000000000000000000000000000000"
# 32-byte signature of the event BridgeEvent(uint8 leafType, uint32 originNetwork, address originAddress, uint32 destinationNetwork, address destinationAddress, uint256 amount, bytes metadata, uint32 depositCount) # 32-byte signature of the event BridgeEvent(uint8 leafType, uint32 originNetwork, address originAddress, uint32 destinationNetwork, address destinationAddress, uint256 amount, bytes metadata, uint32 depositCount)
@bridge_event "0x501781209a1f8899323b96b4ef08b168df93e0a90c673d1e4cce39366cb62f9b" @bridge_event "0x501781209a1f8899323b96b4ef08b168df93e0a90c673d1e4cce39366cb62f9b"
@ -95,7 +97,7 @@ defmodule Indexer.Fetcher.Zkevm.BridgeL1 do
with {:start_block_undefined, false} <- {:start_block_undefined, is_nil(env[:start_block])}, with {:start_block_undefined, false} <- {:start_block_undefined, is_nil(env[:start_block])},
rpc = env[:rpc], rpc = env[:rpc],
{:rpc_undefined, false} <- {:rpc_undefined, is_nil(rpc)}, {:rpc_undefined, false} <- {:rpc_undefined, is_nil(rpc)},
{:bridge_contract_address_is_valid, true} <- {:bridge_contract_address_is_valid, Helper.is_address_correct?(env[:bridge_contract])}, {:bridge_contract_address_is_valid, true} <- {:bridge_contract_address_is_valid, Helper.address_correct?(env[:bridge_contract])},
start_block = parse_integer(env[:start_block]), start_block = parse_integer(env[:start_block]),
false <- is_nil(start_block), false <- is_nil(start_block),
true <- start_block > 0, true <- start_block > 0,
@ -294,7 +296,7 @@ defmodule Indexer.Fetcher.Zkevm.BridgeL1 do
defp get_last_l1_item do defp get_last_l1_item do
query = query =
from(b in Bridge, from(b in Bridge,
select: {b.l1_block_number, b.l1_transaction_hash}, select: {b.block_number, b.l1_transaction_hash},
where: b.type == :deposit and not is_nil(b.block_number), where: b.type == :deposit and not is_nil(b.block_number),
order_by: [desc: b.index], order_by: [desc: b.index],
limit: 1 limit: 1
@ -352,12 +354,12 @@ defmodule Indexer.Fetcher.Zkevm.BridgeL1 do
end end
end end
defp get_token_data(token_addresses) do defp get_token_data(token_addresses, json_rpc_named_arguments) do
# first, we're trying to read token data from the DB. # first, we're trying to read token data from the DB.
# if tokens are not in the DB, read them through RPC. # if tokens are not in the DB, read them through RPC.
token_addresses token_addresses
|> get_token_data_from_db() |> get_token_data_from_db()
|> get_token_data_from_rpc() |> get_token_data_from_rpc(json_rpc_named_arguments)
end end
defp get_token_data_from_db(token_addresses) do defp get_token_data_from_db(token_addresses) do
@ -386,8 +388,8 @@ defmodule Indexer.Fetcher.Zkevm.BridgeL1 do
{token_data, token_addresses_for_rpc} {token_data, token_addresses_for_rpc}
end end
defp get_token_data_from_rpc({token_data, token_addresses}) do defp get_token_data_from_rpc({token_data, token_addresses}, json_rpc_named_arguments) do
{requests, responses} = get_token_data_request_symbol_decimals(token_addresses) {requests, responses} = get_token_data_request_symbol_decimals(token_addresses, json_rpc_named_arguments)
requests requests
|> Enum.zip(responses) |> Enum.zip(responses)
@ -415,13 +417,13 @@ defmodule Indexer.Fetcher.Zkevm.BridgeL1 do
defp get_new_data(data, request, response) do defp get_new_data(data, request, response) do
if atomized_key(request.method_id) == :symbol do if atomized_key(request.method_id) == :symbol do
%{data | symbol: response} Map.put(data, :symbol, response)
else else
%{data | decimals: response} Map.put(data, :decimals, response)
end end
end end
defp get_token_data_request_symbol_decimals(token_addresses) do defp get_token_data_request_symbol_decimals(token_addresses, json_rpc_named_arguments) do
requests = requests =
token_addresses token_addresses
|> Enum.map(fn address -> |> Enum.map(fn address ->
@ -436,7 +438,7 @@ defmodule Indexer.Fetcher.Zkevm.BridgeL1 do
end) end)
|> List.flatten() |> List.flatten()
{responses, error_messages} = read_contracts_with_retries(requests, @erc20_abi, 3) {responses, error_messages} = read_contracts_with_retries(requests, @erc20_abi, json_rpc_named_arguments, 3)
if !Enum.empty?(error_messages) or Enum.count(requests) != Enum.count(responses) do if !Enum.empty?(error_messages) or Enum.count(requests) != Enum.count(responses) do
Logger.warning( Logger.warning(
@ -476,67 +478,87 @@ defmodule Indexer.Fetcher.Zkevm.BridgeL1 do
Map.put(acc, block_number, timestamp) Map.put(acc, block_number, timestamp)
end) end)
bridge_event_params = [{:uint, 8}, {:uint, 32}, :address, {:uint, 32}, :address, {:uint, 256}, :bytes, {:uint, 32}]
claim_event_params = [{:uint, 32}, {:uint, 32}, :address, :address, {:uint, 256}]
token_data = token_data =
deposit_events deposit_events
|> Enum.reduce(%{}, fn event, acc -> |> Enum.reduce(%{}, fn event, acc ->
[leaf_type, _origin_network, origin_address, _destination_network, _destination_address, _amount, _metadata, _deposit_count] = decode_data(event["data"], [{:uint, 8}, {:uint, 32}, :address, {:uint, 32}, :address, {:uint, 256}, :bytes, {:uint, 32}]) [leaf_type, _origin_network, origin_address, _destination_network, _destination_address, _amount, _metadata, _deposit_count] = decode_data(event["data"], bridge_event_params)
origin_address = "0x" <> Base.encode16(origin_address, case: :lower)
if leaf_type != 1 do if leaf_type != 1 and origin_address != burn_address_hash_string() do
Map.put(acc, origin_address, true) Map.put(acc, origin_address, true)
else else
acc acc
end end
end) end)
|> Map.values() |> Map.keys()
|> get_token_data() |> get_token_data(json_rpc_named_arguments)
tokens =
token_data
|> Enum.map(fn {address, data} ->
Map.put(data, :address, address)
end)
# todo: select known tokens from zkevm_bridge_l1_tokens table
Logger.warn("tokens: #{inspect(tokens)}")
# todo: insert only unknown tokens
{:ok, tokens_inserted} = Chain.import(%{
zkevm_bridge_l1_tokens: %{params: tokens},
timeout: :infinity
})
# todo: select remaining tokens from zkevm_bridge_l1_tokens table if they are not in `tokens_inserted`
Logger.warn("tokens_inserted: #{inspect(tokens_inserted)}")
events events
|> Enum.map(fn event -> |> Enum.map(fn event ->
topic0 = Enum.at(event["topics"], 0) {type, index, amount, block_number, block_timestamp} =
if Enum.at(event["topics"], 0) == @bridge_event do
# user = get_op_user(topic0, event) [_leaf_type, _origin_network, _origin_address, _destination_network, _destination_address, amount, _metadata, deposit_count] = decode_data(event["data"], bridge_event_params)
# {amounts_or_ids, operation_id} = get_op_amounts(topic0, event)
# {erc1155_ids, erc1155_amounts} = get_op_erc1155_data(topic0, event) block_number = quantity_to_integer(event["blockNumber"])
block_timestamp = Map.get(timestamps, block_number)
# l1_block_number = quantity_to_integer(event["blockNumber"])
{:deposit, deposit_count, amount, block_number, block_timestamp}
# {operation_type, timestamp} = else
# if is_deposit(topic0) do [index, _origin_network, _origin_address, _destination_address, amount] = decode_data(event["data"], claim_event_params)
# {:deposit, Map.get(timestamps, l1_block_number)}
# else {:withdrawal, index, amount, nil, nil}
# {:withdrawal, nil} end
# end
result =
# token_type = %{
# cond do type: type,
# Enum.member?([@new_deposit_block_event, @withdraw_event], topic0) -> index: index,
# "bone" l1_transaction_hash: event["transactionHash"],
# l1_token_id: ...,
# Enum.member?([@locked_ether_event, @exited_ether_event], topic0) -> amount: amount
# "eth" }
# true -> result =
# "other" if not is_nil(block_number) do
# end Map.put(result, :block_number, block_number)
else
# %{ result
# user: user, end
# amount_or_id: amount_or_id,
# erc1155_ids: if(Enum.empty?(erc1155_ids), do: nil, else: erc1155_ids), if not is_nil(block_timestamp) do
# erc1155_amounts: if(Enum.empty?(erc1155_amounts), do: nil, else: erc1155_amounts), Map.put(result, :block_timestamp, block_timestamp)
# l1_transaction_hash: event["transactionHash"], else
# l1_block_number: l1_block_number, result
# l2_transaction_hash: @empty_hash, end
# operation_hash: calc_operation_hash(user, amount_or_id, erc1155_ids, erc1155_amounts, operation_id),
# operation_type: operation_type,
# token_type: token_type,
# timestamp: timestamp
# }
end) end)
end end
defp read_contracts_with_retries(requests, abi, retries_left) when retries_left > 0 do defp read_contracts_with_retries(requests, abi, json_rpc_named_arguments, retries_left) when retries_left > 0 do
responses = Reader.query_contracts(requests, abi) responses = Reader.query_contracts(requests, abi, json_rpc_named_arguments: json_rpc_named_arguments)
error_messages = error_messages =
Enum.reduce(responses, [], fn {status, error_message}, acc -> Enum.reduce(responses, [], fn {status, error_message}, acc ->
@ -556,7 +578,7 @@ defmodule Indexer.Fetcher.Zkevm.BridgeL1 do
if retries_left == 0 do if retries_left == 0 do
{responses, Enum.uniq(error_messages)} {responses, Enum.uniq(error_messages)}
else else
read_contracts_with_retries(requests, abi, retries_left) read_contracts_with_retries(requests, abi, json_rpc_named_arguments, retries_left)
end end
end end
end end

@ -16,6 +16,15 @@ defmodule Indexer.Helper do
alias EthereumJSONRPC.Block.ByNumber alias EthereumJSONRPC.Block.ByNumber
alias Explorer.Chain.Hash alias Explorer.Chain.Hash
@spec address_correct?(binary()) :: boolean()
def address_correct?(address) when is_binary(address) do
String.match?(address, ~r/^0x[[:xdigit:]]{40}$/i)
end
def address_correct?(_address) do
false
end
@spec address_hash_to_string(binary(), boolean()) :: binary() @spec address_hash_to_string(binary(), boolean()) :: binary()
def address_hash_to_string(hash, downcase \\ false) def address_hash_to_string(hash, downcase \\ false)
@ -35,13 +44,65 @@ defmodule Indexer.Helper do
end end
end end
@spec address_correct?(binary()) :: boolean() @spec get_block_number_by_tag(binary(), list(), integer()) :: {:ok, non_neg_integer()} | {:error, atom()}
def address_correct?(address) when is_binary(address) do def get_block_number_by_tag(tag, json_rpc_named_arguments, retries \\ 3) do
String.match?(address, ~r/^0x[[:xdigit:]]{40}$/i) error_message = &"Cannot fetch #{tag} block number. Error: #{inspect(&1)}"
repeated_call(&fetch_block_number_by_tag/2, [tag, json_rpc_named_arguments], error_message, retries)
end end
def address_correct?(_address) do def get_transaction_by_hash(hash, json_rpc_named_arguments, retries_left \\ 3)
false
def get_transaction_by_hash(hash, _json_rpc_named_arguments, _retries_left) when is_nil(hash), do: {:ok, nil}
def get_transaction_by_hash(hash, json_rpc_named_arguments, retries) do
req =
request(%{
id: 0,
method: "eth_getTransactionByHash",
params: [hash]
})
error_message = &"eth_getTransactionByHash failed. Error: #{inspect(&1)}"
repeated_call(&json_rpc/2, [req, json_rpc_named_arguments], error_message, retries)
end
def log_blocks_chunk_handling(chunk_start, chunk_end, start_block, end_block, items_count, layer) do
is_start = is_nil(items_count)
{type, found} =
if is_start do
{"Start", ""}
else
{"Finish", " Found #{items_count}."}
end
target_range =
if chunk_start != start_block or chunk_end != end_block do
progress =
if is_start do
""
else
percentage =
(chunk_end - start_block + 1)
|> Decimal.div(end_block - start_block + 1)
|> Decimal.mult(100)
|> Decimal.round(2)
|> Decimal.to_string()
" Progress: #{percentage}%"
end
" Target range: #{start_block}..#{end_block}.#{progress}"
else
""
end
if chunk_start == chunk_end do
Logger.info("#{type} handling #{layer} block ##{chunk_start}.#{found}#{target_range}")
else
Logger.info("#{type} handling #{layer} block range #{chunk_start}..#{chunk_end}.#{found}#{target_range}")
end
end end
@doc """ @doc """
@ -195,4 +256,50 @@ defmodule Indexer.Helper do
Hash.to_string(topic) Hash.to_string(topic)
end end
end end
def repeated_call(func, args, error_message, retries_left) do
case apply(func, args) do
{:ok, _} = res ->
res
{:error, message} = err ->
retries_left = retries_left - 1
if retries_left <= 0 do
Logger.error(error_message.(message))
err
else
Logger.error("#{error_message.(message)} Retrying...")
:timer.sleep(3000)
repeated_call(func, args, error_message, retries_left)
end
end
end
def get_block_timestamp_by_number(number, json_rpc_named_arguments, retries \\ 3) do
func = &get_block_timestamp_by_number_inner/2
args = [number, json_rpc_named_arguments]
error_message = &"Cannot fetch block ##{number} or its timestamp. Error: #{inspect(&1)}"
repeated_call(func, args, error_message, retries)
end
defp get_block_timestamp_by_number_inner(number, json_rpc_named_arguments) do
result =
%{id: 0, number: number}
|> ByNumber.request(false)
|> json_rpc(json_rpc_named_arguments)
with {:ok, block} <- result,
false <- is_nil(block),
timestamp <- Map.get(block, "timestamp"),
false <- is_nil(timestamp) do
{:ok, quantity_to_integer(timestamp)}
else
{:error, message} ->
{:error, message}
true ->
{:error, "RPC returned nil."}
end
end
end end

@ -146,9 +146,9 @@ defmodule Indexer.Supervisor do
]), ]),
configure(Indexer.Fetcher.Shibarium.L1.Supervisor, [[memory_monitor: memory_monitor]]), configure(Indexer.Fetcher.Shibarium.L1.Supervisor, [[memory_monitor: memory_monitor]]),
configure(Indexer.Fetcher.Zkevm.BridgeL1.Supervisor, [[memory_monitor: memory_monitor]]), configure(Indexer.Fetcher.Zkevm.BridgeL1.Supervisor, [[memory_monitor: memory_monitor]]),
configure(Indexer.Fetcher.Zkevm.BridgeL2.Supervisor, [ # configure(Indexer.Fetcher.Zkevm.BridgeL2.Supervisor, [
[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor] # [json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]
]), # ]),
configure(Indexer.Fetcher.Zkevm.TransactionBatch.Supervisor, [ configure(Indexer.Fetcher.Zkevm.TransactionBatch.Supervisor, [
[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor] [json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]
]), ]),

@ -738,7 +738,7 @@ config :indexer, Indexer.Fetcher.Zkevm.BridgeL1.Supervisor, enabled: ConfigHelpe
config :indexer, Indexer.Fetcher.Zkevm.BridgeL2, config :indexer, Indexer.Fetcher.Zkevm.BridgeL2,
start_block: System.get_env("INDEXER_POLYGON_ZKEVM_L2_BRIDGE_START_BLOCK"), start_block: System.get_env("INDEXER_POLYGON_ZKEVM_L2_BRIDGE_START_BLOCK"),
bridge_contract: System.get_env("INDEXER_POLYGON_ZKEVM_L2_BRIDGE_CONTRACT"), bridge_contract: System.get_env("INDEXER_POLYGON_ZKEVM_L2_BRIDGE_CONTRACT")
config :indexer, Indexer.Fetcher.Zkevm.BridgeL2.Supervisor, enabled: ConfigHelper.chain_type() == "polygon_zkevm" config :indexer, Indexer.Fetcher.Zkevm.BridgeL2.Supervisor, enabled: ConfigHelper.chain_type() == "polygon_zkevm"

Loading…
Cancel
Save