Skip to content

Commit

Permalink
Handle empty id in json rpc responses
Browse files Browse the repository at this point in the history
  • Loading branch information
Qwerty5Uiop authored and carterqw2 committed Jan 25, 2024
1 parent 8b22e3a commit 673c5f5
Show file tree
Hide file tree
Showing 11 changed files with 48 additions and 8 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

### Features

- [#9072](https://github.com/blockscout/blockscout/pull/9072) - Add tracing by block logic for geth
- [#7532](https://github.com/blockscout/blockscout/pull/7532) - Handle empty id in json rpc responses
- [#6721](https://github.com/blockscout/blockscout/pull/6721) - Implement fetching internal transactions from callTracer
- [#5561](https://github.com/blockscout/blockscout/pull/5561), [#6523](https://github.com/blockscout/blockscout/pull/6523) - Improve working with contracts implementations
- [#6401](https://github.com/blockscout/blockscout/pull/6401) - Add Sol2Uml contract visualization
Expand Down
33 changes: 30 additions & 3 deletions apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ defmodule EthereumJSONRPC do
documentation for `EthereumJSONRPC.RequestCoordinator`.
"""

require Logger

alias EthereumJSONRPC.{
Block,
Blocks,
Expand Down Expand Up @@ -380,6 +382,29 @@ defmodule EthereumJSONRPC do
|> Enum.into(%{}, fn {params, id} -> {id, params} end)
end

@doc """
Assigns not matched ids between requests and responses to responses with incorrect ids
"""
def sanitize_responses(responses, id_to_params) do
responses
|> Enum.reduce(
{[], Map.keys(id_to_params) -- Enum.map(responses, & &1.id)},
fn
%{id: nil} = res, {result_res, [id | rest]} ->
Logger.error(
"Empty id in response: #{inspect(res)}, stacktrace: #{inspect(Process.info(self(), :current_stacktrace))}"
)

{[%{res | id: id} | result_res], rest}

res, {result_res, non_matched} ->
{[res | result_res], non_matched}
end
)
|> elem(0)
|> Enum.reverse()
end

@doc """
1. POSTs JSON `payload` to `url`
2. Decodes the response
Expand All @@ -404,7 +429,7 @@ defmodule EthereumJSONRPC do
@doc """
Converts `t:quantity/0` to `t:non_neg_integer/0`.
"""
@spec quantity_to_integer(quantity) :: non_neg_integer() | :error
@spec quantity_to_integer(quantity) :: non_neg_integer() | nil
def quantity_to_integer("0x" <> hexadecimal_digits) do
String.to_integer(hexadecimal_digits, 16)
end
Expand All @@ -414,10 +439,12 @@ defmodule EthereumJSONRPC do
def quantity_to_integer(string) when is_binary(string) do
case Integer.parse(string) do
{integer, ""} -> integer
_ -> :error
_ -> nil
end
end

def quantity_to_integer(_), do: nil

@doc """
Converts `t:non_neg_integer/0` to `t:quantity/0`
"""
Expand Down Expand Up @@ -489,7 +516,7 @@ defmodule EthereumJSONRPC do
"""
def timestamp_to_datetime(timestamp) do
case quantity_to_integer(timestamp) do
:error ->
nil ->
nil

quantity ->
Expand Down
1 change: 1 addition & 0 deletions apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/blocks.ex
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ defmodule EthereumJSONRPC.Blocks do
def from_responses(responses, id_to_params) when is_list(responses) and is_map(id_to_params) do
%{errors: errors, blocks: blocks} =
responses
|> EthereumJSONRPC.sanitize_responses(id_to_params)
|> Enum.map(&Block.from_response(&1, id_to_params))
|> Enum.reduce(%{errors: [], blocks: []}, fn
{:ok, block}, %{blocks: blocks} = acc ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ defmodule EthereumJSONRPC.FetchedBalances do
"""
def from_responses(responses, id_to_params) do
responses
|> EthereumJSONRPC.sanitize_responses(id_to_params)
|> Enum.map(&FetchedBalance.from_response(&1, id_to_params))
|> Enum.reduce(
%__MODULE__{},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ defmodule EthereumJSONRPC.FetchedBeneficiaries do
"""
def from_responses(responses, id_to_params) when is_list(responses) and is_map(id_to_params) do
responses
|> EthereumJSONRPC.sanitize_responses(id_to_params)
|> Enum.map(&response_to_params_set(&1, id_to_params))
|> Enum.reduce(
%EthereumJSONRPC.FetchedBeneficiaries{},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ defmodule EthereumJSONRPC.FetchedCodes do
"""
def from_responses(responses, id_to_params) do
responses
|> EthereumJSONRPC.sanitize_responses(id_to_params)
|> Enum.map(&FetchedCode.from_response(&1, id_to_params))
|> Enum.reduce(
%__MODULE__{},
Expand Down
1 change: 1 addition & 0 deletions apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth.ex
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ defmodule EthereumJSONRPC.Geth do
)
when is_list(responses) and is_map(id_to_params) do
responses
|> EthereumJSONRPC.sanitize_responses(id_to_params)
|> Enum.map(&debug_trace_transaction_response_to_internal_transactions_params(&1, id_to_params))
|> reduce_internal_transactions_params()
end
Expand Down
11 changes: 7 additions & 4 deletions apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/http.ex
Original file line number Diff line number Diff line change
Expand Up @@ -159,15 +159,18 @@ defmodule EthereumJSONRPC.HTTP do

standardized = %{jsonrpc: jsonrpc, id: id}

case unstandardized do
%{"result" => _, "error" => _} ->
case {id, unstandardized} do
{_id, %{"result" => _, "error" => _}} ->
raise ArgumentError,
"result and error keys are mutually exclusive in JSONRPC 2.0 response objects, but got #{inspect(unstandardized)}"

%{"result" => result} ->
{nil, %{"result" => error}} ->
Map.put(standardized, :error, standardize_error(error))

{_id, %{"result" => result}} ->
Map.put(standardized, :result, result)

%{"error" => error} ->
{_id, %{"error" => error}} ->
Map.put(standardized, :error, standardize_error(error))
end
end
Expand Down
1 change: 1 addition & 0 deletions apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/receipts.ex
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ defmodule EthereumJSONRPC.Receipts do
defp reduce_responses(responses, id_to_transaction_params)
when is_list(responses) and is_map(id_to_transaction_params) do
responses
|> EthereumJSONRPC.sanitize_responses(id_to_transaction_params)
|> Stream.map(&response_to_receipt(&1, id_to_transaction_params))
|> Enum.reduce({:ok, []}, &reduce_receipt(&1, &2))
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ defmodule EthereumJSONRPC.TraceReplayBlockTransactions do
defp trace_replay_block_transactions_responses_to_traces(responses, id_to_params)
when is_list(responses) and is_map(id_to_params) do
responses
|> EthereumJSONRPC.sanitize_responses(id_to_params)
|> Enum.map(&trace_replay_block_transactions_response_to_traces(&1, id_to_params))
|> Enum.reduce(
{:ok, []},
Expand Down Expand Up @@ -158,6 +159,7 @@ defmodule EthereumJSONRPC.TraceReplayBlockTransactions do
defp trace_replay_transaction_responses_to_first_trace(responses, id_to_params)
when is_list(responses) and is_map(id_to_params) do
responses
|> EthereumJSONRPC.sanitize_responses(id_to_params)
|> Enum.map(&trace_replay_transaction_response_to_first_trace(&1, id_to_params))
|> Enum.reduce(
{:ok, []},
Expand Down
2 changes: 1 addition & 1 deletion apps/ethereum_jsonrpc/test/ethereum_jsonrpc_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -973,7 +973,7 @@ defmodule EthereumJSONRPCSyncTest do
params_list: [
%{
address_hash: hash,
block_number: :error,
block_number: nil,
value: expected_fetched_balance
}
]
Expand Down

0 comments on commit 673c5f5

Please sign in to comment.