Finalize Besu variant

vb-besu-support
Victor Baranov 5 years ago
parent e0ec581557
commit 41dc4bdb4d
  1. 16
      apps/block_scout_web/lib/block_scout_web/templates/layout/_topnav.html.eex
  2. 4
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex
  3. 287
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/besu.ex
  4. 2
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth.ex
  5. 2
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/parity.ex
  6. 5
      apps/explorer/config/config.exs
  7. 25
      apps/explorer/config/dev/besu.exs
  8. 25
      apps/explorer/config/prod/besu.exs
  9. 14
      apps/explorer/config/test/besu.exs
  10. 30
      apps/indexer/config/dev/besu.exs
  11. 29
      apps/indexer/config/prod/besu.exs
  12. 8
      apps/indexer/config/test/besu.exs
  13. 22
      apps/indexer/lib/indexer/fetcher/internal_transaction.ex

@ -55,12 +55,16 @@
class: "dropdown-item #{tab_status("txs", @conn.request_path)}",
to: transaction_path(@conn, :index)
) %>
<%= link(
gettext("Pending"),
class: "dropdown-item #{tab_status("pending_transactions", @conn.request_path)}",
"data-test": "pending_transactions_link",
to: pending_transaction_path(@conn, :index)
) %>
<% json_rpc_named_arguments = Application.fetch_env!(:indexer, :json_rpc_named_arguments)%>
<% variant = Keyword.fetch!(json_rpc_named_arguments, :variant) %>
<%= if variant !== EthereumJSONRPC.Besu do %>
<%= link(
gettext("Pending"),
class: "dropdown-item #{tab_status("pending_transactions", @conn.request_path)}",
"data-test": "pending_transactions_link",
to: pending_transaction_path(@conn, :index)
) %>
<% end %>
</div>
</li>
<li class="nav-item">

