Reduce function input to address' hash only where possible

Problem: a lot of function take an Explorer.Chain.Address as an parameter, but only use its hash.
This makes tracing the necessary scope of functions harder and is one of the cause for unnecessary queries/code execution.

Solution: refactor this functions to take only an address' hash.
pull/2325/head
pasqu4le 5 years ago
parent 09e69efb0c
commit 4e2ba9ae0c
No known key found for this signature in database
GPG Key ID: 8F3EE01F1DC90687
  1. 1
      CHANGELOG.md
  2. 4
      apps/block_scout_web/lib/block_scout_web/controllers/address_coin_balance_controller.ex
  3. 4
      apps/block_scout_web/lib/block_scout_web/controllers/address_contract_controller.ex
  4. 12
      apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex
  5. 4
      apps/block_scout_web/lib/block_scout_web/controllers/address_decompiled_contract_controller.ex
  6. 6
      apps/block_scout_web/lib/block_scout_web/controllers/address_internal_transaction_controller.ex
  7. 8
      apps/block_scout_web/lib/block_scout_web/controllers/address_logs_controller.ex
  8. 4
      apps/block_scout_web/lib/block_scout_web/controllers/address_read_contract_controller.ex
  9. 4
      apps/block_scout_web/lib/block_scout_web/controllers/address_token_controller.ex
  10. 4
      apps/block_scout_web/lib/block_scout_web/controllers/address_token_transfer_controller.ex
  11. 6
      apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex
  12. 11
      apps/block_scout_web/lib/block_scout_web/controllers/address_validation_controller.ex
  13. 4
      apps/block_scout_web/lib/block_scout_web/resolvers/transaction.ex
  14. 4
      apps/block_scout_web/lib/block_scout_web/templates/address/overview.html.eex
  15. 2
      apps/block_scout_web/lib/block_scout_web/templates/tokens/overview/_details.html.eex
  16. 12
      apps/block_scout_web/lib/block_scout_web/views/address_view.ex
  17. 2
      apps/block_scout_web/test/block_scout_web/views/address_view_test.exs
  18. 54
      apps/explorer/lib/explorer/chain.ex
  19. 16
      apps/explorer/lib/explorer/chain/address_token_transfer_csv_exporter.ex
  20. 14
      apps/explorer/lib/explorer/chain/address_transaction_csv_exporter.ex
  21. 7
      apps/explorer/lib/explorer/graphql.ex
  22. 125
      apps/explorer/test/explorer/chain_test.exs
  23. 18
      apps/explorer/test/explorer/graphql_test.exs

@ -12,6 +12,7 @@
- [#2291](https://github.com/poanetwork/blockscout/pull/2291) - dashboard fix for md resolution, transactions load fix, block info row fix, addresses page issue, check mark issue
### Chore
- [#2325](https://github.com/poanetwork/blockscout/pull/2325) - Reduce function input to address' hash only where possible
- [#2305](https://github.com/poanetwork/blockscout/pull/2305) - Improve Address controllers
- [#2302](https://github.com/poanetwork/blockscout/pull/2302) - fix names for xDai source
- [#2289](https://github.com/poanetwork/blockscout/pull/2289) - Optional websockets for dev environment

@ -64,8 +64,8 @@ defmodule BlockScoutWeb.AddressCoinBalanceController do
address: address,
coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address),
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
transaction_count: transaction_count(address),
validation_count: validation_count(address),
transaction_count: transaction_count(address_hash),
validation_count: validation_count(address_hash),
current_path: current_path(conn)
)
end

@ -16,8 +16,8 @@ defmodule BlockScoutWeb.AddressContractController do
address: address,
coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address),
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
transaction_count: transaction_count(address),
validation_count: validation_count(address)
transaction_count: transaction_count(address_hash),
validation_count: validation_count(address_hash)
)
else
:error ->

@ -5,7 +5,7 @@ defmodule BlockScoutWeb.AddressController do
alias BlockScoutWeb.AddressView
alias Explorer.{Chain, Market}
alias Explorer.Chain.Address
alias Explorer.Chain.Hash
alias Explorer.ExchangeRates.Token
alias Phoenix.View
@ -45,7 +45,7 @@ defmodule BlockScoutWeb.AddressController do
exchange_rate: exchange_rate,
total_supply: total_supply,
tx_count: tx_count,
validation_count: validation_count(address)
validation_count: validation_count(address.hash)
)
end)
@ -69,11 +69,11 @@ defmodule BlockScoutWeb.AddressController do
redirect(conn, to: address_transaction_path(conn, :index, id))
end
def transaction_count(%Address{} = address) do
Chain.total_transactions_sent_by_address(address)
def transaction_count(%Hash{byte_count: unquote(Hash.Address.byte_count())} = address_hash) do
Chain.total_transactions_sent_by_address(address_hash)
end
def validation_count(%Address{} = address) do
Chain.address_to_validation_count(address)
def validation_count(%Hash{byte_count: unquote(Hash.Address.byte_count())} = address_hash) do
Chain.address_to_validation_count(address_hash)
end
end

@ -16,8 +16,8 @@ defmodule BlockScoutWeb.AddressDecompiledContractController do
address: address,
coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address),
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
transaction_count: transaction_count(address),
validation_count: validation_count(address)
transaction_count: transaction_count(address_hash),
validation_count: validation_count(address_hash)
)
else
:error ->

