Skip to content

Commit

Permalink
Merge pull request #885 from celo-org/carterqw2/call-tracer
Browse files Browse the repository at this point in the history
Implement fetching internal transactions from callTracer
  • Loading branch information
rkachowski authored Jul 18, 2023
2 parents b352db7 + b4d0f51 commit e2e2982
Show file tree
Hide file tree
Showing 9 changed files with 458 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,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

0 comments on commit e2e2982

Please sign in to comment.