@ -295,7 +295,7 @@ defmodule EthereumJSONRPC do
Fetches internal transactions for entire blocks from variant API.
"""
def fetch_block_internal_transactions(params_list, json_rpc_named_arguments) when is_list(params_list) do
EthereumJSONRPC.Parity.fetch_block_internal_transactions(
Keyword.fetch!(json_rpc_named_arguments, :variant).fetch_block_internal_transactions(
params_list,
json_rpc_named_arguments
)
@ -315,7 +315,7 @@ defmodule EthereumJSONRPC do
Fetches pending transactions from variant API.
"""
def fetch_pending_transactions(json_rpc_named_arguments) do
EthereumJSONRPC.Parity.fetch_pending_transactions(json_rpc_named_arguments)
Keyword.fetch!(json_rpc_named_arguments, :variant).fetch_pending_transactions(json_rpc_named_arguments)
end
@spec fetch_transaction_receipts(

@ -0,0 +1,287 @@
defmodule EthereumJSONRPC.Besu do
@moduledoc """
Ethereum JSONRPC methods that are only supported by [Besu](https://besu.hyperledger.org/en/stable/Reference/API-Methods).
"""
require Logger
import EthereumJSONRPC, only: [id_to_params: 1, integer_to_quantity: 1, json_rpc: 2, request: 1]
alias EthereumJSONRPC.Parity.{Traces}
alias EthereumJSONRPC.{Transaction, Transactions}
@behaviour EthereumJSONRPC.Variant
@impl EthereumJSONRPC.Variant
def fetch_beneficiaries(_block_range, _json_rpc_named_arguments), do: :ignore
@impl EthereumJSONRPC.Variant
def fetch_internal_transactions(_transactions_params, _json_rpc_named_arguments), do: :ignore
@doc """
Fetches the `t:Explorer.Chain.InternalTransaction.changeset/2` params from the Besu trace URL.
"""
@impl EthereumJSONRPC.Variant
def fetch_block_internal_transactions(block_numbers, json_rpc_named_arguments) when is_list(block_numbers) do
id_to_params = id_to_params(block_numbers)
with {:ok, responses} <-
id_to_params
|> trace_replay_block_transactions_requests()
|> json_rpc(json_rpc_named_arguments) do
trace_replay_block_transactions_responses_to_internal_transactions_params(responses, id_to_params)
end
end
@impl EthereumJSONRPC.Variant
def fetch_first_trace(transactions_params, json_rpc_named_arguments) when is_list(transactions_params) do
id_to_params = id_to_params(transactions_params)
trace_replay_transaction_response =
id_to_params
|> trace_replay_transaction_requests()
|> json_rpc(json_rpc_named_arguments)
case trace_replay_transaction_response do
{:ok, responses} ->
case trace_replay_transaction_responses_to_first_trace_params(responses, id_to_params) do
{:ok, [first_trace]} ->
%{block_hash: block_hash} =
transactions_params
|> Enum.at(0)
{:ok,
[%{first_trace: first_trace, block_hash: block_hash, json_rpc_named_arguments: json_rpc_named_arguments}]}
{:error, error} ->
Logger.error(inspect(error))
{:error, error}
end
{:error, :econnrefused} ->
{:error, :econnrefused}
{:error, [error]} ->
Logger.error(inspect(error))
{:error, error}
end
end
@doc """
Fetches the pending transactions from the Besu node.
*NOTE*: The pending transactions are local to the node that is contacted and may not be consistent across nodes based
on the transactions that each node has seen and how each node prioritizes collating transactions into the next block.
"""
@impl EthereumJSONRPC.Variant
@spec fetch_pending_transactions(EthereumJSONRPC.json_rpc_named_arguments()) ::
{:ok, [Transaction.params()]} | {:error, reason :: term}
def fetch_pending_transactions(json_rpc_named_arguments) do
with {:ok, transactions} <-
%{id: 1, method: "txpool_besuTransactions", params: []}
|> request()
|> json_rpc(json_rpc_named_arguments) do
transactions_params =
transactions
|> Transactions.to_elixir()
|> Transactions.elixir_to_params()
{:ok, transactions_params}
end
end
defp trace_replay_block_transactions_responses_to_internal_transactions_params(responses, id_to_params)
when is_list(responses) and is_map(id_to_params) do
with {:ok, traces} <- trace_replay_block_transactions_responses_to_traces(responses, id_to_params) do
params =
traces
|> Traces.to_elixir()
|> Traces.elixir_to_params()
{:ok, params}
end
end
defp trace_replay_block_transactions_responses_to_traces(responses, id_to_params)
when is_list(responses) and is_map(id_to_params) do
responses
|> Enum.map(&trace_replay_block_transactions_response_to_traces(&1, id_to_params))
|> Enum.reduce(
{:ok, []},
fn
{:ok, traces}, {:ok, acc_traces_list} ->
{:ok, [traces | acc_traces_list]}
{:ok, _}, {:error, _} = acc_error ->
acc_error
{:error, reason}, {:ok, _} ->
{:error, [reason]}
{:error, reason}, {:error, acc_reason} ->
{:error, [reason | acc_reason]}
end
)
|> case do
{:ok, traces_list} ->
traces =
traces_list
|> Enum.reverse()
|> List.flatten()
{:ok, traces}
{:error, reverse_reasons} ->
reasons = Enum.reverse(reverse_reasons)
{:error, reasons}
end
end
defp trace_replay_block_transactions_response_to_traces(%{id: id, result: results}, id_to_params)
when is_list(results) and is_map(id_to_params) do
block_number = Map.fetch!(id_to_params, id)
annotated_traces =
results
|> Stream.with_index()
|> Enum.flat_map(fn {%{"trace" => traces, "transactionHash" => transaction_hash}, transaction_index} ->
traces
|> Stream.with_index()
|> Enum.map(fn {trace, index} ->
Map.merge(trace, %{
"blockNumber" => block_number,
"transactionHash" => transaction_hash,
"transactionIndex" => transaction_index,
"index" => index
})
end)
end)
{:ok, annotated_traces}
end
defp trace_replay_block_transactions_response_to_traces(%{id: id, error: error}, id_to_params)
when is_map(id_to_params) do
block_number = Map.fetch!(id_to_params, id)
annotated_error =
Map.put(error, :data, %{
"blockNumber" => block_number
})
{:error, annotated_error}
end
defp trace_replay_block_transactions_requests(id_to_params) when is_map(id_to_params) do
Enum.map(id_to_params, fn {id, block_number} ->
trace_replay_block_transactions_request(%{id: id, block_number: block_number})
end)
end
defp trace_replay_block_transactions_request(%{id: id, block_number: block_number}) do
request(%{id: id, method: "trace_replayBlockTransactions", params: [integer_to_quantity(block_number), ["trace"]]})
end
def trace_replay_transaction_responses_to_first_trace_params(responses, id_to_params)
when is_list(responses) and is_map(id_to_params) do
with {:ok, traces} <- trace_replay_transaction_responses_to_first_trace(responses, id_to_params) do
params =
traces
|> Traces.to_elixir()
|> Traces.elixir_to_params()
{:ok, params}
end
end
defp trace_replay_transaction_responses_to_first_trace(responses, id_to_params)
when is_list(responses) and is_map(id_to_params) do
responses
|> Enum.map(&trace_replay_transaction_response_to_first_trace(&1, id_to_params))
|> Enum.reduce(
{:ok, []},
fn
{:ok, traces}, {:ok, acc_traces_list} ->
{:ok, [traces | acc_traces_list]}
{:ok, _}, {:error, _} = acc_error ->
acc_error
{:error, reason}, {:ok, _} ->
{:error, [reason]}
{:error, reason}, {:error, acc_reason} ->
{:error, [reason | acc_reason]}
end
)
|> case do
{:ok, traces_list} ->
traces =
traces_list
|> Enum.reverse()
|> List.flatten()
{:ok, traces}
{:error, reverse_reasons} ->
reasons = Enum.reverse(reverse_reasons)
{:error, reasons}
end
end
defp trace_replay_transaction_response_to_first_trace(%{id: id, result: %{"trace" => traces}}, id_to_params)
when is_list(traces) and is_map(id_to_params) do
%{
block_hash: block_hash,
block_number: block_number,
hash_data: transaction_hash,
transaction_index: transaction_index
} = Map.fetch!(id_to_params, id)
first_trace =
traces
|> Stream.with_index()
|> Enum.map(fn {trace, index} ->
Map.merge(trace, %{
"blockHash" => block_hash,
"blockNumber" => block_number,
"index" => index,
"transactionIndex" => transaction_index,
"transactionHash" => transaction_hash
})
end)
|> Enum.filter(fn trace ->
Map.get(trace, "index") == 0
end)
{:ok, first_trace}
end
defp trace_replay_transaction_response_to_first_trace(%{id: id, error: error}, id_to_params)
when is_map(id_to_params) do
%{
block_hash: block_hash,
block_number: block_number,
hash_data: transaction_hash,
transaction_index: transaction_index
} = Map.fetch!(id_to_params, id)
annotated_error =
Map.put(error, :data, %{
"blockHash" => block_hash,
"blockNumber" => block_number,
"transactionIndex" => transaction_index,
"transactionHash" => transaction_hash
})
{:error, annotated_error}
end
defp trace_replay_transaction_requests(id_to_params) when is_map(id_to_params) do
Enum.map(id_to_params, fn {id, %{hash_data: hash_data}} ->
trace_replay_transaction_request(%{id: id, hash_data: hash_data})
end)
end
defp trace_replay_transaction_request(%{id: id, hash_data: hash_data}) do
request(%{id: id, method: "trace_replayTransaction", params: [hash_data, ["trace"]]})
end
end

@ -38,7 +38,7 @@ defmodule EthereumJSONRPC.Geth do
end
@doc """
Fetches the first trace from the Parity trace URL.
Fetches the first trace from the trace URL.
"""
@impl EthereumJSONRPC.Variant
def fetch_first_trace(_transactions_params, _json_rpc_named_arguments), do: :ignore

@ -94,7 +94,7 @@ defmodule EthereumJSONRPC.Parity do
{:ok, [Transaction.params()]} | {:error, reason :: term}
def fetch_pending_transactions(json_rpc_named_arguments) do
with {:ok, transactions} <-
%{id: 1, method: "txpool_besuTransactions", params: []}
%{id: 1, method: "parity_pendingTransactions", params: []}
|> request()
|> json_rpc(json_rpc_named_arguments) do
transactions_params =

@ -207,6 +207,11 @@ config :explorer, Explorer.Chain.Cache.Accounts,
global_ttl: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(5))
config :explorer, Explorer.Chain.Cache.PendingTransactions,
enabled:
if(System.get_env("ETHEREUM_JSONRPC_VARIANT") == "besu",
do: false,
else: true
),
ttl_check_interval: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(1), else: false),
global_ttl: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(5))