@ -28,7 +28,7 @@ defmodule BlockScoutWeb.AddressInternalTransactionController do
|> Keyword.merge(paging_options(params))
|> Keyword.merge(current_filter(params))
internal_transactions_plus_one = Chain.address_to_internal_transactions(address, full_options)
internal_transactions_plus_one = Chain.address_to_internal_transactions(address_hash, full_options)
{internal_transactions, next_page} = split_list_by_page(internal_transactions_plus_one)
next_page_path =
@ -71,8 +71,8 @@ defmodule BlockScoutWeb.AddressInternalTransactionController do
current_path: current_path(conn),
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
filter: params["filter"],
transaction_count: transaction_count(address),
validation_count: validation_count(address)
transaction_count: transaction_count(address_hash),
validation_count: validation_count(address_hash)
)
else
:error ->

@ -17,7 +17,7 @@ defmodule BlockScoutWeb.AddressLogsController do
def index(conn, %{"address_id" => address_hash_string, "type" => "JSON"} = params) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, address} <- Chain.hash_to_address(address_hash, [], false) do
logs_plus_one = Chain.address_to_logs(address, paging_options(params))
logs_plus_one = Chain.address_to_logs(address_hash, paging_options(params))
{results, next_page} = split_list_by_page(logs_plus_one)
next_page_url =
@ -63,8 +63,8 @@ defmodule BlockScoutWeb.AddressLogsController do
current_path: current_path(conn),
coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address),
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
transaction_count: transaction_count(address),
validation_count: validation_count(address)
transaction_count: transaction_count(address_hash),
validation_count: validation_count(address_hash)
)
else
_ ->
@ -79,7 +79,7 @@ defmodule BlockScoutWeb.AddressLogsController do
formatted_topic = if String.starts_with?(topic, "0x"), do: topic, else: "0x" <> topic
logs_plus_one = Chain.address_to_logs(address, topic: formatted_topic)
logs_plus_one = Chain.address_to_logs(address_hash, topic: formatted_topic)
{results, next_page} = split_list_by_page(logs_plus_one)

@ -23,8 +23,8 @@ defmodule BlockScoutWeb.AddressReadContractController do
address: address,
coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address),
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
transaction_count: transaction_count(address),
validation_count: validation_count(address)
transaction_count: transaction_count(address_hash),
validation_count: validation_count(address_hash)
)
else
:error ->

@ -64,8 +64,8 @@ defmodule BlockScoutWeb.AddressTokenController do
current_path: current_path(conn),
coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address),
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
transaction_count: transaction_count(address),
validation_count: validation_count(address)
transaction_count: transaction_count(address_hash),
validation_count: validation_count(address_hash)
)
else
:error ->

@ -85,8 +85,8 @@ defmodule BlockScoutWeb.AddressTokenTransferController do
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
current_path: current_path(conn),
token: token,
transaction_count: transaction_count(address),
validation_count: validation_count(address)
transaction_count: transaction_count(address_hash),
validation_count: validation_count(address_hash)
)
else
:error ->

@ -38,7 +38,7 @@ defmodule BlockScoutWeb.AddressTransactionController do
|> Keyword.merge(paging_options(params))
|> Keyword.merge(current_filter(params))
results_plus_one = Chain.address_to_transactions_with_rewards(address, options)
results_plus_one = Chain.address_to_transactions_with_rewards(address_hash, options)
{results, next_page} = split_list_by_page(results_plus_one)
next_page_url =
@ -97,8 +97,8 @@ defmodule BlockScoutWeb.AddressTransactionController do
coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address),
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
filter: params["filter"],
transaction_count: transaction_count(address),
validation_count: validation_count(address),
transaction_count: transaction_count(address_hash),
validation_count: validation_count(address_hash),
current_path: current_path(conn)
)
else

@ -17,7 +17,7 @@ defmodule BlockScoutWeb.AddressValidationController do
def index(conn, %{"address_id" => address_hash_string, "type" => "JSON"} = params) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
{:ok, address} <- Chain.find_or_insert_address_from_hash(address_hash, [], false) do
{:ok, _} <- Chain.find_or_insert_address_from_hash(address_hash, [], false) do
full_options =
Keyword.merge(
[
@ -31,7 +31,7 @@ defmodule BlockScoutWeb.AddressValidationController do
paging_options(params)
)
blocks_plus_one = Chain.get_blocks_validated_by_address(full_options, address)
blocks_plus_one = Chain.get_blocks_validated_by_address(full_options, address_hash)
{blocks, next_page} = split_list_by_page(blocks_plus_one)
next_page_path =
@ -63,9 +63,6 @@ defmodule BlockScoutWeb.AddressValidationController do
else
:error ->
unprocessable_entity(conn)
{:error, :not_found} ->
not_found(conn)
end
end
@ -78,8 +75,8 @@ defmodule BlockScoutWeb.AddressValidationController do
address: address,
coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address),
current_path: current_path(conn),
transaction_count: transaction_count(address),
validation_count: validation_count(address),
transaction_count: transaction_count(address.hash),
validation_count: validation_count(address.hash),
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null()
)
else

@ -12,8 +12,8 @@ defmodule BlockScoutWeb.Resolvers.Transaction do
end
end
def get_by(%Address{} = address, args, _) do
address
def get_by(%Address{hash: address_hash}, args, _) do
address_hash
|> GraphQL.address_to_transactions_query()
|> Connection.from_query(&Repo.all/1, args, options(args))
end

