Skip to content

Commit

Permalink
Suave functional (blockscout#8556)
Browse files Browse the repository at this point in the history
* Add execution_node field indexing

* Add `wrapped` field (and its subfields) indexing

* Add allowed_peekers field for transaction to API v2 response

* Add execution_node field for transaction to API v2 response

* Add Suave fields for regular transaction to API v2 response

* Add API v2 endpoint for transactions filtered by execution node

* Add index for execution_node field

* Small fixes

* Update changelog

* Fix for CHAIN_TYPE, backward compatibility

* Update spelling

* Fix for dyalizer

* Fix default_on_conflict

* Fix test.exs

* Fix view for Suave transaction

* Add Suave-specific comments

* mix format

* Set pool_size to 1 for suave

* Small refactoring for repo

* mix format, mix dialyzer

---------

Co-authored-by: POA <33550681+poa@users.noreply.github.com>
  • Loading branch information
2 people authored and tony-armstrong committed Dec 1, 2023
1 parent 3975c1d commit 0b9bacc
Show file tree
Hide file tree
Showing 26 changed files with 697 additions and 122 deletions.
3 changes: 3 additions & 0 deletions .dialyzer-ignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,6 @@ lib/indexer/fetcher/polygon_edge/withdrawal.ex:204
lib/indexer/fetcher/zkevm/transaction_batch.ex:116
lib/indexer/fetcher/zkevm/transaction_batch.ex:156
lib/indexer/fetcher/zkevm/transaction_batch.ex:252
lib/block_scout_web/views/api/v2/transaction_view.ex:431
lib/block_scout_web/views/api/v2/transaction_view.ex:472
lib/explorer/chain/transaction.ex:165
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Features

- [#8673](https://github.com/blockscout/blockscout/pull/8673) - Add a window for balances fetching from non-archive node
- [#8556](https://github.com/blockscout/blockscout/pull/8556) - Suave functional
- [#8528](https://github.com/blockscout/blockscout/pull/8528) - Account: add pagination + envs for limits
- [#7584](https://github.com/blockscout/blockscout/pull/7584) - Add Polygon zkEVM batches fetcher

Expand Down
4 changes: 4 additions & 0 deletions apps/block_scout_web/lib/block_scout_web/api_router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,10 @@ defmodule BlockScoutWeb.ApiRouter do
get("/zkevm-batch/:batch_number", V2.TransactionController, :zkevm_batch)
end

if System.get_env("CHAIN_TYPE") == "suave" do
get("/execution-node/:execution_node_hash_param", V2.TransactionController, :execution_node)
end

get("/:transaction_hash_param", V2.TransactionController, :transaction)
get("/:transaction_hash_param/token-transfers", V2.TransactionController, :token_transfers)
get("/:transaction_hash_param/internal-transactions", V2.TransactionController, :internal_transactions)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ defmodule BlockScoutWeb.API.V2.TransactionController do
import BlockScoutWeb.Account.AuthController, only: [current_user: 1]

import BlockScoutWeb.Chain,
only: [next_page_params: 3, token_transfers_next_page_params: 3, paging_options: 1, split_list_by_page: 1]
only: [
next_page_params: 3,
put_key_value_to_paging_options: 3,
token_transfers_next_page_params: 3,
paging_options: 1,
split_list_by_page: 1
]

import BlockScoutWeb.PagingHelper,
only: [
Expand Down Expand Up @@ -72,13 +78,21 @@ defmodule BlockScoutWeb.API.V2.TransactionController do
Map.put(@transaction_necessity_by_association, :transaction_actions, :optional)

necessity_by_association =
if Application.get_env(:explorer, :chain_type) == "polygon_zkevm" do
necessity_by_association_with_actions
|> Map.put(:zkevm_batch, :optional)
|> Map.put(:zkevm_sequence_transaction, :optional)
|> Map.put(:zkevm_verify_transaction, :optional)
else
necessity_by_association_with_actions
case Application.get_env(:explorer, :chain_type) do
"polygon_zkevm" ->
necessity_by_association_with_actions
|> Map.put(:zkevm_batch, :optional)
|> Map.put(:zkevm_sequence_transaction, :optional)
|> Map.put(:zkevm_verify_transaction, :optional)

"suave" ->
necessity_by_association_with_actions
|> Map.put(:logs, :optional)
|> Map.put([execution_node: :names], :optional)
|> Map.put([wrapped_to_address: :names], :optional)

_ ->
necessity_by_association_with_actions
end

with {:format, {:ok, transaction_hash}} <- {:format, Chain.string_to_transaction_hash(transaction_hash_string)},
Expand Down Expand Up @@ -143,6 +157,27 @@ defmodule BlockScoutWeb.API.V2.TransactionController do
|> render(:transactions, %{transactions: transactions, items: true})
end

def execution_node(conn, %{"execution_node_hash_param" => execution_node_hash_string} = params) do
with {:format, {:ok, execution_node_hash}} <- {:format, Chain.string_to_address_hash(execution_node_hash_string)} do
full_options =
[necessity_by_association: @transaction_necessity_by_association]
|> Keyword.merge(put_key_value_to_paging_options(paging_options(params), :is_index_in_asc_order, true))
|> Keyword.merge(@api_true)

transactions_plus_one = Chain.execution_node_to_transactions(execution_node_hash, full_options)

{transactions, next_page} = split_list_by_page(transactions_plus_one)

next_page_params =
next_page
|> next_page_params(transactions, delete_parameters_from_next_page_params(params))

conn
|> put_status(200)
|> render(:transactions, %{transactions: transactions, next_page_params: next_page_params})
end
end

@doc """
Function to handle GET requests to `/api/v2/transactions/:transaction_hash_param/raw-trace` endpoint.
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,18 @@ defmodule BlockScoutWeb.API.V2.TransactionView do
alias BlockScoutWeb.TransactionStateView
alias Ecto.Association.NotLoaded
alias Explorer.{Chain, Market}
alias Explorer.Chain.{Address, Block, InternalTransaction, Log, Token, Transaction, Wei}
alias Explorer.Chain.{Address, Block, Hash, InternalTransaction, Log, Token, Transaction, Wei}
alias Explorer.Chain.Block.Reward
alias Explorer.Chain.PolygonEdge.Reader
alias Explorer.Chain.Transaction.StateChange
alias Explorer.Counters.AverageBlockTime
alias Timex.Duration

import BlockScoutWeb.Account.AuthController, only: [current_user: 1]
import Explorer.Helper, only: [decode_data: 2]

@api_true [api?: true]
@suave_bid_event "0x83481d5b04dea534715acad673a8177a46fc93882760f36bdc16ccac439d504e"

def render("message.json", assigns) do
ApiView.render("message.json", assigns)
Expand Down Expand Up @@ -414,10 +416,10 @@ defmodule BlockScoutWeb.API.V2.TransactionView do
"has_error_in_internal_txs" => transaction.has_error_in_internal_txs
}

chain_type_fields(result, transaction, single_tx?, conn)
chain_type_fields(result, transaction, single_tx?, conn, watchlist_names)
end

defp chain_type_fields(result, transaction, single_tx?, conn) do
defp chain_type_fields(result, transaction, single_tx?, conn, watchlist_names) do
case single_tx? && Application.get_env(:explorer, :chain_type) do
"polygon_edge" ->
result
Expand All @@ -433,6 +435,9 @@ defmodule BlockScoutWeb.API.V2.TransactionView do

Map.put(extended_result, "zkevm_status", zkevm_status(extended_result))

"suave" ->
suave_fields(transaction, result, single_tx?, conn, watchlist_names)

_ ->
result
end
Expand All @@ -454,6 +459,97 @@ defmodule BlockScoutWeb.API.V2.TransactionView do
end
end

defp suave_fields(transaction, result, single_tx?, conn, watchlist_names) do
if is_nil(transaction.execution_node_hash) do
result
else
{[wrapped_decoded_input], _, _} =
decode_transactions(
[
%Transaction{
to_address: transaction.wrapped_to_address,
input: transaction.wrapped_input,
hash: transaction.wrapped_hash
}
],
false
)

result
|> Map.put("allowed_peekers", suave_parse_allowed_peekers(transaction.logs))
|> Map.put(
"execution_node",
Helper.address_with_info(
single_tx? && conn,
transaction.execution_node,
transaction.execution_node_hash,
single_tx?,
watchlist_names
)
)
|> Map.put("wrapped", %{
"type" => transaction.wrapped_type,
"nonce" => transaction.wrapped_nonce,
"to" =>
Helper.address_with_info(
single_tx? && conn,
transaction.wrapped_to_address,
transaction.wrapped_to_address_hash,
single_tx?,
watchlist_names
),
"gas_limit" => transaction.wrapped_gas,
"gas_price" => transaction.wrapped_gas_price,
"fee" =>
format_fee(
Chain.fee(
%Transaction{gas: transaction.wrapped_gas, gas_price: transaction.wrapped_gas_price, gas_used: nil},
:wei
)
),
"max_priority_fee_per_gas" => transaction.wrapped_max_priority_fee_per_gas,
"max_fee_per_gas" => transaction.wrapped_max_fee_per_gas,
"value" => transaction.wrapped_value,
"hash" => transaction.wrapped_hash,
"method" =>
method_name(
%Transaction{to_address: transaction.wrapped_to_address, input: transaction.wrapped_input},
wrapped_decoded_input
),
"decoded_input" => decoded_input(wrapped_decoded_input),
"raw_input" => transaction.wrapped_input
})
end
end

defp suave_parse_allowed_peekers(logs) do
suave_bid_contracts =
Application.get_all_env(:explorer)[Transaction][:suave_bid_contracts]
|> String.split(",")
|> Enum.map(fn sbc -> String.downcase(String.trim(sbc)) end)

bid_event =
Enum.find(logs, fn log ->
sanitize_log_first_topic(log.first_topic) == @suave_bid_event &&
Enum.member?(suave_bid_contracts, String.downcase(Hash.to_string(log.address_hash)))
end)

if is_nil(bid_event) do
[]
else
[_bid_id, _decryption_condition, allowed_peekers] =
decode_data(bid_event.data, [{:bytes, 16}, {:uint, 64}, {:array, :address}])

Enum.map(allowed_peekers, fn peeker ->
"0x" <> Base.encode16(peeker, case: :lower)
end)
end
end

defp sanitize_log_first_topic(first_topic) do
if is_nil(first_topic), do: "", else: String.downcase(first_topic)
end

def token_transfers(_, _conn, false), do: nil
def token_transfers(%NotLoaded{}, _conn, _), do: nil

Expand Down
1 change: 1 addition & 0 deletions apps/block_scout_web/test/test_helper.exs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo.Account, :manual)
Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo.PolygonEdge, :manual)
Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo.PolygonZkevm, :manual)
Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo.RSK, :manual)
Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo.Suave, :manual)

Absinthe.Test.prime(BlockScoutWeb.Schema)

Expand Down
Loading

0 comments on commit 0b9bacc

Please sign in to comment.