parent
ae03f9436b
commit
b4c049abb1
@ -0,0 +1,124 @@ |
||||
defmodule Indexer.PendingTransactionsSanitizer do |
||||
@moduledoc """ |
||||
Periodically checks pending transactions status in order to detect that transaction already included to the block |
||||
And we need to re-fetch that block. |
||||
""" |
||||
|
||||
use GenServer |
||||
|
||||
require Logger |
||||
|
||||
import EthereumJSONRPC, only: [json_rpc: 2, request: 1] |
||||
|
||||
alias Explorer.{Chain, Repo} |
||||
alias Explorer.Chain.Import.Runner.Blocks |
||||
|
||||
@interval :timer.hours(3) |
||||
|
||||
defstruct interval: @interval, |
||||
json_rpc_named_arguments: [] |
||||
|
||||
def child_spec([init_arguments]) do |
||||
child_spec([init_arguments, []]) |
||||
end |
||||
|
||||
def child_spec([_init_arguments, _gen_server_options] = start_link_arguments) do |
||||
default = %{ |
||||
id: __MODULE__, |
||||
start: {__MODULE__, :start_link, start_link_arguments} |
||||
} |
||||
|
||||
Supervisor.child_spec(default, []) |
||||
end |
||||
|
||||
def start_link(init_opts, gen_server_opts \\ []) do |
||||
GenServer.start_link(__MODULE__, init_opts, gen_server_opts) |
||||
end |
||||
|
||||
def init(opts) when is_list(opts) do |
||||
state = %__MODULE__{ |
||||
json_rpc_named_arguments: Keyword.fetch!(opts, :json_rpc_named_arguments), |
||||
interval: opts[:interval] || @interval |
||||
} |
||||
|
||||
Process.send_after(self(), :sanitize_pending_transactions, state.interval) |
||||
|
||||
{:ok, state} |
||||
end |
||||
|
||||
def handle_info( |
||||
:sanitize_pending_transactions, |
||||
%{interval: interval, json_rpc_named_arguments: json_rpc_named_arguments} = state |
||||
) do |
||||
Logger.debug("Start sanitizing of pending transactions", |
||||
fetcher: :pending_transactions_to_refetch |
||||
) |
||||
|
||||
sanitize_pending_transactions(json_rpc_named_arguments) |
||||
|
||||
Process.send_after(self(), :sanitize_pending_transactions, interval) |
||||
|
||||
{:noreply, state} |
||||
end |
||||
|
||||
defp sanitize_pending_transactions(json_rpc_named_arguments) do |
||||
pending_transactions_list_from_db = Chain.pending_transactions_list() |
||||
|
||||
pending_transactions_list_from_db |
||||
|> Enum.with_index() |
||||
|> Enum.each(fn {pending_tx, ind} -> |
||||
pending_tx_hash_str = "0x" <> Base.encode16(pending_tx.hash.bytes, case: :lower) |
||||
|
||||
with {:ok, result} <- |
||||
%{id: ind, method: "eth_getTransactionReceipt", params: [pending_tx_hash_str]} |
||||
|> request() |
||||
|> json_rpc(json_rpc_named_arguments) do |
||||
if result do |
||||
block_hash = Map.get(result, "blockHash") |
||||
|
||||
Logger.debug( |
||||
"Transaction #{pending_tx_hash_str} already included into the block #{block_hash}. We should invalidate consensus for it in order to re-fetch transactions", |
||||
fetcher: :pending_transactions_to_refetch |
||||
) |
||||
|
||||
fetch_block_and_invalidate(block_hash) |
||||
end |
||||
end |
||||
end) |
||||
|
||||
Logger.debug("Pending transactions are sanitized", |
||||
fetcher: :pending_transactions_to_refetch |
||||
) |
||||
end |
||||
|
||||
defp fetch_block_and_invalidate(block_hash) do |
||||
case Chain.fetch_block_by_hash(block_hash) do |
||||
%{number: number, consensus: consensus} -> |
||||
Logger.debug( |
||||
"Corresponding number of the block with hash #{block_hash} to invalidate is #{number} and consensus #{ |
||||
consensus |
||||
}", |
||||
fetcher: :pending_transactions_to_refetch |
||||
) |
||||
|
||||
invalidate_block(number, consensus) |
||||
|
||||
_ -> |
||||
Logger.debug( |
||||
"Block with hash #{block_hash} is not yet in the DB", |
||||
fetcher: :pending_transactions_to_refetch |
||||
) |
||||
end |
||||
end |
||||
|
||||
defp invalidate_block(number, consensus) do |
||||
if consensus do |
||||
opts = %{ |
||||
timeout: 60_000, |
||||
timestamps: %{updated_at: DateTime.utc_now()} |
||||
} |
||||
|
||||
Blocks.lose_consensus(Repo, [], [number], [], opts) |
||||
end |
||||
end |
||||
end |
Loading…
Reference in new issue