@ -74,7 +74,7 @@
<%= if contract?(@address) do %>
<%= gettext(">=") %>
<span data-selector="transaction-count">
<%= incoming_transaction_count(@address) %>
<%= incoming_transaction_count(@address.hash) %>
</span>
<%= gettext("Incoming Transactions") %>
<% else %>
@ -146,7 +146,7 @@
</button>
</div>
<div class="modal-body">
<img src="data:image/png;base64, <%= qr_code(@address) %>" class="qr-code" alt="qr_code" title="<%= @address %>" />
<img src="data:image/png;base64, <%= qr_code(@address.hash) %>" class="qr-code" alt="qr_code" title="<%= @address %>" />
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-dismiss="modal"><%= gettext "Close" %></button>

@ -109,7 +109,7 @@
</button>
</div>
<div class="modal-body">
<img src="data:image/png;base64, <%= BlockScoutWeb.AddressView.qr_code(@token.contract_address) %> " class="qr-code" alt="qr_code" title="<%= @token.contract_address %>" />
<img src="data:image/png;base64, <%= BlockScoutWeb.AddressView.qr_code(@token.contract_address_hash) %> " class="qr-code" alt="qr_code" title="<%= @token.contract_address %>" />
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-dismiss="modal"><%= gettext "Close" %></button>

@ -189,8 +189,8 @@ defmodule BlockScoutWeb.AddressView do
|> Timex.format!("{M}-{D}-{YYYY}")
end
def qr_code(%Address{hash: hash}) do
hash
def qr_code(address_hash) do
address_hash
|> to_string()
|> QRCode.to_png()
|> Base.encode64()
@ -219,10 +219,10 @@ defmodule BlockScoutWeb.AddressView do
def token_title(%Token{name: name, symbol: symbol}), do: "#{name} (#{symbol})"
def incoming_transaction_count(%Address{} = address) do
count = Chain.address_to_incoming_transaction_count(address)
Cldr.Number.to_string!(count, format: "#,###")
def incoming_transaction_count(address_hash) do
address_hash
|> Chain.address_to_incoming_transaction_count()
|> Cldr.Number.to_string!(format: "#,###")
end
def trimmed_hash(%Hash{} = hash) do

@ -199,7 +199,7 @@ defmodule BlockScoutWeb.AddressViewTest do
describe "qr_code/1" do
test "it returns an encoded value" do
address = build(:address)
assert {:ok, _} = Base.decode64(AddressView.qr_code(address))
assert {:ok, _} = Base.decode64(AddressView.qr_code(address.hash))
end
end