@ -0,0 +1,25 @@
use Mix.Config
config :explorer,
json_rpc_named_arguments: [
transport: EthereumJSONRPC.HTTP,
transport_options: [
http: EthereumJSONRPC.HTTP.HTTPoison,
url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "http://localhost:8545",
method_to_url: [
eth_call: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545",
eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545",
trace_replayTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545"
],
http_options: [recv_timeout: :timer.minutes(1), timeout: :timer.minutes(1), hackney: [pool: :ethereum_jsonrpc]]
],
variant: EthereumJSONRPC.Besu
],
subscribe_named_arguments: [
transport: EthereumJSONRPC.WebSocket,
transport_options: [
web_socket: EthereumJSONRPC.WebSocket.WebSocketClient,
url: System.get_env("ETHEREUM_JSONRPC_WS_URL")
],
variant: EthereumJSONRPC.Besu
]

@ -0,0 +1,25 @@
use Mix.Config
config :explorer,
json_rpc_named_arguments: [
transport: EthereumJSONRPC.HTTP,
transport_options: [
http: EthereumJSONRPC.HTTP.HTTPoison,
url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL"),
method_to_url: [
eth_call: System.get_env("ETHEREUM_JSONRPC_TRACE_URL"),
eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL"),
trace_replayTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL")
],
http_options: [recv_timeout: :timer.minutes(1), timeout: :timer.minutes(1), hackney: [pool: :ethereum_jsonrpc]]
],
variant: EthereumJSONRPC.Besu
],
subscribe_named_arguments: [
transport: EthereumJSONRPC.WebSocket,
transport_options: [
web_socket: EthereumJSONRPC.WebSocket.WebSocketClient,
url: System.get_env("ETHEREUM_JSONRPC_WS_URL")
],
variant: EthereumJSONRPC.Besu
]

