Blockchain explorer for Ethereum based network and a tool for inspecting and analyzing EVM based blockchains.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
blockscout/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth.ex

148 lines
5.2 KiB

defmodule EthereumJSONRPC.Geth do
@moduledoc """
Ethereum JSONRPC methods that are only supported by [Geth](https://github.com/ethereum/go-ethereum/wiki/geth).
"""
import EthereumJSONRPC, only: [id_to_params: 1, json_rpc: 2, request: 1]
alias EthereumJSONRPC.Geth.Calls
@behaviour EthereumJSONRPC.Variant
@doc """
Block reward contract beneficiary fetching is not supported currently for Geth.
To signal to the caller that fetching is not supported, `:ignore` is returned.
"""
@impl EthereumJSONRPC.Variant
def fetch_beneficiaries(_block_range, _json_rpc_named_arguments), do: :ignore
@doc """
Fetches the `t:Explorer.Chain.InternalTransaction.changeset/2` params.
"""
@impl EthereumJSONRPC.Variant
def fetch_internal_transactions(transactions_params, json_rpc_named_arguments) when is_list(transactions_params) do
id_to_params = id_to_params(transactions_params)
with {:ok, responses} <-
id_to_params
|> debug_trace_transaction_requests()
|> json_rpc(json_rpc_named_arguments) do
debug_trace_transaction_responses_to_internal_transactions_params(responses, id_to_params)
end
end
@doc """
Pending transaction fetching is not supported currently for Geth.
To signal to the caller that fetching is not supported, `:ignore` is returned.
"""
@impl EthereumJSONRPC.Variant
def fetch_pending_transactions(_json_rpc_named_arguments), do: :ignore
defp debug_trace_transaction_requests(id_to_params) when is_map(id_to_params) do
Enum.map(id_to_params, fn {id, %{hash_data: hash_data}} ->
debug_trace_transaction_request(%{id: id, hash_data: hash_data})
end)
end
@tracer_path "priv/js/ethereum_jsonrpc/geth/debug_traceTransaction/tracer.js"
@external_resource @tracer_path
@tracer File.read!(@tracer_path)
defp debug_trace_transaction_request(%{id: id, hash_data: hash_data}) do
request(%{id: id, method: "debug_traceTransaction", params: [hash_data, %{tracer: @tracer}]})
end
defp debug_trace_transaction_responses_to_internal_transactions_params(responses, id_to_params)
when is_list(responses) and is_map(id_to_params) do
responses
|> Enum.map(&debug_trace_transaction_response_to_internal_transactions_params(&1, id_to_params))
|> reduce_internal_transactions_params()
end
defp debug_trace_transaction_response_to_internal_transactions_params(%{id: id, result: calls}, id_to_params)
when is_map(id_to_params) do
%{block_number: block_number, hash_data: transaction_hash, transaction_index: transaction_index} =
Map.fetch!(id_to_params, id)
internal_transaction_params =
calls
|> Stream.with_index()
|> Enum.map(fn {trace, index} ->
Map.merge(trace, %{
"blockNumber" => block_number,
"index" => index,
"transactionIndex" => transaction_index,
"transactionHash" => transaction_hash
})
end)
|> Calls.to_internal_transactions_params()
{:ok, internal_transaction_params}
end
defp debug_trace_transaction_response_to_internal_transactions_params(%{id: id, error: error}, id_to_params)
when is_map(id_to_params) do
%{
block_number: block_number,
hash_data: "0x" <> transaction_hash_digits = transaction_hash,
transaction_index: transaction_index
} = Map.fetch!(id_to_params, id)
not_found_message = "transaction " <> transaction_hash_digits <> " not found"
normalized_error =
case error do
%{code: -32_000, message: ^not_found_message} ->
%{message: :not_found}
%{code: -32_000, message: "execution timeout"} ->
%{message: :timeout}
_ ->
error
end
annotated_error =
Map.put(normalized_error, :data, %{
block_number: block_number,
transaction_index: transaction_index,
transaction_hash: transaction_hash
})
{:error, annotated_error}
end
defp reduce_internal_transactions_params(internal_transactions_params) when is_list(internal_transactions_params) do
internal_transactions_params
|> Enum.reduce({:ok, []}, &internal_transactions_params_reducer/2)
|> finalize_internal_transactions_params()
end
defp internal_transactions_params_reducer(
{:ok, internal_transactions_params},
{:ok, acc_internal_transactions_params_list}
),
do: {:ok, [internal_transactions_params, acc_internal_transactions_params_list]}
defp internal_transactions_params_reducer({:ok, _}, {:error, _} = acc_error), do: acc_error
defp internal_transactions_params_reducer({:error, reason}, {:ok, _}), do: {:error, [reason]}
defp internal_transactions_params_reducer({:error, reason}, {:error, acc_reasons}) when is_list(acc_reasons),
do: {:error, [reason | acc_reasons]}
defp finalize_internal_transactions_params({:ok, acc_internal_transactions_params_list})
when is_list(acc_internal_transactions_params_list) do
internal_transactions_params =
acc_internal_transactions_params_list
|> Enum.reverse()
|> List.flatten()
{:ok, internal_transactions_params}
end
defp finalize_internal_transactions_params({:error, acc_reasons}) do
{:error, Enum.reverse(acc_reasons)}
end
end