Add /api/v2/transactions/{transaction_hash_param}/summary endpoint + Transaction Interpretation Service interface

pull/8957/head
Nikita Pozdniakov 12 months ago
parent 1d91e30308
commit 005b1b20ac
No known key found for this signature in database
GPG Key ID: F344106F9804FE5F
  1. 26
      .github/workflows/config.yml
  2. 2
      CHANGELOG.md
  3. 1
      apps/block_scout_web/lib/block_scout_web/api_router.ex
  4. 8
      apps/block_scout_web/lib/block_scout_web/controllers/api/v2/fallback_controller.ex
  5. 92
      apps/block_scout_web/lib/block_scout_web/controllers/api/v2/transaction_controller.ex
  6. 147
      apps/block_scout_web/lib/block_scout_web/microservice_interfaces/transaction_interpretation.ex
  7. 66
      apps/block_scout_web/lib/block_scout_web/views/api/v2/transaction_view.ex
  8. 11
      apps/explorer/lib/explorer/helper.ex
  9. 4
      apps/explorer/lib/explorer/smart_contract/rust_verifier_interface_behaviour.ex
  10. 4
      apps/explorer/lib/explorer/smart_contract/sig_provider_interface.ex
  11. 15
      apps/explorer/lib/explorer/utility/microservice.ex
  12. 15
      apps/explorer/lib/explorer/utility/rust_service.ex
  13. 4
      apps/explorer/lib/explorer/visualize/sol2uml.ex
  14. 4
      config/runtime.exs

@ -72,7 +72,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_28-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_29-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-
@ -130,7 +130,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_28-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_29-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -154,7 +154,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_28-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_29-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -183,7 +183,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_28-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_29-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -227,7 +227,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_28-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_29-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -253,7 +253,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_28-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_29-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -282,7 +282,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_28-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_29-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -330,7 +330,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_28-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_29-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -376,7 +376,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_28-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_29-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -438,7 +438,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_28-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_29-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -498,7 +498,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_28-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_29-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -569,7 +569,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_28-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_29-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"
@ -637,7 +637,7 @@ jobs:
path: |
deps
_build
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_28-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-mixlockhash_29-${{ hashFiles('mix.lock') }}
restore-keys: |
${{ runner.os }}-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ env.MIX_ENV }}-deps-"