@ -126,7 +126,7 @@ defmodule Explorer.Chain do
end
@doc """
`t:Explorer.Chain.InternalTransaction/0`s from `address`.
`t:Explorer.Chain.InternalTransaction/0`s from the address with the given `hash`.
This function excludes any internal transactions in the results where the
internal transaction has no siblings within the parent transaction.
@ -145,10 +145,10 @@ defmodule Explorer.Chain do
transactions older than the `block_number`, `transaction index`, and `index` that are passed.
"""
@spec address_to_internal_transactions(Address.t(), [paging_options | necessity_by_association_option]) :: [
@spec address_to_internal_transactions(Hash.Address.t(), [paging_options | necessity_by_association_option]) :: [
InternalTransaction.t()
]
def address_to_internal_transactions(%Address{hash: hash}, options \\ []) do
def address_to_internal_transactions(hash, options \\ []) do
necessity_by_association = Keyword.get(options, :necessity_by_association, %{})
direction = Keyword.get(options, :direction)
paging_options = Keyword.get(options, :paging_options, @default_paging_options)
@ -171,13 +171,13 @@ defmodule Explorer.Chain do
end
@doc """
Get the total number of transactions sent by the given address according to the last block indexed.
Get the total number of transactions sent by the address with the given hash according to the last block indexed.
We have to increment +1 in the last nonce result because it works like an array position, the first
nonce has the value 0. When last nonce is nil, it considers that the given address has 0 transactions.
"""
@spec total_transactions_sent_by_address(Address.t()) :: non_neg_integer()
def total_transactions_sent_by_address(%Address{hash: address_hash}) do
@spec total_transactions_sent_by_address(Hash.Address.t()) :: non_neg_integer()
def total_transactions_sent_by_address(address_hash) do
last_nonce =
address_hash
|> Transaction.last_nonce_by_address_query()
@ -190,9 +190,9 @@ defmodule Explorer.Chain do
end
@doc """
Fetches the transactions related to the given address, including transactions
that only have the address in the `token_transfers` related table and rewards
for block validation.
Fetches the transactions related to the address with the given hash, including
transactions that only have the address in the `token_transfers` related table
and rewards for block validation.
This query is divided into multiple subqueries intentionally in order to
improve the listing performance.
@ -214,14 +214,10 @@ defmodule Explorer.Chain do
the `block_number` and `index` that are passed.
"""
@spec address_to_transactions_with_rewards(Address.t(), [paging_options | necessity_by_association_option]) :: [
@spec address_to_transactions_with_rewards(Hash.Address.t(), [paging_options | necessity_by_association_option]) :: [
Transaction.t()
]
def address_to_transactions_with_rewards(
%Address{hash: %Hash{byte_count: unquote(Hash.Address.byte_count())} = address_hash},
options \\ []
)
when is_list(options) do
def address_to_transactions_with_rewards(address_hash, options \\ []) when is_list(options) do
direction = Keyword.get(options, :direction)
necessity_by_association = Keyword.get(options, :necessity_by_association, %{})
paging_options = Keyword.get(options, :paging_options, @default_paging_options)
@ -256,14 +252,8 @@ defmodule Explorer.Chain do
end
end
@spec address_to_logs(Address.t(), Keyword.t()) :: [
Log.t()
]
def address_to_logs(
%Address{hash: %Hash{byte_count: unquote(Hash.Address.byte_count())} = address_hash},
options \\ []
)
when is_list(options) do
@spec address_to_logs(Hash.Address.t(), Keyword.t()) :: [Log.t()]
def address_to_logs(address_hash, options \\ []) when is_list(options) do
paging_options = Keyword.get(options, :paging_options) || %PagingOptions{page_size: 50}
{block_number, transaction_index, log_index} = paging_options.key || {BlockNumberCache.max_number(), 0, 0}
@ -442,8 +432,8 @@ defmodule Explorer.Chain do
Repo.aggregate(query, :count, :hash)
end
@spec address_to_incoming_transaction_count(Address.t()) :: non_neg_integer()
def address_to_incoming_transaction_count(%Address{hash: address_hash}) do
@spec address_to_incoming_transaction_count(Hash.Address.t()) :: non_neg_integer()
def address_to_incoming_transaction_count(address_hash) do
paging_options = %PagingOptions{page_size: @max_incoming_transactions_count}
base_query =
@ -1333,7 +1323,7 @@ defmodule Explorer.Chain do
end
@doc """
Finds all Blocks validated by the address given.
Finds all Blocks validated by the address with the given hash.
## Options
* `:necessity_by_association` - use to load `t:association/0` as `:required` or `:optional`. If an association is
@ -1347,15 +1337,15 @@ defmodule Explorer.Chain do
"""
@spec get_blocks_validated_by_address(
[paging_options | necessity_by_association_option],
Address.t()
Hash.Address.t()
) :: [Block.t()]
def get_blocks_validated_by_address(options \\ [], %Address{hash: hash}) when is_list(options) do
def get_blocks_validated_by_address(options \\ [], address_hash) when is_list(options) do
necessity_by_association = Keyword.get(options, :necessity_by_association, %{})
paging_options = Keyword.get(options, :paging_options, @default_paging_options)
Block
|> join_associations(necessity_by_association)
|> where(miner_hash: ^hash)
|> where(miner_hash: ^address_hash)
|> page_blocks(paging_options)
|> limit(^paging_options.page_size)
|> order_by(desc: :number)
@ -1379,10 +1369,10 @@ defmodule Explorer.Chain do
end
@doc """
Counts the number of `t:Explorer.Chain.Block.t/0` validated by the `address`.
Counts the number of `t:Explorer.Chain.Block.t/0` validated by the address with the given `hash`.
"""
@spec address_to_validation_count(Address.t()) :: non_neg_integer()
def address_to_validation_count(%Address{hash: hash}) do
@spec address_to_validation_count(Hash.Address.t()) :: non_neg_integer()
def address_to_validation_count(hash) do
query = from(block in Block, where: block.miner_hash == ^hash, select: fragment("COUNT(*)"))
Repo.one(query)

@ -4,7 +4,7 @@ defmodule Explorer.Chain.AddressTokenTransferCsvExporter do
"""
alias Explorer.{Chain, PagingOptions}
alias Explorer.Chain.{Address, TokenTransfer, Transaction}
alias Explorer.Chain.{TokenTransfer, Transaction}
alias NimbleCSV.RFC4180
@necessity_by_association [
@ -24,18 +24,18 @@ defmodule Explorer.Chain.AddressTokenTransferCsvExporter do
@paging_options %PagingOptions{page_size: @page_size + 1}
def export(address) do
address
address.hash
|> fetch_all_transactions(@paging_options)
|> to_token_transfers()
|> to_csv_format(address)
|> dump_to_stream()
end
defp fetch_all_transactions(address, paging_options, acc \\ []) do
defp fetch_all_transactions(address_hash, paging_options, acc \\ []) do
options = Keyword.merge(@necessity_by_association, paging_options: paging_options)
transactions =
address
address_hash
|> Chain.address_to_transactions_with_rewards(options)
|> Enum.filter(fn transaction -> Enum.count(transaction.token_transfers) > 0 end)
@ -44,7 +44,7 @@ defmodule Explorer.Chain.AddressTokenTransferCsvExporter do
case Enum.split(transactions, @page_size) do
{_transactions, [%Transaction{block_number: block_number, index: index}]} ->
new_paging_options = %{@paging_options | key: {block_number, index}}
fetch_all_transactions(address, new_paging_options, new_acc)
fetch_all_transactions(address_hash, new_paging_options, new_acc)
{_, []} ->
new_acc
@ -90,7 +90,7 @@ defmodule Explorer.Chain.AddressTokenTransferCsvExporter do
token_transfer.from_address |> to_string() |> String.downcase(),
token_transfer.to_address |> to_string() |> String.downcase(),
token_transfer.token_contract_address |> to_string() |> String.downcase(),
type(token_transfer, address),
type(token_transfer, address.hash),
token_transfer.token.symbol,
token_transfer.amount,
fee(token_transfer.transaction),
@ -102,9 +102,9 @@ defmodule Explorer.Chain.AddressTokenTransferCsvExporter do
Stream.concat([row_names], token_transfer_lists)
end
defp type(%TokenTransfer{from_address_hash: from_address}, %Address{hash: from_address}), do: "OUT"
defp type(%TokenTransfer{from_address_hash: address_hash}, address_hash), do: "OUT"
defp type(%TokenTransfer{to_address_hash: to_address}, %Address{hash: to_address}), do: "IN"
defp type(%TokenTransfer{to_address_hash: address_hash}, address_hash), do: "IN"
defp type(_, _), do: ""

@ -35,23 +35,23 @@ defmodule Explorer.Chain.AddressTransactionCsvExporter do
def export(address) do
exchange_rate = Market.get_exchange_rate(Explorer.coin()) || Token.null()
address
address.hash
|> fetch_all_transactions(@paging_options)
|> to_csv_format(address, exchange_rate)
|> dump_to_stream()
end
defp fetch_all_transactions(address, paging_options, acc \\ []) do
defp fetch_all_transactions(address_hash, paging_options, acc \\ []) do
options = Keyword.merge(@necessity_by_association, paging_options: paging_options)
transactions = Chain.address_to_transactions_with_rewards(address, options)
transactions = Chain.address_to_transactions_with_rewards(address_hash, options)
new_acc = transactions ++ acc
case Enum.split(transactions, @page_size) do
{_transactions, [%Transaction{block_number: block_number, index: index}]} ->
new_paging_options = %{@paging_options | key: {block_number, index}}
fetch_all_transactions(address, new_paging_options, new_acc)
fetch_all_transactions(address_hash, new_paging_options, new_acc)
{_, []} ->
new_acc
@ -93,7 +93,7 @@ defmodule Explorer.Chain.AddressTransactionCsvExporter do
to_string(transaction.from_address),
to_string(transaction.to_address),
to_string(transaction.created_contract_address),
type(transaction, address),
type(transaction, address.hash),
Wei.to(transaction.value, :wei),
fee(transaction),
transaction.status,
@ -107,9 +107,9 @@ defmodule Explorer.Chain.AddressTransactionCsvExporter do
Stream.concat([row_names], transaction_lists)
end
defp type(%Transaction{from_address_hash: from_address}, %Address{hash: from_address}), do: "OUT"
defp type(%Transaction{from_address_hash: address_hash}, address_hash), do: "OUT"
defp type(%Transaction{to_address_hash: to_address}, %Address{hash: to_address}), do: "IN"
defp type(%Transaction{to_address_hash: address_hash}, address_hash), do: "IN"
defp type(_, _), do: ""

@ -12,7 +12,6 @@ defmodule Explorer.GraphQL do
]
alias Explorer.Chain.{
Address,
Hash,
InternalTransaction,
TokenTransfer,
@ -23,12 +22,12 @@ defmodule Explorer.GraphQL do
@doc """
Returns a query to fetch transactions with a matching `to_address_hash`,
`from_address_hash`, or `created_contract_address_hash` field for a given address.
`from_address_hash`, or `created_contract_address_hash` field for a given address hash.
Orders transactions by descending block number and index.
"""
@spec address_to_transactions_query(Address.t()) :: Ecto.Query.t()
def address_to_transactions_query(%Address{hash: %Hash{byte_count: unquote(Hash.Address.byte_count())} = address_hash}) do
@spec address_to_transactions_query(Hash.Address.t()) :: Ecto.Query.t()
def address_to_transactions_query(address_hash) do
Transaction
|> order_by([transaction], desc: transaction.block_number, desc: transaction.index)
|> where([transaction], transaction.to_address_hash == ^address_hash)

@ -70,7 +70,7 @@ defmodule Explorer.ChainTest do
describe "address_to_logs/2" do
test "fetches logs" do
address = insert(:address)
%Address{hash: address_hash} = address = insert(:address)
transaction1 =
:transaction
@ -86,11 +86,11 @@ defmodule Explorer.ChainTest do
insert(:log, transaction: transaction2, index: 2, address: address)
assert Enum.count(Chain.address_to_logs(address)) == 2
assert Enum.count(Chain.address_to_logs(address_hash)) == 2
end
test "paginates logs" do
address = insert(:address)
%Address{hash: address_hash} = address = insert(:address)
transaction =
:transaction
@ -105,15 +105,15 @@ defmodule Explorer.ChainTest do
paging_options1 = %PagingOptions{page_size: 1}
[_log] = Chain.address_to_logs(address, paging_options: paging_options1)
[_log] = Chain.address_to_logs(address_hash, paging_options: paging_options1)
paging_options2 = %PagingOptions{page_size: 60, key: {transaction.block_number, transaction.index, log1.index}}
assert Enum.count(Chain.address_to_logs(address, paging_options: paging_options2)) == 50
assert Enum.count(Chain.address_to_logs(address_hash, paging_options: paging_options2)) == 50
end
test "searches logs by topic when the first topic matches" do
address = insert(:address)
%Address{hash: address_hash} = address = insert(:address)
transaction1 =
:transaction
@ -129,13 +129,13 @@ defmodule Explorer.ChainTest do
insert(:log, transaction: transaction2, index: 2, address: address, first_topic: "test")
[found_log] = Chain.address_to_logs(address, topic: "test")
[found_log] = Chain.address_to_logs(address_hash, topic: "test")
assert found_log.transaction.hash == transaction2.hash
end
test "searches logs by topic when the fourth topic matches" do
address = insert(:address)
%Address{hash: address_hash} = address = insert(:address)
transaction1 =
:transaction
@ -151,7 +151,7 @@ defmodule Explorer.ChainTest do
insert(:log, transaction: transaction2, index: 2, address: address)
[found_log] = Chain.address_to_logs(address, topic: "test")
[found_log] = Chain.address_to_logs(address_hash, topic: "test")
assert found_log.transaction.hash == transaction1.hash
end
@ -159,15 +159,15 @@ defmodule Explorer.ChainTest do
describe "address_to_transactions_with_rewards/2" do
test "without transactions" do
address = insert(:address)
%Address{hash: address_hash} = insert(:address)
assert Repo.aggregate(Transaction, :count, :hash) == 0
assert [] == Chain.address_to_transactions_with_rewards(address)
assert [] == Chain.address_to_transactions_with_rewards(address_hash)
end
test "with from transactions" do
address = insert(:address)
%Address{hash: address_hash} = address = insert(:address)
transaction =
:transaction
@ -176,12 +176,12 @@ defmodule Explorer.ChainTest do
|> Repo.preload(:token_transfers)
assert [transaction] ==
Chain.address_to_transactions_with_rewards(address, direction: :from)
Chain.address_to_transactions_with_rewards(address_hash, direction: :from)
|> Repo.preload([:block, :to_address, :from_address])
end
test "with to transactions" do
address = insert(:address)
%Address{hash: address_hash} = address = insert(:address)
transaction =
:transaction
@ -190,12 +190,12 @@ defmodule Explorer.ChainTest do
|> Repo.preload(:token_transfers)
assert [transaction] ==
Chain.address_to_transactions_with_rewards(address, direction: :to)
Chain.address_to_transactions_with_rewards(address_hash, direction: :to)
|> Repo.preload([:block, :to_address, :from_address])
end
test "with to and from transactions and direction: :from" do
address = insert(:address)
%Address{hash: address_hash} = address = insert(:address)
transaction =
:transaction
@ -205,12 +205,12 @@ defmodule Explorer.ChainTest do
# only contains "from" transaction
assert [transaction] ==
Chain.address_to_transactions_with_rewards(address, direction: :from)
Chain.address_to_transactions_with_rewards(address_hash, direction: :from)
|> Repo.preload([:block, :to_address, :from_address])
end
test "with to and from transactions and direction: :to" do
address = insert(:address)
%Address{hash: address_hash} = address = insert(:address)
transaction =
:transaction
@ -219,12 +219,12 @@ defmodule Explorer.ChainTest do
|> Repo.preload(:token_transfers)
assert [transaction] ==
Chain.address_to_transactions_with_rewards(address, direction: :to)
Chain.address_to_transactions_with_rewards(address_hash, direction: :to)
|> Repo.preload([:block, :to_address, :from_address])
end
test "with to and from transactions and no :direction option" do
address = insert(:address)
%Address{hash: address_hash} = address = insert(:address)
block = insert(:block)
transaction1 =
@ -240,7 +240,7 @@ defmodule Explorer.ChainTest do
|> Repo.preload(:token_transfers)
assert [transaction2, transaction1] ==
Chain.address_to_transactions_with_rewards(address)
Chain.address_to_transactions_with_rewards(address_hash)
|> Repo.preload([:block, :to_address, :from_address])
end
@ -259,11 +259,11 @@ defmodule Explorer.ChainTest do
transaction_index: transaction.index
)
assert [] == Chain.address_to_transactions_with_rewards(address)
assert [] == Chain.address_to_transactions_with_rewards(address.hash)
end
test "returns transactions that have token transfers for the given to_address" do
address = insert(:address)
%Address{hash: address_hash} = address = insert(:address)
transaction =
:transaction
@ -277,12 +277,12 @@ defmodule Explorer.ChainTest do
)
assert [transaction.hash] ==
Chain.address_to_transactions_with_rewards(address)
Chain.address_to_transactions_with_rewards(address_hash)
|> Enum.map(& &1.hash)
end
test "returns just the token transfers related to the given address" do
address = insert(:address)
%Address{hash: address_hash} = address = insert(:address)
transaction =
:transaction
@ -303,7 +303,7 @@ defmodule Explorer.ChainTest do
)
transaction =
address
address_hash
|> Chain.address_to_transactions_with_rewards()
|> List.first()
@ -344,7 +344,7 @@ defmodule Explorer.ChainTest do
)
transaction =
contract_address
contract_address.hash
|> Chain.address_to_transactions_with_rewards()
|> List.first()
@ -360,7 +360,8 @@ defmodule Explorer.ChainTest do
end
test "returns all token transfers when the given address is the token contract address" do
contract_address = insert(:address, contract_code: Factory.data("contract_code"))
%Address{hash: contract_address_hash} =
contract_address = insert(:address, contract_code: Factory.data("contract_code"))
transaction =
:transaction
@ -381,7 +382,7 @@ defmodule Explorer.ChainTest do
transaction: transaction
)
transaction = Chain.address_to_transactions_with_rewards(contract_address) |> List.first()
transaction = Chain.address_to_transactions_with_rewards(contract_address_hash) |> List.first()
assert Enum.count(transaction.token_transfers) == 2
end
@ -427,7 +428,7 @@ defmodule Explorer.ChainTest do
)
transactions_hashes =
paul
paul.hash
|> Chain.address_to_transactions_with_rewards()
|> Enum.map(& &1.hash)
@ -436,7 +437,7 @@ defmodule Explorer.ChainTest do
end
test "with transactions can be paginated" do
address = insert(:address)
%Address{hash: address_hash} = address = insert(:address)
second_page_hashes =
2
@ -450,7 +451,7 @@ defmodule Explorer.ChainTest do
|> with_block()
assert second_page_hashes ==
address
address_hash
|> Chain.address_to_transactions_with_rewards(
paging_options: %PagingOptions{
key: {block_number, index},
@ -462,7 +463,7 @@ defmodule Explorer.ChainTest do
end
test "returns results in reverse chronological order by block number and transaction index" do
address = insert(:address)
%Address{hash: address_hash} = address = insert(:address)
a_block = insert(:block, number: 6000)
@ -499,7 +500,7 @@ defmodule Explorer.ChainTest do
|> with_block(b_block)
result =
address
address_hash
|> Chain.address_to_transactions_with_rewards()
|> Enum.map(& &1.hash)
@ -525,7 +526,7 @@ defmodule Explorer.ChainTest do
address_type: :emission_funds
)
assert [{_, _}] = Chain.address_to_transactions_with_rewards(block.miner)
assert [{_, _}] = Chain.address_to_transactions_with_rewards(block.miner.hash)
Application.put_env(:block_scout_web, BlockScoutWeb.Chain, has_emission_funds: false)
end
@ -554,7 +555,7 @@ defmodule Explorer.ChainTest do
|> with_block()
|> Repo.preload(:token_transfers)
assert [_, {_, _}] = Chain.address_to_transactions_with_rewards(block.miner, direction: :from)
assert [_, {_, _}] = Chain.address_to_transactions_with_rewards(block.miner.hash, direction: :from)
Application.put_env(:block_scout_web, BlockScoutWeb.Chain, has_emission_funds: false)
end
@ -578,7 +579,7 @@ defmodule Explorer.ChainTest do
address_type: :emission_funds
)
assert [] == Chain.address_to_transactions_with_rewards(block.miner)
assert [] == Chain.address_to_transactions_with_rewards(block.miner.hash)
end
end
@ -590,7 +591,7 @@ defmodule Explorer.ChainTest do
|> insert(nonce: 100, from_address: address)
|> with_block(insert(:block, number: 1000))
assert Chain.total_transactions_sent_by_address(address) == 101
assert Chain.total_transactions_sent_by_address(address.hash) == 101
end
test "returns 0 when the address did not send transactions" do
@ -600,7 +601,7 @@ defmodule Explorer.ChainTest do
|> insert(nonce: 100, to_address: address)
|> with_block(insert(:block, number: 1000))
assert Chain.total_transactions_sent_by_address(address) == 0
assert Chain.total_transactions_sent_by_address(address.hash) == 0
end
end
@ -707,15 +708,15 @@ defmodule Explorer.ChainTest do
describe "address_to_incoming_transaction_count/1" do
test "without transactions" do
address = insert(:address)
%Address{hash: address_hash} = insert(:address)
assert Chain.address_to_incoming_transaction_count(address) == 0
assert Chain.address_to_incoming_transaction_count(address_hash) == 0
end
test "with transactions" do
%Transaction{to_address: to_address} = insert(:transaction)
assert Chain.address_to_incoming_transaction_count(to_address) == 1
assert Chain.address_to_incoming_transaction_count(to_address.hash) == 1
end
end
@ -1575,20 +1576,20 @@ defmodule Explorer.ChainTest do
describe "get_blocks_validated_by_address/2" do
test "returns nothing when there are no blocks" do
address = insert(:address)
%Address{hash: address_hash} = insert(:address)
assert [] = Chain.get_blocks_validated_by_address(address)
assert [] = Chain.get_blocks_validated_by_address(address_hash)
end
test "returns the blocks validated by a specified address" do
address = insert(:address)
%Address{hash: address_hash} = address = insert(:address)
another_address = insert(:address)
block = insert(:block, miner: address, miner_hash: address.hash)
insert(:block, miner: another_address, miner_hash: another_address.hash)
results =
address
address_hash
|> Chain.get_blocks_validated_by_address()
|> Enum.map(& &1.hash)
@ -1596,20 +1597,20 @@ defmodule Explorer.ChainTest do
end
test "with blocks can be paginated" do
address = insert(:address)
%Address{hash: address_hash} = address = insert(:address)
first_page_block = insert(:block, miner: address, miner_hash: address.hash, number: 0)
second_page_block = insert(:block, miner: address, miner_hash: address.hash, number: 2)
assert [first_page_block.number] ==
[paging_options: %PagingOptions{key: {1}, page_size: 1}]
|> Chain.get_blocks_validated_by_address(address)
|> Chain.get_blocks_validated_by_address(address_hash)
|> Enum.map(& &1.number)
|> Enum.reverse()
assert [second_page_block.number] ==
[paging_options: %PagingOptions{key: {3}, page_size: 1}]
|> Chain.get_blocks_validated_by_address(address)
|> Chain.get_blocks_validated_by_address(address_hash)
|> Enum.map(& &1.number)
|> Enum.reverse()
end
@ -1674,7 +1675,7 @@ defmodule Explorer.ChainTest do
)
result =
address
address.hash
|> Chain.address_to_internal_transactions()
|> Enum.map(&{&1.transaction_hash, &1.index})
@ -1683,7 +1684,7 @@ defmodule Explorer.ChainTest do
end
test "loads associations in necessity_by_association" do
address = insert(:address)
%Address{hash: address_hash} = address = insert(:address)
block = insert(:block, number: 2000)
transaction =
@ -1714,7 +1715,7 @@ defmodule Explorer.ChainTest do
transaction: %Transaction{}
}
| _
] = Chain.address_to_internal_transactions(address)
] = Chain.address_to_internal_transactions(address_hash)
assert [
%InternalTransaction{
@ -1725,7 +1726,7 @@ defmodule Explorer.ChainTest do
| _
] =
Chain.address_to_internal_transactions(
address,
address_hash,
necessity_by_association: %{
[from_address: :names] => :optional,
[to_address: :names] => :optional,
@ -1844,7 +1845,7 @@ defmodule Explorer.ChainTest do
)
result =
address
address.hash
|> Chain.address_to_internal_transactions()
|> Enum.map(&{&1.transaction_hash, &1.index})
@ -1968,7 +1969,7 @@ defmodule Explorer.ChainTest do
{second_transaction_hash, second_index},
{first_transaction_hash, first_index}
] ==
address
address.hash
|> Chain.address_to_internal_transactions(
paging_options: %PagingOptions{key: {6001, 3, 2}, page_size: 8}
)
@ -1981,7 +1982,7 @@ defmodule Explorer.ChainTest do
{second_transaction_hash, second_index},
{first_transaction_hash, first_index}
] ==
address
address.hash
|> Chain.address_to_internal_transactions(
paging_options: %PagingOptions{key: {6000, 0, 1}, page_size: 8}
)
@ -1994,7 +1995,7 @@ defmodule Explorer.ChainTest do
{second_transaction_hash, second_index},
{first_transaction_hash, first_index}
] ==
address
address.hash
|> Chain.address_to_internal_transactions(
paging_options: %PagingOptions{key: {6000, -1, -1}, page_size: 8}
)
@ -2002,7 +2003,7 @@ defmodule Explorer.ChainTest do
# block number <
assert [] ==
address
address.hash
|> Chain.address_to_internal_transactions(
paging_options: %PagingOptions{key: {2000, -1, -1}, page_size: 8}
)
@ -2010,7 +2011,7 @@ defmodule Explorer.ChainTest do
end
test "excludes internal transactions of type `call` when they are alone in the parent transaction" do
address = insert(:address)
%Address{hash: address_hash} = address = insert(:address)
transaction =
:transaction
@ -2025,11 +2026,11 @@ defmodule Explorer.ChainTest do
transaction_index: transaction.index
)
assert Enum.empty?(Chain.address_to_internal_transactions(address))
assert Enum.empty?(Chain.address_to_internal_transactions(address_hash))
end
test "includes internal transactions of type `create` even when they are alone in the parent transaction" do
address = insert(:address)
%Address{hash: address_hash} = address = insert(:address)
transaction =
:transaction
@ -2046,7 +2047,7 @@ defmodule Explorer.ChainTest do
transaction_index: transaction.index
)
actual = Enum.at(Chain.address_to_internal_transactions(address), 0)
actual = Enum.at(Chain.address_to_internal_transactions(address_hash), 0)
assert {actual.transaction_hash, actual.index} == {expected.transaction_hash, expected.index}
end

@ -4,12 +4,14 @@ defmodule Explorer.GraphQLTest do
import Explorer.Factory
alias Explorer.{GraphQL, Repo}
alias Explorer.Chain.Address
describe "address_to_transactions_query/1" do
test "with address hash with zero transactions" do
result =
:address
|> insert()
|> Map.get(:hash)
|> GraphQL.address_to_transactions_query()
|> Repo.all()
@ -17,12 +19,12 @@ defmodule Explorer.GraphQLTest do
end
test "with matching 'to_address_hash'" do
address = insert(:address)
%Address{hash: address_hash} = address = insert(:address)
transaction = insert(:transaction, to_address: address)
insert(:transaction)
[found_transaction] =
address
address_hash
|> GraphQL.address_to_transactions_query()
|> Repo.all()
@ -30,12 +32,12 @@ defmodule Explorer.GraphQLTest do
end
test "with matching 'from_address_hash'" do
address = insert(:address)
%Address{hash: address_hash} = address = insert(:address)
transaction = insert(:transaction, from_address: address)
insert(:transaction)
[found_transaction] =
address
address_hash
|> GraphQL.address_to_transactions_query()
|> Repo.all()
@ -43,12 +45,12 @@ defmodule Explorer.GraphQLTest do
end
test "with matching 'created_contract_address_hash'" do
address = insert(:address)
%Address{hash: address_hash} = address = insert(:address)
transaction = insert(:transaction, created_contract_address: address)
insert(:transaction)
[found_transaction] =
address
address_hash
|> GraphQL.address_to_transactions_query()
|> Repo.all()
@ -60,7 +62,7 @@ defmodule Explorer.GraphQLTest do
second_block = insert(:block)
third_block = insert(:block)
address = insert(:address)
%Address{hash: address_hash} = address = insert(:address)
3
|> insert_list(:transaction, from_address: address)
@ -75,7 +77,7 @@ defmodule Explorer.GraphQLTest do
|> with_block(first_block)
found_transactions =
address
address_hash
|> GraphQL.address_to_transactions_query()
|> Repo.all()

Loading…
Cancel
Save