parent
b5978a1f87
commit
0f4a4bd6a7
@ -1,178 +0,0 @@ |
|||||||
defmodule EthereumJSONRPC.Besu.FetchedBeneficiaries do |
|
||||||
@moduledoc """ |
|
||||||
Beneficiaries and errors from batch requests to `trace_block`. |
|
||||||
""" |
|
||||||
|
|
||||||
import EthereumJSONRPC, only: [quantity_to_integer: 1] |
|
||||||
|
|
||||||
@doc """ |
|
||||||
Converts `responses` to `EthereumJSONRPC.FetchedBeneficiaries.t()`. |
|
||||||
|
|
||||||
responses - List with trace_block responses |
|
||||||
id_to_params - Maps request id to query params |
|
||||||
|
|
||||||
## Examples |
|
||||||
iex> EthereumJSONRPC.Besu.FetchedBeneficiaries.from_responses( |
|
||||||
...> [ |
|
||||||
...> %{ |
|
||||||
...> id: 0, |
|
||||||
...> result: [ |
|
||||||
...> %{ |
|
||||||
...> "action" => %{"author" => "0x1", "rewardType" => "external", "value" => "0x0"}, |
|
||||||
...> "blockHash" => "0xFFF", |
|
||||||
...> "blockNumber" => 12, |
|
||||||
...> "result" => nil, |
|
||||||
...> "subtraces" => 0, |
|
||||||
...> "traceAddress" => [], |
|
||||||
...> "transactionHash" => nil, |
|
||||||
...> "transactionPosition" => nil, |
|
||||||
...> "type" => "reward" |
|
||||||
...> }, |
|
||||||
...> %{ |
|
||||||
...> "action" => %{"author" => "0x2", "rewardType" => "external", "value" => "0x0"}, |
|
||||||
...> "blockHash" => "0xFFF", |
|
||||||
...> "blockNumber" => 12, |
|
||||||
...> "result" => nil, |
|
||||||
...> "subtraces" => 0, |
|
||||||
...> "traceAddress" => [], |
|
||||||
...> "transactionHash" => nil, |
|
||||||
...> "transactionPosition" => nil, |
|
||||||
...> "type" => "reward" |
|
||||||
...> } |
|
||||||
...> ] |
|
||||||
...> } |
|
||||||
...> ], |
|
||||||
...> %{0 => %{block_quantity: "0xC"}} |
|
||||||
...> ) |
|
||||||
%EthereumJSONRPC.FetchedBeneficiaries{ |
|
||||||
errors: [], |
|
||||||
params_set: #MapSet<[ |
|
||||||
%{ |
|
||||||
address_hash: "0x1", |
|
||||||
address_type: :validator, |
|
||||||
block_hash: "0xFFF", |
|
||||||
block_number: 12, |
|
||||||
reward: "0x0" |
|
||||||
}, |
|
||||||
%{ |
|
||||||
address_hash: "0x2", |
|
||||||
address_type: :emission_funds, |
|
||||||
block_hash: "0xFFF", |
|
||||||
block_number: 12, |
|
||||||
reward: "0x0" |
|
||||||
} |
|
||||||
]> |
|
||||||
} |
|
||||||
""" |
|
||||||
def from_responses(responses, id_to_params) when is_list(responses) and is_map(id_to_params) do |
|
||||||
responses |
|
||||||
|> Enum.map(&response_to_params_set(&1, id_to_params)) |
|
||||||
|> Enum.reduce( |
|
||||||
%EthereumJSONRPC.FetchedBeneficiaries{}, |
|
||||||
fn |
|
||||||
{:ok, params_set}, %EthereumJSONRPC.FetchedBeneficiaries{params_set: acc_params_set} = acc -> |
|
||||||
%EthereumJSONRPC.FetchedBeneficiaries{acc | params_set: MapSet.union(acc_params_set, params_set)} |
|
||||||
|
|
||||||
{:error, reason}, %EthereumJSONRPC.FetchedBeneficiaries{errors: errors} = acc -> |
|
||||||
%EthereumJSONRPC.FetchedBeneficiaries{acc | errors: [reason | errors]} |
|
||||||
end |
|
||||||
) |
|
||||||
end |
|
||||||
|
|
||||||
@doc """ |
|
||||||
`trace_block` requests for `id_to_params`. |
|
||||||
""" |
|
||||||
def requests(id_to_params) when is_map(id_to_params) do |
|
||||||
Enum.map(id_to_params, fn {id, %{block_quantity: block_quantity}} -> |
|
||||||
request(%{id: id, block_quantity: block_quantity}) |
|
||||||
end) |
|
||||||
end |
|
||||||
|
|
||||||
@spec response_to_params_set(%{id: id, result: nil}, %{id => %{block_quantity: block_quantity}}) :: |
|
||||||
{:error, %{code: 404, message: String.t(), data: %{block_quantity: block_quantity}}} |
|
||||||
when id: non_neg_integer(), block_quantity: String.t() |
|
||||||
defp response_to_params_set(%{id: id, result: nil}, id_to_params) when is_map(id_to_params) do |
|
||||||
%{block_quantity: block_quantity} = Map.fetch!(id_to_params, id) |
|
||||||
|
|
||||||
{:error, %{code: 404, message: "Not Found", data: %{block_quantity: block_quantity}}} |
|
||||||
end |
|
||||||
|
|
||||||
@spec response_to_params_set(%{id: id, result: list(map())}, %{id => %{block_quantity: block_quantity}}) :: |
|
||||||
{:ok, MapSet.t(EthereumJSONRPC.FetchedBeneficiary.params())} |
|
||||||
when id: non_neg_integer(), block_quantity: String.t() |
|
||||||
defp response_to_params_set(%{id: id, result: traces}, id_to_params) when is_list(traces) and is_map(id_to_params) do |
|
||||||
%{block_quantity: block_quantity} = Map.fetch!(id_to_params, id) |
|
||||||
block_number = quantity_to_integer(block_quantity) |
|
||||||
params_set = traces_to_params_set(traces, block_number) |
|
||||||
|
|
||||||
{:ok, params_set} |
|
||||||
end |
|
||||||
|
|
||||||
@spec response_to_params_set(%{id: id, error: %{code: code, message: message}}, %{ |
|
||||||
id => %{block_quantity: block_quantity} |
|
||||||
}) :: {:error, %{code: code, message: message, data: %{block_quantity: block_quantity}}} |
|
||||||
when id: non_neg_integer(), code: integer(), message: String.t(), block_quantity: String.t() |
|
||||||
defp response_to_params_set(%{id: id, error: error}, id_to_params) when is_map(id_to_params) do |
|
||||||
%{block_quantity: block_quantity} = Map.fetch!(id_to_params, id) |
|
||||||
|
|
||||||
annotated_error = Map.put(error, :data, %{block_quantity: block_quantity}) |
|
||||||
|
|
||||||
{:error, annotated_error} |
|
||||||
end |
|
||||||
|
|
||||||
defp request(%{id: id, block_quantity: block_quantity}) when is_integer(id) and is_binary(block_quantity) do |
|
||||||
EthereumJSONRPC.request(%{id: id, method: "trace_block", params: [block_quantity]}) |
|
||||||
end |
|
||||||
|
|
||||||
defp traces_to_params_set(traces, block_number) when is_list(traces) and is_integer(block_number) do |
|
||||||
traces |
|
||||||
|> Stream.filter(&(&1["type"] == "reward")) |
|
||||||
|> Stream.with_index() |
|
||||||
|> Enum.reduce(MapSet.new(), fn {trace, index}, acc -> |
|
||||||
MapSet.union(acc, trace_to_params_set(trace, block_number, index)) |
|
||||||
end) |
|
||||||
end |
|
||||||
|
|
||||||
defp trace_to_params_set( |
|
||||||
%{ |
|
||||||
"action" => %{ |
|
||||||
"rewardType" => reward_type, |
|
||||||
"author" => address_hash_data, |
|
||||||
"value" => reward_value |
|
||||||
}, |
|
||||||
"blockHash" => block_hash, |
|
||||||
"blockNumber" => block_number |
|
||||||
}, |
|
||||||
block_number, |
|
||||||
index |
|
||||||
) |
|
||||||
when is_integer(block_number) and reward_type in ~w(block external uncle) do |
|
||||||
MapSet.new([ |
|
||||||
%{ |
|
||||||
address_hash: address_hash_data, |
|
||||||
block_hash: block_hash, |
|
||||||
block_number: block_number, |
|
||||||
reward: reward_value, |
|
||||||
address_type: get_address_type(reward_type, index) |
|
||||||
} |
|
||||||
]) |
|
||||||
end |
|
||||||
|
|
||||||
# Beneficiary's address type will depend on the responses' action.rewardType, |
|
||||||
# which will vary depending on which network is being indexed |
|
||||||
# |
|
||||||
# On POA networks, rewardType will always be external and the type of the address being |
|
||||||
# rewarded will depend on its position. |
|
||||||
# First address will always be the validator's while the second will be the EmissionsFunds address |
|
||||||
# |
|
||||||
# On PoW networks, like Ethereum, the reward type will already specify the type for the |
|
||||||
# address being rewarded |
|
||||||
# The rewardType "block" will show the reward for the consensus block validator |
|
||||||
# The rewardType "uncle" will show reward for validating an uncle block |
|
||||||
defp get_address_type(reward_type, index) when reward_type == "external" and index == 0, do: :validator |
|
||||||
defp get_address_type(reward_type, index) when reward_type == "external" and index == 1, do: :emission_funds |
|
||||||
defp get_address_type(reward_type, index) when reward_type == "external" and index >= 2, do: :validator |
|
||||||
defp get_address_type(reward_type, _index) when reward_type == "block", do: :validator |
|
||||||
defp get_address_type(reward_type, _index) when reward_type == "uncle", do: :uncle |
|
||||||
defp get_address_type(reward_type, _index) when reward_type == "emptyStep", do: :validator |
|
||||||
end |
|
@ -1,184 +0,0 @@ |
|||||||
defmodule EthereumJSONRPC.Nethermind.FetchedBeneficiaries do |
|
||||||
@moduledoc """ |
|
||||||
Beneficiaries and errors from batch requests to `trace_block`. |
|
||||||
""" |
|
||||||
|
|
||||||
import EthereumJSONRPC, only: [quantity_to_integer: 1] |
|
||||||
|
|
||||||
@doc """ |
|
||||||
Converts `responses` to `EthereumJSONRPC.FetchedBeneficiaries.t()`. |
|
||||||
|
|
||||||
responses - List with trace_block responses |
|
||||||
id_to_params - Maps request id to query params |
|
||||||
|
|
||||||
## Examples |
|
||||||
iex> EthereumJSONRPC.Nethermind.FetchedBeneficiaries.from_responses( |
|
||||||
...> [ |
|
||||||
...> %{ |
|
||||||
...> id: 0, |
|
||||||
...> result: [ |
|
||||||
...> %{ |
|
||||||
...> "action" => %{"author" => "0x1", "rewardType" => "external", "value" => "0x0"}, |
|
||||||
...> "blockHash" => "0xFFF", |
|
||||||
...> "blockNumber" => 12, |
|
||||||
...> "result" => nil, |
|
||||||
...> "subtraces" => 0, |
|
||||||
...> "traceAddress" => [], |
|
||||||
...> "transactionHash" => nil, |
|
||||||
...> "transactionPosition" => nil, |
|
||||||
...> "type" => "reward" |
|
||||||
...> }, |
|
||||||
...> %{ |
|
||||||
...> "action" => %{"author" => "0x2", "rewardType" => "external", "value" => "0x0"}, |
|
||||||
...> "blockHash" => "0xFFF", |
|
||||||
...> "blockNumber" => 12, |
|
||||||
...> "result" => nil, |
|
||||||
...> "subtraces" => 0, |
|
||||||
...> "traceAddress" => [], |
|
||||||
...> "transactionHash" => nil, |
|
||||||
...> "transactionPosition" => nil, |
|
||||||
...> "type" => "reward" |
|
||||||
...> } |
|
||||||
...> ] |
|
||||||
...> } |
|
||||||
...> ], |
|
||||||
...> %{0 => %{block_quantity: "0xC"}} |
|
||||||
...> ) |
|
||||||
%EthereumJSONRPC.FetchedBeneficiaries{ |
|
||||||
errors: [], |
|
||||||
params_set: #MapSet<[ |
|
||||||
%{ |
|
||||||
address_hash: "0x1", |
|
||||||
address_type: :validator, |
|
||||||
block_hash: "0xFFF", |
|
||||||
block_number: 12, |
|
||||||
reward: "0x0" |
|
||||||
}, |
|
||||||
%{ |
|
||||||
address_hash: "0x2", |
|
||||||
address_type: :emission_funds, |
|
||||||
block_hash: "0xFFF", |
|
||||||
block_number: 12, |
|
||||||
reward: "0x0" |
|
||||||
} |
|
||||||
]> |
|
||||||
} |
|
||||||
""" |
|
||||||
def from_responses(responses, id_to_params) when is_list(responses) and is_map(id_to_params) do |
|
||||||
responses |
|
||||||
|> Enum.map(&response_to_params_set(&1, id_to_params)) |
|
||||||
|> Enum.reduce( |
|
||||||
%EthereumJSONRPC.FetchedBeneficiaries{}, |
|
||||||
fn |
|
||||||
{:ok, params_set}, %EthereumJSONRPC.FetchedBeneficiaries{params_set: acc_params_set} = acc -> |
|
||||||
%EthereumJSONRPC.FetchedBeneficiaries{acc | params_set: MapSet.union(acc_params_set, params_set)} |
|
||||||
|
|
||||||
{:error, reason}, %EthereumJSONRPC.FetchedBeneficiaries{errors: errors} = acc -> |
|
||||||
%EthereumJSONRPC.FetchedBeneficiaries{acc | errors: [reason | errors]} |
|
||||||
end |
|
||||||
) |
|
||||||
end |
|
||||||
|
|
||||||
@doc """ |
|
||||||
`trace_block` requests for `id_to_params`. |
|
||||||
""" |
|
||||||
def requests(id_to_params) when is_map(id_to_params) do |
|
||||||
id_to_params |
|
||||||
|> Enum.map(fn {id, %{block_quantity: block_quantity}} -> |
|
||||||
request(%{id: id, block_quantity: block_quantity}) |
|
||||||
end) |
|
||||||
|> Enum.filter(&(!is_nil(&1))) |
|
||||||
end |
|
||||||
|
|
||||||
@spec response_to_params_set(%{id: id, result: nil}, %{id => %{block_quantity: block_quantity}}) :: |
|
||||||
{:error, %{code: 404, message: String.t(), data: %{block_quantity: block_quantity}}} |
|
||||||
when id: non_neg_integer(), block_quantity: String.t() |
|
||||||
defp response_to_params_set(%{id: id, result: nil}, id_to_params) when is_map(id_to_params) do |
|
||||||
%{block_quantity: block_quantity} = Map.fetch!(id_to_params, id) |
|
||||||
|
|
||||||
{:error, %{code: 404, message: "Not Found", data: %{block_quantity: block_quantity}}} |
|
||||||
end |
|
||||||
|
|
||||||
@spec response_to_params_set(%{id: id, result: list(map())}, %{id => %{block_quantity: block_quantity}}) :: |
|
||||||
{:ok, MapSet.t(EthereumJSONRPC.FetchedBeneficiary.params())} |
|
||||||
when id: non_neg_integer(), block_quantity: String.t() |
|
||||||
defp response_to_params_set(%{id: id, result: traces}, id_to_params) when is_list(traces) and is_map(id_to_params) do |
|
||||||
%{block_quantity: block_quantity} = Map.fetch!(id_to_params, id) |
|
||||||
block_number = quantity_to_integer(block_quantity) |
|
||||||
params_set = traces_to_params_set(traces, block_number) |
|
||||||
|
|
||||||
{:ok, params_set} |
|
||||||
end |
|
||||||
|
|
||||||
@spec response_to_params_set(%{id: id, error: %{code: code, message: message}}, %{ |
|
||||||
id => %{block_quantity: block_quantity} |
|
||||||
}) :: {:error, %{code: code, message: message, data: %{block_quantity: block_quantity}}} |
|
||||||
when id: non_neg_integer(), code: integer(), message: String.t(), block_quantity: String.t() |
|
||||||
defp response_to_params_set(%{id: id, error: error}, id_to_params) when is_map(id_to_params) do |
|
||||||
%{block_quantity: block_quantity} = Map.fetch!(id_to_params, id) |
|
||||||
|
|
||||||
annotated_error = Map.put(error, :data, %{block_quantity: block_quantity}) |
|
||||||
|
|
||||||
{:error, annotated_error} |
|
||||||
end |
|
||||||
|
|
||||||
defp request(%{id: id, block_quantity: block_quantity}) when is_integer(id) and is_binary(block_quantity) do |
|
||||||
if block_quantity == "0x0" do |
|
||||||
nil |
|
||||||
else |
|
||||||
EthereumJSONRPC.request(%{id: id, method: "trace_block", params: [block_quantity]}) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
defp traces_to_params_set(traces, block_number) when is_list(traces) and is_integer(block_number) do |
|
||||||
traces |
|
||||||
|> Stream.filter(&(&1["type"] == "reward")) |
|
||||||
|> Stream.with_index() |
|
||||||
|> Enum.reduce(MapSet.new(), fn {trace, index}, acc -> |
|
||||||
MapSet.union(acc, trace_to_params_set(trace, block_number, index)) |
|
||||||
end) |
|
||||||
end |
|
||||||
|
|
||||||
defp trace_to_params_set( |
|
||||||
%{ |
|
||||||
"action" => %{ |
|
||||||
"rewardType" => reward_type, |
|
||||||
"author" => address_hash_data, |
|
||||||
"value" => reward_value |
|
||||||
}, |
|
||||||
"blockHash" => block_hash, |
|
||||||
"blockNumber" => block_number |
|
||||||
}, |
|
||||||
block_number, |
|
||||||
index |
|
||||||
) |
|
||||||
when is_integer(block_number) and reward_type in ~w(block external uncle) do |
|
||||||
MapSet.new([ |
|
||||||
%{ |
|
||||||
address_hash: address_hash_data, |
|
||||||
block_hash: block_hash, |
|
||||||
block_number: block_number, |
|
||||||
reward: reward_value, |
|
||||||
address_type: get_address_type(reward_type, index) |
|
||||||
} |
|
||||||
]) |
|
||||||
end |
|
||||||
|
|
||||||
# Beneficiary's address type will depend on the responses' action.rewardType, |
|
||||||
# which will vary depending on which network is being indexed |
|
||||||
# |
|
||||||
# On POA networks, rewardType will always be external and the type of the address being |
|
||||||
# rewarded will depend on its position. |
|
||||||
# First address will always be the validator's while the second will be the EmissionsFunds address |
|
||||||
# |
|
||||||
# On PoW networks, like Ethereum, the reward type will already specify the type for the |
|
||||||
# address being rewarded |
|
||||||
# The rewardType "block" will show the reward for the consensus block validator |
|
||||||
# The rewardType "uncle" will show reward for validating an uncle block |
|
||||||
defp get_address_type(reward_type, index) when reward_type == "external" and index == 0, do: :validator |
|
||||||
defp get_address_type(reward_type, index) when reward_type == "external" and index == 1, do: :emission_funds |
|
||||||
defp get_address_type(reward_type, index) when reward_type == "external" and index >= 2, do: :validator |
|
||||||
defp get_address_type(reward_type, _index) when reward_type == "block", do: :validator |
|
||||||
defp get_address_type(reward_type, _index) when reward_type == "uncle", do: :uncle |
|
||||||
defp get_address_type(reward_type, _index) when reward_type == "emptyStep", do: :validator |
|
||||||
end |
|
@ -0,0 +1,71 @@ |
|||||||
|
defmodule EthereumJSONRPC.PendingTransaction do |
||||||
|
@moduledoc """ |
||||||
|
Defines pending transactions fetching functions |
||||||
|
""" |
||||||
|
|
||||||
|
import EthereumJSONRPC, only: [json_rpc: 2, request: 1] |
||||||
|
alias EthereumJSONRPC.{Transaction, Transactions} |
||||||
|
|
||||||
|
@doc """ |
||||||
|
Geth-style fetching of pending transactions (from `txpool_content`) |
||||||
|
""" |
||||||
|
@spec fetch_pending_transactions_geth(EthereumJSONRPC.json_rpc_named_arguments()) :: |
||||||
|
{:ok, [Transaction.params()]} | {:error, reason :: term} |
||||||
|
def fetch_pending_transactions_geth(json_rpc_named_arguments) do |
||||||
|
with {:ok, transaction_data} <- |
||||||
|
%{id: 1, method: "txpool_content", params: []} |> request() |> json_rpc(json_rpc_named_arguments) do |
||||||
|
transactions_params = |
||||||
|
transaction_data["pending"] |
||||||
|
|> Enum.flat_map(fn {_address, nonce_transactions_map} -> |
||||||
|
nonce_transactions_map |
||||||
|
|> Enum.map(fn {_nonce, transaction} -> |
||||||
|
transaction |
||||||
|
end) |
||||||
|
end) |
||||||
|
|> Transactions.to_elixir() |
||||||
|
|> Transactions.elixir_to_params() |
||||||
|
|> Enum.map(fn params -> |
||||||
|
# txpool_content always returns transaction with 0x0000000000000000000000000000000000000000000000000000000000000000 value in block hash and index is null. |
||||||
|
# https://github.com/ethereum/go-ethereum/issues/19897 |
||||||
|
%{params | block_hash: nil, index: nil} |
||||||
|
end) |
||||||
|
|
||||||
|
{:ok, transactions_params} |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
@doc """ |
||||||
|
Зфкшен-style fetching of pending transactions (from `parity_pendingTransactions`) |
||||||
|
""" |
||||||
|
@spec fetch_pending_transactions_parity(EthereumJSONRPC.json_rpc_named_arguments()) :: |
||||||
|
{:ok, [Transaction.params()]} | {:error, reason :: term} |
||||||
|
def fetch_pending_transactions_parity(json_rpc_named_arguments) do |
||||||
|
with {:ok, transactions} <- |
||||||
|
%{id: 1, method: "parity_pendingTransactions", params: []} |
||||||
|
|> request() |
||||||
|
|> json_rpc(json_rpc_named_arguments) do |
||||||
|
transactions_params = |
||||||
|
transactions |
||||||
|
|> Transactions.to_elixir() |
||||||
|
|> Transactions.elixir_to_params() |
||||||
|
|
||||||
|
{:ok, transactions_params} |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
@spec fetch_pending_transactions_besu(EthereumJSONRPC.json_rpc_named_arguments()) :: |
||||||
|
{:ok, [Transaction.params()]} | {:error, reason :: term} |
||||||
|
def fetch_pending_transactions_besu(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 |
||||||
|
end |
@ -0,0 +1,250 @@ |
|||||||
|
defmodule EthereumJSONRPC.TraceReplayBlockTransactions do |
||||||
|
@moduledoc """ |
||||||
|
Methods for processing the data from `trace_replayBlockTransactions` JSON RPC method |
||||||
|
""" |
||||||
|
require Logger |
||||||
|
import EthereumJSONRPC, only: [id_to_params: 1, integer_to_quantity: 1, json_rpc: 2, request: 1] |
||||||
|
|
||||||
|
def fetch_first_trace(transactions_params, json_rpc_named_arguments, traces_module) |
||||||
|
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, traces_module) 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 |
||||||
|
|
||||||
|
def fetch_block_internal_transactions(block_numbers, json_rpc_named_arguments, traces_module) |
||||||
|
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, traces_module) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
defp trace_replay_block_transactions_responses_to_internal_transactions_params(responses, id_to_params, traces_module) |
||||||
|
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_module.to_elixir() |
||||||
|
|> traces_module.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, traces_module) |
||||||
|
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_module.to_elixir() |
||||||
|
|> traces_module.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 |
@ -1,8 +1,8 @@ |
|||||||
defmodule EthereumJSONRPC.Nethermind.FetchedBeneficiariesTest do |
defmodule EthereumJSONRPC.FetchedBeneficiariesTest do |
||||||
use ExUnit.Case, async: true |
use ExUnit.Case, async: true |
||||||
|
|
||||||
alias EthereumJSONRPC |
alias EthereumJSONRPC |
||||||
alias EthereumJSONRPC.Nethermind.FetchedBeneficiaries |
alias EthereumJSONRPC.FetchedBeneficiaries |
||||||
|
|
||||||
describe "from_responses/2" do |
describe "from_responses/2" do |
||||||
test "when block is not found" do |
test "when block is not found" do |
Loading…
Reference in new issue