@ -0,0 +1,14 @@
use Mix.Config
config :explorer,
transport: EthereumJSONRPC.HTTP,
json_rpc_named_arguments: [
transport: EthereumJSONRPC.Mox,
transport_options: [],
variant: EthereumJSONRPC.Besu
],
subscribe_named_arguments: [
transport: EthereumJSONRPC.Mox,
transport_options: [],
variant: EthereumJSONRPC.Besu
]

@ -0,0 +1,30 @@
use Mix.Config
config :indexer,
block_interval: :timer.seconds(5),
json_rpc_named_arguments: [
transport:
if(System.get_env("ETHEREUM_JSONRPC_TRANSPORT", "http") == "http",
do: EthereumJSONRPC.HTTP,
else: EthereumJSONRPC.IPC
),
else: EthereumJSONRPC.IPC,
transport_options: [
http: EthereumJSONRPC.HTTP.HTTPoison,
url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "http://localhost:8545",
method_to_url: [
eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545",
trace_block: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545",
trace_replayBlockTransactions: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545"
],
http_options: [recv_timeout: :timer.minutes(10), timeout: :timer.minutes(10), hackney: [pool: :ethereum_jsonrpc]]
],
variant: EthereumJSONRPC.Besu
],
subscribe_named_arguments: [
transport: System.get_env("ETHEREUM_JSONRPC_WS_URL") && EthereumJSONRPC.WebSocket,
transport_options: [
web_socket: EthereumJSONRPC.WebSocket.WebSocketClient,
url: System.get_env("ETHEREUM_JSONRPC_WS_URL")
]
]

@ -0,0 +1,29 @@
use Mix.Config
config :indexer,
block_interval: :timer.seconds(5),
json_rpc_named_arguments: [
transport:
if(System.get_env("ETHEREUM_JSONRPC_TRANSPORT", "http") == "http",
do: EthereumJSONRPC.HTTP,
else: EthereumJSONRPC.IPC
),
transport_options: [
http: EthereumJSONRPC.HTTP.HTTPoison,
url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL"),
method_to_url: [
eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL"),
trace_block: System.get_env("ETHEREUM_JSONRPC_TRACE_URL"),
trace_replayTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL")
],
http_options: [recv_timeout: :timer.minutes(10), timeout: :timer.minutes(10), hackney: [pool: :ethereum_jsonrpc]]
],
variant: EthereumJSONRPC.Besu
],
subscribe_named_arguments: [
transport: System.get_env("ETHEREUM_JSONRPC_WS_URL") && EthereumJSONRPC.WebSocket,
transport_options: [
web_socket: EthereumJSONRPC.WebSocket.WebSocketClient,
url: System.get_env("ETHEREUM_JSONRPC_WS_URL")
]
]

@ -0,0 +1,8 @@
use Mix.Config
config :indexer,
json_rpc_named_arguments: [
transport: EthereumJSONRPC.Mox,
transport_options: [],
variant: EthereumJSONRPC.Besu
]

@ -99,16 +99,18 @@ defmodule Indexer.Fetcher.InternalTransaction do
Logger.debug("fetching internal transactions for blocks")
# json_rpc_named_arguments
# |> Keyword.fetch!(:variant)
# |> case do
# EthereumJSONRPC.Parity ->
# EthereumJSONRPC.fetch_block_internal_transactions(unique_numbers, json_rpc_named_arguments)
# _ ->
# fetch_block_internal_transactions_by_transactions(unique_numbers, json_rpc_named_arguments)
# end
EthereumJSONRPC.fetch_block_internal_transactions(unique_numbers, json_rpc_named_arguments)
json_rpc_named_arguments
|> Keyword.fetch!(:variant)
|> case do
EthereumJSONRPC.Parity ->
EthereumJSONRPC.fetch_block_internal_transactions(unique_numbers, json_rpc_named_arguments)
EthereumJSONRPC.Besu ->
EthereumJSONRPC.fetch_block_internal_transactions(unique_numbers, json_rpc_named_arguments)
_ ->
fetch_block_internal_transactions_by_transactions(unique_numbers, json_rpc_named_arguments)
end
|> case do
{:ok, internal_transactions_params} ->
import_internal_transaction(internal_transactions_params, unique_numbers)

Loading…
Cancel
Save