@ -4,6 +4,8 @@
### Features
- [#8957](https://github.com/blockscout/blockscout/pull/8957) - Add Tx Interpreter Service integration
### Fixes
### Chore

@ -216,6 +216,7 @@ defmodule BlockScoutWeb.ApiRouter do
get("/:transaction_hash_param/logs", V2.TransactionController, :logs)
get("/:transaction_hash_param/raw-trace", V2.TransactionController, :raw_trace)
get("/:transaction_hash_param/state-changes", V2.TransactionController, :state_changes)
get("/:transaction_hash_param/summary", V2.TransactionController, :summary)
end
scope "/blocks" do

@ -26,6 +26,7 @@ defmodule BlockScoutWeb.API.V2.FallbackController do
@address_not_found "Address not found"
@address_is_not_smart_contract "Address is not smart-contract"
@empty_response "Empty response"
@tx_interpreter_service_disabled "Transaction Interpretation Service is not enabled"
def call(conn, {:format, _params}) do
Logger.error(fn ->
@ -256,4 +257,11 @@ defmodule BlockScoutWeb.API.V2.FallbackController do
|> put_view(ApiView)
|> render(:message, %{message: @empty_response})
end
def call(conn, {:tx_interpreter_enabled, false}) do
conn
|> put_status(404)
|> put_view(ApiView)
|> render(:message, %{message: @tx_interpreter_service_disabled})
end
end

@ -23,8 +23,9 @@ defmodule BlockScoutWeb.API.V2.TransactionController do
]
alias BlockScoutWeb.AccessHelper
alias BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation, as: TransactionInterpretationService
alias BlockScoutWeb.Models.TransactionStateHelper
alias Explorer.Chain
alias Explorer.{Chain, Helper}
alias Explorer.Chain.Zkevm.Reader
alias Indexer.Fetcher.FirstTraceOnDemand
@ -36,7 +37,6 @@ defmodule BlockScoutWeb.API.V2.TransactionController do
[created_contract_address: :token] => :optional,
[from_address: :names] => :optional,
[to_address: :names] => :optional,
# as far as I remember this needed for substituting implementation name in `to` address instead of is's real name (in transactions)
[to_address: :smart_contract] => :optional
}
@ -95,16 +95,11 @@ defmodule BlockScoutWeb.API.V2.TransactionController do
necessity_by_association_with_actions
end
with {:format, {:ok, transaction_hash}} <- {:format, Chain.string_to_transaction_hash(transaction_hash_string)},
{:not_found, {:ok, transaction}} <-
{:not_found,
Chain.hash_to_transaction(
transaction_hash,
necessity_by_association: necessity_by_association,
api?: true
)},
{:ok, false} <- AccessHelper.restricted_access?(to_string(transaction.from_address_hash), params),
{:ok, false} <- AccessHelper.restricted_access?(to_string(transaction.to_address_hash), params),
with {:ok, transaction, _transaction_hash} <-
validate_transaction(transaction_hash_string, params,
necessity_by_association: necessity_by_association,
api?: true
),
preloaded <-
Chain.preload_token_transfers(transaction, @token_transfers_in_tx_necessity_by_association, @api_true, false) do
conn
@ -183,11 +178,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do
"""
@spec raw_trace(Plug.Conn.t(), map()) :: Plug.Conn.t() | {atom(), any()}
def raw_trace(conn, %{"transaction_hash_param" => transaction_hash_string} = params) do
with {:format, {:ok, transaction_hash}} <- {:format, Chain.string_to_transaction_hash(transaction_hash_string)},
{:not_found, {:ok, transaction}} <-
{:not_found, Chain.hash_to_transaction(transaction_hash, @api_true)},
{:ok, false} <- AccessHelper.restricted_access?(to_string(transaction.from_address_hash), params),
{:ok, false} <- AccessHelper.restricted_access?(to_string(transaction.to_address_hash), params) do
with {:ok, transaction, transaction_hash} <- validate_transaction(transaction_hash_string, params) do
if is_nil(transaction.block_number) do
conn
|> put_status(200)
@ -216,11 +207,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do
"""
@spec token_transfers(Plug.Conn.t(), map()) :: Plug.Conn.t() | {atom(), any()}
def token_transfers(conn, %{"transaction_hash_param" => transaction_hash_string} = params) do
with {:format, {:ok, transaction_hash}} <- {:format, Chain.string_to_transaction_hash(transaction_hash_string)},
{:not_found, {:ok, transaction}} <-
{:not_found, Chain.hash_to_transaction(transaction_hash, @api_true)},
{:ok, false} <- AccessHelper.restricted_access?(to_string(transaction.from_address_hash), params),
{:ok, false} <- AccessHelper.restricted_access?(to_string(transaction.to_address_hash), params) do
with {:ok, _transaction, transaction_hash} <- validate_transaction(transaction_hash_string, params) do
paging_options = paging_options(params)
full_options =
@ -252,11 +239,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do
"""
@spec internal_transactions(Plug.Conn.t(), map()) :: Plug.Conn.t() | {atom(), any()}
def internal_transactions(conn, %{"transaction_hash_param" => transaction_hash_string} = params) do
with {:format, {:ok, transaction_hash}} <- {:format, Chain.string_to_transaction_hash(transaction_hash_string)},
{:not_found, {:ok, transaction}} <-
{:not_found, Chain.hash_to_transaction(transaction_hash, @api_true)},
{:ok, false} <- AccessHelper.restricted_access?(to_string(transaction.from_address_hash), params),
{:ok, false} <- AccessHelper.restricted_access?(to_string(transaction.to_address_hash), params) do
with {:ok, _transaction, transaction_hash} <- validate_transaction(transaction_hash_string, params) do
full_options =
@internal_transaction_necessity_by_association
|> Keyword.merge(paging_options(params))
@ -284,11 +267,7 @@ defmodule BlockScoutWeb.API.V2.TransactionController do
"""
@spec logs(Plug.Conn.t(), map()) :: Plug.Conn.t() | {atom(), any()}
def logs(conn, %{"transaction_hash_param" => transaction_hash_string} = params) do
with {:format, {:ok, transaction_hash}} <- {:format, Chain.string_to_transaction_hash(transaction_hash_string)},
{:not_found, {:ok, transaction}} <-
{:not_found, Chain.hash_to_transaction(transaction_hash, @api_true)},
{:ok, false} <- AccessHelper.restricted_access?(to_string(transaction.from_address_hash), params),
{:ok, false} <- AccessHelper.restricted_access?(to_string(transaction.to_address_hash), params) do
with {:ok, _transaction, transaction_hash} <- validate_transaction(transaction_hash_string, params) do
full_options =
[
necessity_by_association: %{
@ -323,16 +302,12 @@ defmodule BlockScoutWeb.API.V2.TransactionController do
"""
@spec state_changes(Plug.Conn.t(), map()) :: Plug.Conn.t() | {atom(), any()}
def state_changes(conn, %{"transaction_hash_param" => transaction_hash_string} = params) do
with {:format, {:ok, transaction_hash}} <- {:format, Chain.string_to_transaction_hash(transaction_hash_string)},
{:not_found, {:ok, transaction}} <-
{:not_found,
Chain.hash_to_transaction(transaction_hash,
necessity_by_association:
Map.merge(@transaction_necessity_by_association, %{[block: [miner: :names]] => :optional}),
api?: true
)},
{:ok, false} <- AccessHelper.restricted_access?(to_string(transaction.from_address_hash), params),
{:ok, false} <- AccessHelper.restricted_access?(to_string(transaction.to_address_hash), params) do
with {:ok, transaction, _transaction_hash} <-
validate_transaction(transaction_hash_string, params,
necessity_by_association:
Map.merge(@transaction_necessity_by_association, %{[block: [miner: :names]] => :optional}),
api?: true
) do
state_changes_plus_next_page =
transaction |> TransactionStateHelper.state_changes(params |> paging_options() |> Keyword.merge(api?: true))
@ -376,4 +351,37 @@ defmodule BlockScoutWeb.API.V2.TransactionController do
})
end
end
@doc """
Function to handle GET requests to `/api/v2/transactions/:transaction_hash_param/summary` endpoint.
"""
@spec summary(any, map) ::
{:format, :error}
| {:not_found, {:error, :not_found}}
| {:restricted_access, true}
| {:tx_interpreter_enabled, boolean}
| Plug.Conn.t()
def summary(conn, %{"transaction_hash_param" => transaction_hash_string} = params) do
with {:tx_interpreter_enabled, true} <- {:tx_interpreter_enabled, TransactionInterpretationService.enabled?()},
{:ok, transaction, _transaction_hash} <- validate_transaction(transaction_hash_string, params) do
response =
case TransactionInterpretationService.interpret(transaction) do
{:ok, response} -> Helper.maybe_decode(response)
{:error, error} -> %{error: error}
end
conn
|> json(response)
end
end
defp validate_transaction(transaction_hash_string, params, options \\ @api_true) do
with {:format, {:ok, transaction_hash}} <- {:format, Chain.string_to_transaction_hash(transaction_hash_string)},
{:not_found, {:ok, transaction}} <-
{:not_found, Chain.hash_to_transaction(transaction_hash, options)},
{:ok, false} <- AccessHelper.restricted_access?(to_string(transaction.from_address_hash), params),
{:ok, false} <- AccessHelper.restricted_access?(to_string(transaction.to_address_hash), params) do
{:ok, transaction, transaction_hash}
end
end
end

@ -0,0 +1,147 @@
defmodule BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation do
@moduledoc """
Module to interact with Transaction Interpretation Service
"""
alias BlockScoutWeb.API.V2.{Helper, TransactionView}
alias Explorer.Chain
alias Explorer.Chain.Transaction
alias HTTPoison.Response
import Explorer.Utility.Microservice, only: [base_url: 2]
require Logger
@post_timeout :timer.minutes(5)
@request_error_msg "Error while sending request to Transaction Interpretation Service"
@api_true api?: true
@items_limit 50
@spec interpret(any) :: {:error, :disabled | binary} | {:ok, any}
def interpret(transaction) do
if enabled?() do
url = interpret_url()
body = prepare_request_body(transaction)
http_post_request(url, body)
else
{:error, :disabled}
end
end
defp http_post_request(url, body) do
headers = [{"Content-Type", "application/json"}]
case HTTPoison.post(url, Jason.encode!(body), headers, recv_timeout: @post_timeout) do
{:ok, %Response{body: body, status_code: 200}} ->
{:ok, body}
error ->
old_truncate = Application.get_env(:logger, :truncate)
Logger.configure(truncate: :infinity)
Logger.error(fn ->
[
"Error while sending request to microservice url: #{url}, body: #{inspect(body, limit: :infinity, printable_limit: :infinity)}: ",
inspect(error, limit: :infinity, printable_limit: :infinity)
]
end)
Logger.configure(truncate: old_truncate)
{:error, @request_error_msg}
end
end
defp config do
Application.get_env(:block_scout_web, __MODULE__)
end
def enabled?, do: config()[:enabled]
defp interpret_url do
base_url(:block_scout_web, __MODULE__) <> "/transactions/summary"
end
defp prepare_request_body(transaction) do
preloaded_transaction =
Chain.select_repo(@api_true).preload(transaction, [
:transaction_actions,
to_address: [:names, :smart_contract],
from_address: [:names, :smart_contract],
created_contract_address: [:names, :token, :smart_contract]
])
skip_sig_provider? = true
{decoded_input, _abi_acc, _methods_acc} = Transaction.decoded_input_data(transaction, skip_sig_provider?, @api_true)
decoded_input_data = TransactionView.decoded_input(decoded_input)
%{
data: %{
to:
Helper.address_with_info(nil, preloaded_transaction.to_address, preloaded_transaction.to_address_hash, true),
from:
Helper.address_with_info(
nil,
preloaded_transaction.from_address,
preloaded_transaction.from_address_hash,
true
),
hash: transaction.hash,
type: transaction.type,
value: transaction.value,
method: TransactionView.method_name(transaction, decoded_input),
status: transaction.status,
actions: TransactionView.transaction_actions(transaction.transaction_actions),
tx_types: TransactionView.tx_types(transaction),
raw_input: transaction.input,
decoded_input: decoded_input_data,
token_transfers: prepare_token_transfers(preloaded_transaction, decoded_input)
},
logs_data: %{items: prepare_logs(transaction)}
}
end
defp prepare_token_transfers(transaction, decoded_input) do
full_options =
[
necessity_by_association: %{
[from_address: :smart_contract] => :optional,
[to_address: :smart_contract] => :optional,
[from_address: :names] => :optional,
[to_address: :names] => :optional
}
]
|> Keyword.merge(@api_true)
transaction.hash
|> Chain.transaction_to_token_transfers(full_options)
|> Chain.flat_1155_batch_token_transfers()
|> Enum.take(@items_limit)
|> Enum.map(&TransactionView.prepare_token_transfer(&1, nil, decoded_input))
end
defp prepare_logs(transaction) do
full_options =
[
necessity_by_association: %{
[address: :names] => :optional,
[address: :smart_contract] => :optional,
address: :optional
}
]
|> Keyword.merge(@api_true)
logs =
transaction.hash
|> Chain.transaction_to_logs(full_options)
|> Enum.take(@items_limit)
decoded_logs = TransactionView.decode_logs(logs, true)
logs
|> Enum.zip(decoded_logs)
|> Enum.map(fn {log, decoded_log} -> TransactionView.prepare_log(log, transaction.hash, decoded_log, true) end)
end
end

@ -184,7 +184,8 @@ defmodule BlockScoutWeb.API.V2.TransactionView do
}
end
defp decode_logs(logs, skip_sig_provider?) do
@spec decode_logs([Log.t()], boolean) :: [tuple]
def decode_logs(logs, skip_sig_provider?) do
{result, _, _} =
Enum.reduce(logs, {[], %{}, %{}}, fn log, {results, contracts_acc, events_acc} ->
{result, contracts_acc, events_acc} =
@ -283,12 +284,12 @@ defmodule BlockScoutWeb.API.V2.TransactionView do
}
end
def prepare_log(log, transaction_or_hash, decoded_log) do
def prepare_log(log, transaction_or_hash, decoded_log, tags_for_address_needed? \\ false) do
decoded = process_decoded_log(decoded_log)
%{
"tx_hash" => get_tx_hash(transaction_or_hash),
"address" => Helper.address_with_info(nil, log.address, log.address_hash, false),
"address" => Helper.address_with_info(nil, log.address, log.address_hash, tags_for_address_needed?),
"topics" => [
log.first_topic,
log.second_topic,
@ -573,9 +574,9 @@ defmodule BlockScoutWeb.API.V2.TransactionView do
def token_transfers_overflow(token_transfers, _),
do: Enum.count(token_transfers) > Chain.get_token_transfers_per_transaction_preview_count()
defp transaction_actions(%NotLoaded{}), do: []
def transaction_actions(%NotLoaded{}), do: []
defp transaction_actions(actions) do
def transaction_actions(actions) do
render("transaction_actions.json", %{actions: actions})
end
@ -617,7 +618,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do
end
end
defp decoded_input(decoded_input) do
def decoded_input(decoded_input) do
case decoded_input do
{:ok, method_id, text, mapping} ->
render(__MODULE__, "decoded_input.json", method_id: method_id, text: text, mapping: mapping, error?: false)
@ -695,17 +696,17 @@ defmodule BlockScoutWeb.API.V2.TransactionView do
|> Timex.diff(right, :milliseconds)
end
defp method_name(_, _, skip_sc_check? \\ false)
def method_name(_, _, skip_sc_check? \\ false)
defp method_name(_, {:ok, _method_id, text, _mapping}, _) do
def method_name(_, {:ok, _method_id, text, _mapping}, _) do
Transaction.parse_method_name(text, false)
end
defp method_name(
%Transaction{to_address: to_address, input: %{bytes: <<method_id::binary-size(4), _::binary>>}},
_,
skip_sc_check?
) do
def method_name(
%Transaction{to_address: to_address, input: %{bytes: <<method_id::binary-size(4), _::binary>>}},
_,
skip_sc_check?
) do
if skip_sc_check? || Address.is_smart_contract(to_address) do
"0x" <> Base.encode16(method_id, case: :lower)
else
@ -713,13 +714,24 @@ defmodule BlockScoutWeb.API.V2.TransactionView do
end
end
defp method_name(_, _, _) do
def method_name(_, _, _) do
nil
end
defp tx_types(tx, types \\ [], stage \\ :token_transfer)
defp tx_types(%Transaction{token_transfers: token_transfers} = tx, types, :token_transfer) do
@spec tx_types(
Explorer.Chain.Transaction.t(),
list,
:coin_transfer
| :contract_call
| :contract_creation
| :rootstock_bridge
| :rootstock_remasc
| :token_creation
| :token_transfer
) :: list
def tx_types(tx, types \\ [], stage \\ :token_transfer)
def tx_types(%Transaction{token_transfers: token_transfers} = tx, types, :token_transfer) do
types =
if (!is_nil(token_transfers) && token_transfers != [] && !match?(%NotLoaded{}, token_transfers)) ||
tx.has_token_transfers do
@ -731,7 +743,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do
tx_types(tx, types, :token_creation)
end
defp tx_types(%Transaction{created_contract_address: created_contract_address} = tx, types, :token_creation) do
def tx_types(%Transaction{created_contract_address: created_contract_address} = tx, types, :token_creation) do
types =
if match?(%Address{}, created_contract_address) && match?(%Token{}, created_contract_address.token) do
[:token_creation | types]
@ -742,11 +754,11 @@ defmodule BlockScoutWeb.API.V2.TransactionView do
tx_types(tx, types, :contract_creation)
end
defp tx_types(
%Transaction{to_address_hash: to_address_hash} = tx,
types,
:contract_creation
) do
def tx_types(
%Transaction{to_address_hash: to_address_hash} = tx,
types,
:contract_creation
) do
types =
if is_nil(to_address_hash) do
[:contract_creation | types]
@ -757,7 +769,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do
tx_types(tx, types, :contract_call)
end
defp tx_types(%Transaction{to_address: to_address} = tx, types, :contract_call) do
def tx_types(%Transaction{to_address: to_address} = tx, types, :contract_call) do
types =
if Address.is_smart_contract(to_address) do
[:contract_call | types]
@ -768,7 +780,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do
tx_types(tx, types, :coin_transfer)
end
defp tx_types(%Transaction{value: value} = tx, types, :coin_transfer) do
def tx_types(%Transaction{value: value} = tx, types, :coin_transfer) do
types =
if Decimal.compare(value.value, 0) == :gt do
[:coin_transfer | types]
@ -779,7 +791,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do
tx_types(tx, types, :rootstock_remasc)
end
defp tx_types(tx, types, :rootstock_remasc) do
def tx_types(tx, types, :rootstock_remasc) do
types =
if Transaction.is_rootstock_remasc_transaction(tx) do
[:rootstock_remasc | types]
@ -790,7 +802,7 @@ defmodule BlockScoutWeb.API.V2.TransactionView do
tx_types(tx, types, :rootstock_bridge)
end
defp tx_types(tx, types, :rootstock_bridge) do
def tx_types(tx, types, :rootstock_bridge) do
if Transaction.is_rootstock_bridge_transaction(tx) do
[:rootstock_bridge | types]
else

@ -81,4 +81,15 @@ defmodule Explorer.Helper do
_ -> %{error: data}
end
end
@doc """
Tries to decode binary to json, return either decoded object, or initial binary
"""
@spec maybe_decode(binary) :: any
def maybe_decode(data) do
case safe_decode_json(data) do
%{error: _} -> data
decoded -> decoded
end
end
end

@ -5,7 +5,7 @@ defmodule Explorer.SmartContract.RustVerifierInterfaceBehaviour do
defmacro __using__(_) do
# credo:disable-for-next-line
quote([]) do
alias Explorer.Utility.RustService
alias Explorer.Utility.Microservice
alias HTTPoison.Response
require Logger
@ -172,7 +172,7 @@ defmodule Explorer.SmartContract.RustVerifierInterfaceBehaviour do
def base_api_url, do: "#{base_url()}" <> "/api/v2"
def base_url do
RustService.base_url(Explorer.SmartContract.RustVerifierInterfaceBehaviour)
Microservice.base_url(Explorer.SmartContract.RustVerifierInterfaceBehaviour)
end
def enabled?, do: Application.get_env(:explorer, Explorer.SmartContract.RustVerifierInterfaceBehaviour)[:enabled]

@ -3,7 +3,7 @@ defmodule Explorer.SmartContract.SigProviderInterface do
Adapter for decoding events and function calls with https://github.com/blockscout/blockscout-rs/tree/main/sig-provider
"""
alias Explorer.Utility.RustService
alias Explorer.Utility.Microservice
alias HTTPoison.Response
require Logger
@ -81,7 +81,7 @@ defmodule Explorer.SmartContract.SigProviderInterface do
def base_api_url, do: "#{base_url()}" <> "/api/v1/abi"
def base_url do
RustService.base_url(__MODULE__)
Microservice.base_url(__MODULE__)
end
def enabled?, do: Application.get_env(:explorer, __MODULE__)[:enabled]

@ -0,0 +1,15 @@
defmodule Explorer.Utility.Microservice do
@moduledoc """
Module is responsible for common utils related to microservices.
"""
def base_url(application \\ :explorer, module) do
url = Application.get_env(application, module)[:service_url]
if String.ends_with?(url, "/") do
url
|> String.slice(0..(String.length(url) - 2))
else
url
end
end
end

@ -1,15 +0,0 @@
defmodule Explorer.Utility.RustService do
@moduledoc """
Module is responsible for common utils related to rust microservices.
"""
def base_url(module) do
url = Application.get_env(:explorer, module)[:service_url]
if String.ends_with?(url, "/") do
url
|> String.slice(0..(String.length(url) - 2))
else
url
end
end
end

@ -2,7 +2,7 @@ defmodule Explorer.Visualize.Sol2uml do
@moduledoc """
Adapter for sol2uml visualizer with https://github.com/blockscout/blockscout-rs/blob/main/visualizer
"""
alias Explorer.Utility.RustService
alias Explorer.Utility.Microservice
alias HTTPoison.Response
require Logger
@ -61,7 +61,7 @@ defmodule Explorer.Visualize.Sol2uml do
def base_api_url, do: "#{base_url()}" <> "/api/v1"
def base_url do
RustService.base_url(__MODULE__)
Microservice.base_url(__MODULE__)
end
def enabled?, do: Application.get_env(:explorer, __MODULE__)[:enabled]

@ -132,6 +132,10 @@ config :block_scout_web, BlockScoutWeb.Chain.Address.CoinBalance,
config :block_scout_web, BlockScoutWeb.API.V2, enabled: ConfigHelper.parse_bool_env_var("API_V2_ENABLED", "true")
config :block_scout_web, BlockScoutWeb.MicroserviceInterfaces.TransactionInterpretation,
service_url: System.get_env("MICROSERVICE_TX_INTERPRETATION_URL"),
enabled: ConfigHelper.parse_bool_env_var("MICROSERVICE_TX_INTERPRETATION_ENABLED")
# Configures Ueberauth's Auth0 auth provider
config :ueberauth, Ueberauth.Strategy.Auth0.OAuth,
domain: System.get_env("ACCOUNT_AUTH0_DOMAIN"),

Loading…
Cancel
Save