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. 123
      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 - [#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 ### 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 - [#2305](https://github.com/poanetwork/blockscout/pull/2305) - Improve Address controllers
- [#2302](https://github.com/poanetwork/blockscout/pull/2302) - fix names for xDai source - [#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 - [#2289](https://github.com/poanetwork/blockscout/pull/2289) - Optional websockets for dev environment

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

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

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

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

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

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

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

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

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

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

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

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

@ -74,7 +74,7 @@
<%= if contract?(@address) do %> <%= if contract?(@address) do %>
<%= gettext(">=") %> <%= gettext(">=") %>
<span data-selector="transaction-count"> <span data-selector="transaction-count">
<%= incoming_transaction_count(@address) %> <%= incoming_transaction_count(@address.hash) %>
</span> </span>
<%= gettext("Incoming Transactions") %> <%= gettext("Incoming Transactions") %>
<% else %> <% else %>
@ -146,7 +146,7 @@
</button> </button>
</div> </div>
<div class="modal-body"> <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>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-primary" data-dismiss="modal"><%= gettext "Close" %></button> <button type="button" class="btn btn-primary" data-dismiss="modal"><%= gettext "Close" %></button>

@ -109,7 +109,7 @@
</button> </button>
</div> </div>
<div class="modal-body"> <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>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-primary" data-dismiss="modal"><%= gettext "Close" %></button> <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}") |> Timex.format!("{M}-{D}-{YYYY}")
end end
def qr_code(%Address{hash: hash}) do def qr_code(address_hash) do
hash address_hash
|> to_string() |> to_string()
|> QRCode.to_png() |> QRCode.to_png()
|> Base.encode64() |> Base.encode64()
@ -219,10 +219,10 @@ defmodule BlockScoutWeb.AddressView do
def token_title(%Token{name: name, symbol: symbol}), do: "#{name} (#{symbol})" def token_title(%Token{name: name, symbol: symbol}), do: "#{name} (#{symbol})"
def incoming_transaction_count(%Address{} = address) do def incoming_transaction_count(address_hash) do
count = Chain.address_to_incoming_transaction_count(address) address_hash
|> Chain.address_to_incoming_transaction_count()
Cldr.Number.to_string!(count, format: "#,###") |> Cldr.Number.to_string!(format: "#,###")
end end
def trimmed_hash(%Hash{} = hash) do def trimmed_hash(%Hash{} = hash) do

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

@ -126,7 +126,7 @@ defmodule Explorer.Chain do
end end
@doc """ @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 This function excludes any internal transactions in the results where the
internal transaction has no siblings within the parent transaction. 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. 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() 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, %{}) necessity_by_association = Keyword.get(options, :necessity_by_association, %{})
direction = Keyword.get(options, :direction) direction = Keyword.get(options, :direction)
paging_options = Keyword.get(options, :paging_options, @default_paging_options) paging_options = Keyword.get(options, :paging_options, @default_paging_options)
@ -171,13 +171,13 @@ defmodule Explorer.Chain do
end end
@doc """ @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 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. 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() @spec total_transactions_sent_by_address(Hash.Address.t()) :: non_neg_integer()
def total_transactions_sent_by_address(%Address{hash: address_hash}) do def total_transactions_sent_by_address(address_hash) do
last_nonce = last_nonce =
address_hash address_hash
|> Transaction.last_nonce_by_address_query() |> Transaction.last_nonce_by_address_query()
@ -190,9 +190,9 @@ defmodule Explorer.Chain do
end end
@doc """ @doc """
Fetches the transactions related to the given address, including transactions Fetches the transactions related to the address with the given hash, including
that only have the address in the `token_transfers` related table and rewards transactions that only have the address in the `token_transfers` related table
for block validation. and rewards for block validation.
This query is divided into multiple subqueries intentionally in order to This query is divided into multiple subqueries intentionally in order to
improve the listing performance. improve the listing performance.
@ -214,14 +214,10 @@ defmodule Explorer.Chain do
the `block_number` and `index` that are passed. 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() Transaction.t()
] ]
def address_to_transactions_with_rewards( def address_to_transactions_with_rewards(address_hash, options \\ []) when is_list(options) do
%Address{hash: %Hash{byte_count: unquote(Hash.Address.byte_count())} = address_hash},
options \\ []
)
when is_list(options) do
direction = Keyword.get(options, :direction) direction = Keyword.get(options, :direction)
necessity_by_association = Keyword.get(options, :necessity_by_association, %{}) necessity_by_association = Keyword.get(options, :necessity_by_association, %{})
paging_options = Keyword.get(options, :paging_options, @default_paging_options) paging_options = Keyword.get(options, :paging_options, @default_paging_options)
@ -256,14 +252,8 @@ defmodule Explorer.Chain do
end end
end end
@spec address_to_logs(Address.t(), Keyword.t()) :: [ @spec address_to_logs(Hash.Address.t(), Keyword.t()) :: [Log.t()]
Log.t() def address_to_logs(address_hash, options \\ []) when is_list(options) do
]
def address_to_logs(
%Address{hash: %Hash{byte_count: unquote(Hash.Address.byte_count())} = address_hash},
options \\ []
)
when is_list(options) do
paging_options = Keyword.get(options, :paging_options) || %PagingOptions{page_size: 50} 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} {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) Repo.aggregate(query, :count, :hash)
end end
@spec address_to_incoming_transaction_count(Address.t()) :: non_neg_integer() @spec address_to_incoming_transaction_count(Hash.Address.t()) :: non_neg_integer()
def address_to_incoming_transaction_count(%Address{hash: address_hash}) do def address_to_incoming_transaction_count(address_hash) do
paging_options = %PagingOptions{page_size: @max_incoming_transactions_count} paging_options = %PagingOptions{page_size: @max_incoming_transactions_count}
base_query = base_query =
@ -1333,7 +1323,7 @@ defmodule Explorer.Chain do
end end
@doc """ @doc """
Finds all Blocks validated by the address given. Finds all Blocks validated by the address with the given hash.
## Options ## Options
* `:necessity_by_association` - use to load `t:association/0` as `:required` or `:optional`. If an association is * `: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( @spec get_blocks_validated_by_address(
[paging_options | necessity_by_association_option], [paging_options | necessity_by_association_option],
Address.t() Hash.Address.t()
) :: [Block.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, %{}) necessity_by_association = Keyword.get(options, :necessity_by_association, %{})
paging_options = Keyword.get(options, :paging_options, @default_paging_options) paging_options = Keyword.get(options, :paging_options, @default_paging_options)
Block Block
|> join_associations(necessity_by_association) |> join_associations(necessity_by_association)
|> where(miner_hash: ^hash) |> where(miner_hash: ^address_hash)
|> page_blocks(paging_options) |> page_blocks(paging_options)
|> limit(^paging_options.page_size) |> limit(^paging_options.page_size)
|> order_by(desc: :number) |> order_by(desc: :number)
@ -1379,10 +1369,10 @@ defmodule Explorer.Chain do
end end
@doc """ @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() @spec address_to_validation_count(Hash.Address.t()) :: non_neg_integer()
def address_to_validation_count(%Address{hash: hash}) do def address_to_validation_count(hash) do
query = from(block in Block, where: block.miner_hash == ^hash, select: fragment("COUNT(*)")) query = from(block in Block, where: block.miner_hash == ^hash, select: fragment("COUNT(*)"))
Repo.one(query) Repo.one(query)

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

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

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

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

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

Loading…
Cancel
Save