Skip to content
This repository was archived by the owner on Feb 24, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### Features

- [#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
- [#6481](https://github.com/blockscout/blockscout/pull/6481) - Smart contract verification improvements
Expand Down Expand Up @@ -29,6 +30,7 @@

### Fixes

- [#6827](https://github.com/blockscout/blockscout/pull/6827) - Fix handling unknown calls from `callTracer`
- [#6532](https://github.com/blockscout/blockscout/pull/6532) - Fix index creation migration
- [#6473](https://github.com/blockscout/blockscout/pull/6473) - Fix state changes for contract creation transactions
- [#6475](https://github.com/blockscout/blockscout/pull/6475) - Fix token name with unicode graphemes shortening
Expand Down
76 changes: 75 additions & 1 deletion apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/geth.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ defmodule EthereumJSONRPC.Geth do
Ethereum JSONRPC methods that are only supported by [Geth](https://github.com/ethereum/go-ethereum/wiki/geth).
"""

require Logger

import EthereumJSONRPC, only: [id_to_params: 1, integer_to_quantity: 1, json_rpc: 2, request: 1]

alias EthereumJSONRPC.{FetchedBalance, FetchedCode, PendingTransaction}
Expand Down Expand Up @@ -78,12 +80,18 @@ defmodule EthereumJSONRPC.Geth do
defp debug_trace_transaction_request(%{id: id, hash_data: hash_data}) do
timeout = Application.get_env(:ethereum_jsonrpc, :internal_transaction_timeout)

tracer =
case Application.get_env(:ethereum_jsonrpc, __MODULE__)[:tracer] do
"js" -> @tracer
"call_tracer" -> "callTracer"
end

request(%{
id: id,
method: "debug_traceTransaction",
params: [
hash_data,
%{tracer: @tracer, disableStack: true, disableMemory: true, disableStorage: true, timeout: timeout}
%{tracer: tracer, disableStack: true, disableMemory: true, disableStorage: true, timeout: timeout}
]
})
end
Expand Down Expand Up @@ -177,6 +185,7 @@ defmodule EthereumJSONRPC.Geth do

internal_transaction_params =
calls
|> prepare_calls()
|> Stream.with_index()
|> Enum.map(fn {trace, index} ->
Map.merge(trace, %{
Expand Down Expand Up @@ -223,6 +232,71 @@ defmodule EthereumJSONRPC.Geth do
{:error, annotated_error}
end

defp prepare_calls(calls) do
case Application.get_env(:ethereum_jsonrpc, __MODULE__)[:tracer] do
"call_tracer" -> {calls, 0} |> parse_call_tracer_calls([], [], false) |> Enum.reverse()
"js" -> calls
end
end

defp parse_call_tracer_calls(calls, acc, trace_address, inner? \\ true)
defp parse_call_tracer_calls([], acc, _trace_address, _inner?), do: acc
defp parse_call_tracer_calls({%{"type" => 0}, _}, acc, _trace_address, _inner?), do: acc

defp parse_call_tracer_calls(
{%{"type" => type, "from" => from} = call, index},
acc,
trace_address,
inner?
)
when type in ~w(CALL CALLCODE DELEGATECALL STATICCALL CREATE CREATE2 SELFDESTRUCT REWARD) do
new_trace_address = [index | trace_address]

formatted_call =
%{
"type" => if(type in ~w(CALL CALLCODE DELEGATECALL STATICCALL), do: "call", else: String.downcase(type)),
"callType" => String.downcase(type),
"from" => from,
"to" => Map.get(call, "to", "0x"),
"createdContractAddressHash" => Map.get(call, "to", "0x"),
"value" => Map.get(call, "value", "0x0"),
"gas" => Map.get(call, "gas", "0x0"),
"gasUsed" => Map.get(call, "gasUsed", "0x0"),
"input" => Map.get(call, "input", "0x"),
"init" => Map.get(call, "input", "0x"),
"createdContractCode" => Map.get(call, "output", "0x"),
"traceAddress" => if(inner?, do: Enum.reverse(new_trace_address), else: []),
"error" => call["error"]
}
|> case do
%{"error" => nil} = ok_call ->
ok_call
|> Map.delete("error")
# to handle staticcall, all other cases handled by EthereumJSONRPC.Geth.Call.elixir_to_internal_transaction_params/1
|> Map.put("output", Map.get(call, "output", "0x"))

error_call ->
error_call
end

parse_call_tracer_calls(
Map.get(call, "calls", []),
[formatted_call | acc],
if(inner?, do: new_trace_address, else: [])
)
end

defp parse_call_tracer_calls({call, _}, acc, _trace_address, _inner?) do
Logger.warning("Call from a callTracer with an unknown type: #{inspect(call)}")
acc
end

defp parse_call_tracer_calls(calls, acc, trace_address, _inner) when is_list(calls) do
calls
|> Stream.with_index()
|> Enum.reduce(acc, &parse_call_tracer_calls(&1, &2, trace_address))
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)
Expand Down
Loading