Merge branch 'master' into json-rpc-error-messages

pull/1701/head
Zach Daniel 6 years ago committed by GitHub
commit ecb226d113
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .circleci/config.yml
  2. 28
      CHANGELOG.md
  3. 17
      PULL_REQUEST_TEMPLATE.md
  4. 16
      apps/block_scout_web/assets/css/_code.scss
  5. 2
      apps/block_scout_web/config/config.exs
  6. 6
      apps/block_scout_web/lib/block_scout_web/chain.ex
  7. 4
      apps/block_scout_web/lib/block_scout_web/controllers/address_coin_balance_controller.ex
  8. 4
      apps/block_scout_web/lib/block_scout_web/controllers/address_contract_controller.ex
  9. 4
      apps/block_scout_web/lib/block_scout_web/controllers/address_decompiled_contract_controller.ex
  10. 4
      apps/block_scout_web/lib/block_scout_web/controllers/address_internal_transaction_controller.ex
  11. 4
      apps/block_scout_web/lib/block_scout_web/controllers/address_read_contract_controller.ex
  12. 4
      apps/block_scout_web/lib/block_scout_web/controllers/address_token_controller.ex
  13. 4
      apps/block_scout_web/lib/block_scout_web/controllers/address_token_transfer_controller.ex
  14. 4
      apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex
  15. 4
      apps/block_scout_web/lib/block_scout_web/controllers/address_validation_controller.ex
  16. 16
      apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/transaction_controller.ex
  17. 8
      apps/block_scout_web/lib/block_scout_web/etherscan.ex
  18. 2
      apps/block_scout_web/lib/block_scout_web/notifier.ex
  19. 2
      apps/block_scout_web/lib/block_scout_web/templates/address/_tabs.html.eex
  20. 6
      apps/block_scout_web/lib/block_scout_web/templates/address_decompiled_contract/index.html.eex
  21. 2
      apps/block_scout_web/lib/block_scout_web/templates/layout/_footer.html.eex
  22. 41
      apps/block_scout_web/lib/block_scout_web/views/address_decompiled_contract_view.ex
  23. 8
      apps/block_scout_web/lib/block_scout_web/views/api/rpc/address_view.ex
  24. 14
      apps/block_scout_web/lib/block_scout_web/views/api/rpc/transaction_view.ex
  25. 18
      apps/block_scout_web/priv/gettext/default.pot
  26. 22
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  27. 56
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/transaction_controller_test.exs
  28. 74
      apps/block_scout_web/test/block_scout_web/views/address_decompiled_contract_view_test.exs
  29. 13
      apps/explorer/config/config.exs
  30. 9
      apps/explorer/lib/explorer/chain/block_number_cache.ex
  31. 2
      apps/explorer/lib/explorer/chain/transaction_count_cache.ex
  32. 4
      apps/explorer/lib/explorer/counters/addresses_with_balance_counter.ex
  33. 59
      apps/explorer/lib/explorer/counters/average_block_time.ex
  34. 38
      apps/explorer/test/explorer/counters/average_block_time_test.exs
  35. 8
      apps/indexer/config/config.exs
  36. 9
      apps/indexer/lib/indexer/application.ex
  37. 66
      apps/indexer/lib/indexer/block/catchup/fetcher.ex
  38. 6
      apps/indexer/lib/indexer/block/catchup/sequence.ex
  39. 104
      apps/indexer/lib/indexer/block/fetcher.ex
  40. 210
      apps/indexer/lib/indexer/block/realtime/fetcher.ex
  41. 46
      apps/indexer/lib/indexer/block/reward/supervisor.ex
  42. 78
      apps/indexer/lib/indexer/block/supervisor.ex
  43. 21
      apps/indexer/lib/indexer/block/transform.ex
  44. 38
      apps/indexer/lib/indexer/block/uncle/supervisor.ex
  45. 39
      apps/indexer/lib/indexer/code/supervisor.ex
  46. 39
      apps/indexer/lib/indexer/coin_balance/supervisor.ex
  47. 77
      apps/indexer/lib/indexer/fetcher.ex
  48. 20
      apps/indexer/lib/indexer/fetcher/block_reward.ex
  49. 7
      apps/indexer/lib/indexer/fetcher/coin_balance.ex
  50. 15
      apps/indexer/lib/indexer/fetcher/coin_balance_on_demand.ex
  51. 14
      apps/indexer/lib/indexer/fetcher/contract_code.ex
  52. 12
      apps/indexer/lib/indexer/fetcher/internal_transaction.ex
  53. 24
      apps/indexer/lib/indexer/fetcher/pending_transaction.ex
  54. 9
      apps/indexer/lib/indexer/fetcher/replaced_transaction.ex
  55. 7
      apps/indexer/lib/indexer/fetcher/token.ex
  56. 7
      apps/indexer/lib/indexer/fetcher/token_balance.ex
  57. 7
      apps/indexer/lib/indexer/fetcher/token_updater.ex
  58. 23
      apps/indexer/lib/indexer/fetcher/uncle_block.ex
  59. 39
      apps/indexer/lib/indexer/internal_transaction/supervisor.ex
  60. 39
      apps/indexer/lib/indexer/pending_transaction/supervisor.ex
  61. 47
      apps/indexer/lib/indexer/replaced_transaction/supervisor.ex
  62. 97
      apps/indexer/lib/indexer/shrinkable/supervisor.ex
  63. 137
      apps/indexer/lib/indexer/supervisor.ex
  64. 1
      apps/indexer/lib/indexer/temporary/addresses_without_code.ex
  65. 38
      apps/indexer/lib/indexer/temporary/addresses_without_code/supervisor.ex
  66. 4
      apps/indexer/lib/indexer/temporary/failed_created_addresses.ex
  67. 38
      apps/indexer/lib/indexer/temporary/failed_created_addresses/supervisor.ex
  68. 7
      apps/indexer/lib/indexer/temporary/uncataloged_token_transfers.ex
  69. 41
      apps/indexer/lib/indexer/token/supervisor.ex
  70. 38
      apps/indexer/lib/indexer/token_balance/supervisor.ex
  71. 7
      apps/indexer/lib/indexer/token_balances.ex
  72. 44
      apps/indexer/lib/indexer/token_transfer/uncataloged/supervisor.ex
  73. 9
      apps/indexer/lib/indexer/token_transfers.ex
  74. 2
      apps/indexer/lib/indexer/transform/address_coin_balances.ex
  75. 2
      apps/indexer/lib/indexer/transform/address_token_balances.ex
  76. 17
      apps/indexer/lib/indexer/transform/addresses.ex
  77. 20
      apps/indexer/lib/indexer/transform/blocks.ex
  78. 8
      apps/indexer/lib/indexer/transform/blocks/base.ex
  79. 10
      apps/indexer/lib/indexer/transform/blocks/clique.ex
  80. 4
      apps/indexer/lib/indexer/transform/mint_transfers.ex
  81. 2
      apps/indexer/lib/indexer/transform/token_transfers.ex
  82. 24
      apps/indexer/test/indexer/block/catchup/bound_interval_supervisor_test.exs
  83. 9
      apps/indexer/test/indexer/block/catchup/fetcher_test.exs
  84. 4
      apps/indexer/test/indexer/block/catchup/sequence_test.exs
  85. 30
      apps/indexer/test/indexer/block/fetcher_test.exs
  86. 66
      apps/indexer/test/indexer/block/realtime/fetcher_test.exs
  87. 40
      apps/indexer/test/indexer/block/util_test.exs
  88. 48
      apps/indexer/test/indexer/fetcher/block_reward_test.exs
  89. 32
      apps/indexer/test/indexer/fetcher/coin_balance_on_demand_test.exs
  90. 14
      apps/indexer/test/indexer/fetcher/coin_balance_test.exs
  91. 8
      apps/indexer/test/indexer/fetcher/contract_code_test.exs
  92. 31
      apps/indexer/test/indexer/fetcher/internal_transaction_test.exs
  93. 4
      apps/indexer/test/indexer/fetcher/pending_transaction_test.exs
  94. 8
      apps/indexer/test/indexer/fetcher/replaced_transaction_test.exs
  95. 18
      apps/indexer/test/indexer/fetcher/token_balance_test.exs
  96. 8
      apps/indexer/test/indexer/fetcher/token_test.exs
  97. 8
      apps/indexer/test/indexer/fetcher/token_updater_test.exs
  98. 9
      apps/indexer/test/indexer/fetcher/uncle_block_test.exs
  99. 7
      apps/indexer/test/indexer/temporary/addresses_without_code_test.exs
  100. 2
      apps/indexer/test/indexer/temporary/failed_created_addresses_test.exs
  101. Some files were not shown because too many files have changed in this diff Show More

@ -189,7 +189,7 @@ jobs:
- restore_cache:
keys:
- v7-mix-dailyzer-{{ checksum "OTP_VERSION.lock" }}-{{ checksum "ELIXIR_VERSION.lock" }}-{{ checksum "mix.lock" }}
- v7-mix-dialyzer-{{ checksum "OTP_VERSION.lock" }}-{{ checksum "ELIXIR_VERSION.lock" }}-{{ checksum "mix.lock" }}
- v7-mix-dialyzer-{{ checksum "OTP_VERSION.lock" }}-{{ checksum "ELIXIR_VERSION.lock" }}-{{ checksum "mix.exs" }}
- v7-mix-dialyzer-{{ checksum "OTP_VERSION.lock" }}-{{ checksum "ELIXIR_VERSION.lock" }}

@ -1,11 +1,32 @@
## Current
### Features
- [#1739](https://github.com/poanetwork/blockscout/pull/1739) - highlight decompiled source code
### Fixes
- [#1724](https://github.com/poanetwork/blockscout/pull/1724) - Remove internal tx and token balance fetching from realtime fetcher
- [#1727](https://github.com/poanetwork/blockscout/pull/1727) - add logs pagination in rpc api
- [#1740](https://github.com/poanetwork/blockscout/pull/1740) - fix empty block time
- [#1743](https://github.com/poanetwork/blockscout/pull/1743) - sort decompiled smart contracts in lexicographical order
### Chore
- [#1749](https://github.com/poanetwork/blockscout/pull/1749) - Replace the link in the footer with the official POA announcements tg channel link
- [#1718](https://github.com/poanetwork/blockscout/pull/1718) - Flatten indexer module hierarchy and supervisor tree
- [#1753](https://github.com/poanetwork/blockscout/pull/1753) - Add a check mark to decompiled contract tab
## 1.3.9-beta
### Features
- [#1662](https://github.com/poanetwork/blockscout/pull/1662) - allow specifying number of optimization runs
- [#1654](https://github.com/poanetwork/blockscout/pull/1654) - add decompiled code tab
- [#1661](https://github.com/poanetwork/blockscout/pull/1661) - try to compile smart contract with the latest evm version
- [#1665](https://github.com/poanetwork/blockscout/pull/1665) - Add contract verification RPC endpoint.
- [#1706](https://github.com/poanetwork/blockscout/pull/1706) - allow setting update interval for addresses with b
### Fixes
@ -14,9 +35,16 @@
- [#1688](https://github.com/poanetwork/blockscout/pull/1688) - do not fail if failure reason is atom
- [#1692](https://github.com/poanetwork/blockscout/pull/1692) - exclude decompiled smart contract from encoding
- [#1684](https://github.com/poanetwork/blockscout/pull/1684) - Discard child block with parent_hash not matching hash of imported block
- [#1699](https://github.com/poanetwork/blockscout/pull/1699) - use seconds as transaction cache period measure
- [#1697](https://github.com/poanetwork/blockscout/pull/1697) - fix failing in rpc if balance is empty
- [#1711](https://github.com/poanetwork/blockscout/pull/1711) - rescue failing repo in block number cache update
- [#1712](https://github.com/poanetwork/blockscout/pull/1712) - do not set contract code from transaction input
- [#1714](https://github.com/poanetwork/blockscout/pull/1714) - fix average block time calculation
### Chore
- [#1693](https://github.com/poanetwork/blockscout/pull/1693) - Add a checklist to the PR template
## 1.3.8-beta

@ -18,3 +18,20 @@
## Upgrading
*If you have any Incompatible Changes in the above Changelog, outline how users of prior versions can upgrade once this PR lands or when reviewers are testing locally. A common upgrading step is "Database reset and re-index required".*
## Checklist for your PR
<!--
Ideally a PR has all of the checkmarks set.
If something in this list is irrelevant to your PR, you should still set this
checkmark indicating that you are sure it is dealt with (be that by irrelevance).
If you don't set a checkmark (e. g. don't add a test for new functionality),
you must be able to justify that.
-->
- [ ] I added an entry to `CHANGELOG.md` with this PR
- [ ] If I added new functionality, I added tests covering it.
- [ ] If I fixed a bug, I added a regression test to prevent the bug from silently reappearing again.
- [ ] I checked whether I should update the docs and did so if necessary

@ -6,6 +6,22 @@ pre {
white-space: pre-wrap;
}
.pre-decompiled code {
white-space: pre-wrap;
counter-increment: line;
}
.pre-decompiled code::before {
content: counter(line);
display: inline-block;
width: 2.5em; /* Fixed width */
border-right: 1px solid #ddd;
padding: 0 .5em;
margin-right: .5em;
color: #888;
-webkit-user-select: none;
}
.pre-scrollable-shorty {
max-height: $pre-scrollable-max-height / 7;
}

@ -93,7 +93,7 @@ config :block_scout_web, BlockScoutWeb.Gettext, locales: ~w(en), default_locale:
config :block_scout_web, BlockScoutWeb.SocialMedia,
twitter: "PoaNetwork",
telegram: "oraclesnetwork",
telegram: "poa_network",
facebook: "PoaNetwork",
instagram: "PoaNetwork"

@ -115,7 +115,7 @@ defmodule BlockScoutWeb.Chain do
end
end
def paging_options(%{"index" => index_string}) do
def paging_options(%{"index" => index_string}) when is_binary(index_string) do
with {index, ""} <- Integer.parse(index_string) do
[paging_options: %{@default_paging_options | key: {index}}]
else
@ -124,6 +124,10 @@ defmodule BlockScoutWeb.Chain do
end
end
def paging_options(%{"index" => index}) when is_integer(index) do
[paging_options: %{@default_paging_options | key: {index}}]
end
def paging_options(%{"inserted_at" => inserted_at_string, "hash" => hash_string}) do
with {:ok, inserted_at, _} <- DateTime.from_iso8601(inserted_at_string),
{:ok, hash} <- string_to_transaction_hash(hash_string) do

@ -11,7 +11,7 @@ defmodule BlockScoutWeb.AddressCoinBalanceController do
alias BlockScoutWeb.AddressCoinBalanceView
alias Explorer.{Chain, Market}
alias Explorer.ExchangeRates.Token
alias Indexer.CoinBalance.OnDemandFetcher
alias Indexer.Fetcher.CoinBalanceOnDemand
alias Phoenix.View
def index(conn, %{"address_id" => address_hash_string, "type" => "JSON"} = params) do
@ -62,7 +62,7 @@ defmodule BlockScoutWeb.AddressCoinBalanceController do
{:ok, address} <- Chain.hash_to_address(address_hash) do
render(conn, "index.html",
address: address,
coin_balance_status: OnDemandFetcher.trigger_fetch(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),

@ -5,7 +5,7 @@ defmodule BlockScoutWeb.AddressContractController do
alias Explorer.{Chain, Market}
alias Explorer.ExchangeRates.Token
alias Indexer.CoinBalance.OnDemandFetcher
alias Indexer.Fetcher.CoinBalanceOnDemand
def index(conn, %{"address_id" => address_hash_string}) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
@ -14,7 +14,7 @@ defmodule BlockScoutWeb.AddressContractController do
conn,
"index.html",
address: address,
coin_balance_status: OnDemandFetcher.trigger_fetch(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)

@ -5,7 +5,7 @@ defmodule BlockScoutWeb.AddressDecompiledContractController do
alias Explorer.{Chain, Market}
alias Explorer.ExchangeRates.Token
alias Indexer.CoinBalance.OnDemandFetcher
alias Indexer.Fetcher.CoinBalanceOnDemand
def index(conn, %{"address_id" => address_hash_string}) do
with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string),
@ -14,7 +14,7 @@ defmodule BlockScoutWeb.AddressDecompiledContractController do
conn,
"index.html",
address: address,
coin_balance_status: OnDemandFetcher.trigger_fetch(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)

@ -11,7 +11,7 @@ defmodule BlockScoutWeb.AddressInternalTransactionController do
alias BlockScoutWeb.InternalTransactionView
alias Explorer.{Chain, Market}
alias Explorer.ExchangeRates.Token
alias Indexer.CoinBalance.OnDemandFetcher
alias Indexer.Fetcher.CoinBalanceOnDemand
alias Phoenix.View
def index(conn, %{"address_id" => address_hash_string, "type" => "JSON"} = params) do
@ -67,7 +67,7 @@ defmodule BlockScoutWeb.AddressInternalTransactionController do
conn,
"index.html",
address: address,
coin_balance_status: OnDemandFetcher.trigger_fetch(address),
coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address),
current_path: current_path(conn),
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
filter: params["filter"],

@ -10,7 +10,7 @@ defmodule BlockScoutWeb.AddressReadContractController do
alias Explorer.{Chain, Market}
alias Explorer.ExchangeRates.Token
alias Indexer.CoinBalance.OnDemandFetcher
alias Indexer.Fetcher.CoinBalanceOnDemand
import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1]
@ -21,7 +21,7 @@ defmodule BlockScoutWeb.AddressReadContractController do
conn,
"index.html",
address: address,
coin_balance_status: OnDemandFetcher.trigger_fetch(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)

@ -3,7 +3,7 @@ defmodule BlockScoutWeb.AddressTokenController do
alias Explorer.{Chain, Market}
alias Explorer.ExchangeRates.Token
alias Indexer.CoinBalance.OnDemandFetcher
alias Indexer.Fetcher.CoinBalanceOnDemand
import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1]
import BlockScoutWeb.Chain, only: [next_page_params: 3, paging_options: 1, split_list_by_page: 1]
@ -18,7 +18,7 @@ defmodule BlockScoutWeb.AddressTokenController do
conn,
"index.html",
address: address,
coin_balance_status: OnDemandFetcher.trigger_fetch(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),

@ -4,7 +4,7 @@ defmodule BlockScoutWeb.AddressTokenTransferController do
alias BlockScoutWeb.TransactionView
alias Explorer.ExchangeRates.Token
alias Explorer.{Chain, Market}
alias Indexer.CoinBalance.OnDemandFetcher
alias Indexer.Fetcher.CoinBalanceOnDemand
alias Phoenix.View
import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1]
@ -81,7 +81,7 @@ defmodule BlockScoutWeb.AddressTokenTransferController do
conn,
"index.html",
address: address,
coin_balance_status: OnDemandFetcher.trigger_fetch(address),
coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address),
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
current_path: current_path(conn),
token: token,

@ -11,7 +11,7 @@ defmodule BlockScoutWeb.AddressTransactionController do
alias BlockScoutWeb.TransactionView
alias Explorer.{Chain, Market}
alias Explorer.ExchangeRates.Token
alias Indexer.CoinBalance.OnDemandFetcher
alias Indexer.Fetcher.CoinBalanceOnDemand
alias Phoenix.View
@transaction_necessity_by_association [
@ -91,7 +91,7 @@ defmodule BlockScoutWeb.AddressTransactionController do
conn,
"index.html",
address: address,
coin_balance_status: OnDemandFetcher.trigger_fetch(address),
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),

@ -12,7 +12,7 @@ defmodule BlockScoutWeb.AddressValidationController do
alias BlockScoutWeb.BlockView
alias Explorer.ExchangeRates.Token
alias Explorer.{Chain, Market}
alias Indexer.CoinBalance.OnDemandFetcher
alias Indexer.Fetcher.CoinBalanceOnDemand
alias Phoenix.View
def index(conn, %{"address_id" => address_hash_string, "type" => "JSON"} = params) do
@ -76,7 +76,7 @@ defmodule BlockScoutWeb.AddressValidationController do
conn,
"index.html",
address: address,
coin_balance_status: OnDemandFetcher.trigger_fetch(address),
coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address),
current_path: current_path(conn),
transaction_count: transaction_count(address),
validation_count: validation_count(address),

@ -1,14 +1,24 @@
defmodule BlockScoutWeb.API.RPC.TransactionController do
use BlockScoutWeb, :controller
import BlockScoutWeb.Chain, only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1]
alias Explorer.Chain
def gettxinfo(conn, params) do
with {:txhash_param, {:ok, txhash_param}} <- fetch_txhash(params),
{:format, {:ok, transaction_hash}} <- to_transaction_hash(txhash_param),
{:transaction, {:ok, transaction}} <- transaction_from_hash(transaction_hash) do
logs = Chain.transaction_to_logs(transaction)
render(conn, :gettxinfo, %{transaction: transaction, block_height: Chain.block_height(), logs: logs})
{:transaction, {:ok, transaction}} <- transaction_from_hash(transaction_hash),
paging_options <- paging_options(params) do
logs = Chain.transaction_to_logs(transaction, paging_options)
{logs, next_page} = split_list_by_page(logs)
render(conn, :gettxinfo, %{
transaction: transaction,
block_height: Chain.block_height(),
logs: logs,
next_page_params: next_page_params(next_page, logs, params)
})
else
{:transaction, :error} ->
render(conn, :error, error: "Transaction not found")

@ -1995,7 +1995,13 @@ defmodule BlockScoutWeb.Etherscan do
description: "Transaction hash. Hash of contents of the transaction."
}
],
optional_params: [],
optional_params: [
%{
key: "index",
type: "integer",
description: "A nonnegative integer that represents the log index to be used for pagination."
}
],
responses: [
%{
code: "200",

@ -116,7 +116,7 @@ defmodule BlockScoutWeb.Notifier do
defp broadcast_block(block) do
preloaded_block = Repo.preload(block, [[miner: :names], :transactions, :rewards])
average_block_time = AverageBlockTime.average_block_time(preloaded_block)
average_block_time = AverageBlockTime.average_block_time()
Endpoint.broadcast("blocks:new_block", "new_block", %{
block: preloaded_block,

@ -65,6 +65,7 @@
to: address_decompiled_contract_path(@conn, :index, @address.hash),
class: "nav-link #{tab_status("decompiled_contracts", @conn.request_path)}") do %>
<%= gettext("Decompiled code") %>
<i class="far fa-check-circle text-success"></i>
<% end %>
</li>
<% end %>
@ -131,6 +132,7 @@
to: address_decompiled_contract_path(@conn, :index, @address.hash),
class: "dropdown-item #{tab_status("contracts", @conn.request_path)}") do %>
<%= gettext("Decompiled code") %>
<i class="far fa-check-circle text-success"></i>
<% end %>
<% end %>
<% end %>

@ -6,11 +6,11 @@
<%= render BlockScoutWeb.AddressView, "_tabs.html", assigns %>
</div>
<%= for {contract, _i} <- Enum.with_index(@address.decompiled_smart_contracts) do %>
<%= for contract <- sort_contracts_by_version(@address.decompiled_smart_contracts) do %>
<div class="card-body">
<h3><%= gettext "Decompiler version" %></h3>
<div class="tile tile-muted">
<pre class="pre-wrap pre-scrollable"><code class="nohighlight"><%= contract.decompiler_version %></code></pre>
<pre class="pre-wrap"><code class="nohighlight"><%= contract.decompiler_version %></code></pre>
</div>
<br>
<section>
@ -21,7 +21,7 @@
</button>
</div>
<div class="tile tile-muted">
<pre class="pre-wrap pre-scrollable"><code class="nohighlight"><%= contract.decompiled_source_code %></code></pre>
<pre class="pre-decompiled pre-scrollable"><%= raw(highlight_decompiled_code(contract.decompiled_source_code)) %></pre>
</div>
</section>
</div>

@ -17,7 +17,7 @@
<a href="https://www.twitter.com/PoaNetwork/" rel="noreferrer" target="_blank" class="icon-link" data-toggle="tooltip" data-placement="top" title="<%= gettext("Twitter") %>">
<i class="fab fa-twitter"></i>
</a>
<a href="http://t.me/oraclesnetwork" rel="noreferrer" target="_blank" class="icon-link" data-toggle="tooltip" data-placement="top" title="<%= gettext("Telegram") %>">
<a href="https://t.me/poa_network" rel="noreferrer" target="_blank" class="icon-link" data-toggle="tooltip" data-placement="top" title="<%= gettext("Telegram") %>">
<i class="fab fa-telegram-plane"></i>
</a>
</div>

@ -1,3 +1,44 @@
defmodule BlockScoutWeb.AddressDecompiledContractView do
use BlockScoutWeb, :view
@colors %{
"\e[95m" => "136, 0, 0",
# red
"\e[91m" => "236, 89, 58",
# gray
"\e[38;5;8m" => "111, 110, 111",
# green
"\e[32m" => "57, 115, 0",
# yellowgreen
"\e[93m" => "57, 115, 0",
# yellow
"\e[92m" => "119, 232, 81",
# red
"\e[94m" => "136, 0, 0"
}
def highlight_decompiled_code(code) do
@colors
|> Enum.reduce(code, fn {symbol, rgb}, acc ->
String.replace(acc, symbol, "<span style=\"color:rgb(#{rgb})\">")
end)
|> String.replace("\e[1m", "<span style=\"font-weight:bold\">")
|> String.replace("»", "&raquo;")
|> String.replace("\e[0m", "</span>")
|> add_line_numbers()
end
def sort_contracts_by_version(decompiled_contracts) do
decompiled_contracts
|> Enum.sort_by(& &1.decompiler_version)
|> Enum.reverse()
end
defp add_line_numbers(code) do
code
|> String.split("\n")
|> Enum.reduce("", fn line, acc ->
acc <> "<code>#{line}</code>\n"
end)
end
end

@ -9,7 +9,7 @@ defmodule BlockScoutWeb.API.RPC.AddressView do
end
def render("balance.json", %{addresses: [address]}) do
RPCView.render("show.json", data: "#{address.fetched_coin_balance.value}")
RPCView.render("show.json", data: balance(address))
end
def render("balance.json", assigns) do
@ -21,7 +21,7 @@ defmodule BlockScoutWeb.API.RPC.AddressView do
Enum.map(addresses, fn address ->
%{
"account" => "#{address.hash}",
"balance" => "#{address.fetched_coin_balance.value}"
"balance" => balance(address)
}
end)
@ -157,4 +157,8 @@ defmodule BlockScoutWeb.API.RPC.AddressView do
"symbol" => token.symbol
}
end
defp balance(address) do
address.fetched_coin_balance && address.fetched_coin_balance.value && "#{address.fetched_coin_balance.value}"
end
end

@ -3,8 +3,13 @@ defmodule BlockScoutWeb.API.RPC.TransactionView do
alias BlockScoutWeb.API.RPC.RPCView
def render("gettxinfo.json", %{transaction: transaction, block_height: block_height, logs: logs}) do
data = prepare_transaction(transaction, block_height, logs)
def render("gettxinfo.json", %{
transaction: transaction,
block_height: block_height,
logs: logs,
next_page_params: next_page_params
}) do
data = prepare_transaction(transaction, block_height, logs, next_page_params)
RPCView.render("show.json", data: data)
end
@ -50,7 +55,7 @@ defmodule BlockScoutWeb.API.RPC.TransactionView do
}
end
defp prepare_transaction(transaction, block_height, logs) do
defp prepare_transaction(transaction, block_height, logs, next_page_params) do
%{
"hash" => "#{transaction.hash}",
"timeStamp" => "#{DateTime.to_unix(transaction.block.timestamp)}",
@ -63,7 +68,8 @@ defmodule BlockScoutWeb.API.RPC.TransactionView do
"input" => "#{transaction.input}",
"gasLimit" => "#{transaction.gas}",
"gasUsed" => "#{transaction.gas_used}",
"logs" => Enum.map(logs, &prepare_log/1)
"logs" => Enum.map(logs, &prepare_log/1),
"next_page_params" => next_page_params
}
end

@ -188,7 +188,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:40
#: lib/block_scout_web/templates/address/_tabs.html.eex:113
#: lib/block_scout_web/templates/address/_tabs.html.eex:114
#: lib/block_scout_web/templates/address/overview.html.eex:59
#: lib/block_scout_web/templates/address_validation/index.html.eex:30
#: lib/block_scout_web/templates/address_validation/index.html.eex:57
@ -218,7 +218,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:53
#: lib/block_scout_web/templates/address/_tabs.html.eex:123
#: lib/block_scout_web/templates/address/_tabs.html.eex:124
#: lib/block_scout_web/templates/address_validation/index.html.eex:39
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:119
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:141
@ -488,7 +488,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:21
#: lib/block_scout_web/templates/address/_tabs.html.eex:100
#: lib/block_scout_web/templates/address/_tabs.html.eex:101
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:58
#: lib/block_scout_web/templates/address_validation/index.html.eex:24
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:14
@ -698,8 +698,8 @@ msgid "Query"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:75
#: lib/block_scout_web/templates/address/_tabs.html.eex:140
#: lib/block_scout_web/templates/address/_tabs.html.eex:76
#: lib/block_scout_web/templates/address/_tabs.html.eex:142
#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:33
#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:75
#: lib/block_scout_web/views/address_view.ex:298
@ -891,7 +891,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:13
#: lib/block_scout_web/templates/address/_tabs.html.eex:95
#: lib/block_scout_web/templates/address/_tabs.html.eex:96
#: lib/block_scout_web/templates/address_token/index.html.eex:11
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:12
#: lib/block_scout_web/templates/address_validation/index.html.eex:11
@ -948,7 +948,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:5
#: lib/block_scout_web/templates/address/_tabs.html.eex:90
#: lib/block_scout_web/templates/address/_tabs.html.eex:91
#: lib/block_scout_web/templates/address_transaction/index.html.eex:53
#: lib/block_scout_web/templates/address_validation/index.html.eex:14
#: lib/block_scout_web/templates/block_transaction/index.html.eex:13
@ -1399,7 +1399,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:30
#: lib/block_scout_web/templates/address/_tabs.html.eex:106
#: lib/block_scout_web/templates/address/_tabs.html.eex:107
#: lib/block_scout_web/views/address_view.ex:299
msgid "Coin Balance History"
msgstr ""
@ -1714,7 +1714,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:67
#: lib/block_scout_web/templates/address/_tabs.html.eex:133
#: lib/block_scout_web/templates/address/_tabs.html.eex:134
msgid "Decompiled code"
msgstr ""

@ -188,7 +188,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:40
#: lib/block_scout_web/templates/address/_tabs.html.eex:113
#: lib/block_scout_web/templates/address/_tabs.html.eex:114
#: lib/block_scout_web/templates/address/overview.html.eex:59
#: lib/block_scout_web/templates/address_validation/index.html.eex:30
#: lib/block_scout_web/templates/address_validation/index.html.eex:57
@ -218,7 +218,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:53
#: lib/block_scout_web/templates/address/_tabs.html.eex:123
#: lib/block_scout_web/templates/address/_tabs.html.eex:124
#: lib/block_scout_web/templates/address_validation/index.html.eex:39
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:119
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:141
@ -488,7 +488,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:21
#: lib/block_scout_web/templates/address/_tabs.html.eex:100
#: lib/block_scout_web/templates/address/_tabs.html.eex:101
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:58
#: lib/block_scout_web/templates/address_validation/index.html.eex:24
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:14
@ -698,8 +698,8 @@ msgid "Query"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:75
#: lib/block_scout_web/templates/address/_tabs.html.eex:140
#: lib/block_scout_web/templates/address/_tabs.html.eex:76
#: lib/block_scout_web/templates/address/_tabs.html.eex:142
#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:33
#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:75
#: lib/block_scout_web/views/address_view.ex:298
@ -891,7 +891,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:13
#: lib/block_scout_web/templates/address/_tabs.html.eex:95
#: lib/block_scout_web/templates/address/_tabs.html.eex:96
#: lib/block_scout_web/templates/address_token/index.html.eex:11
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:12
#: lib/block_scout_web/templates/address_validation/index.html.eex:11
@ -948,7 +948,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:5
#: lib/block_scout_web/templates/address/_tabs.html.eex:90
#: lib/block_scout_web/templates/address/_tabs.html.eex:91
#: lib/block_scout_web/templates/address_transaction/index.html.eex:53
#: lib/block_scout_web/templates/address_validation/index.html.eex:14
#: lib/block_scout_web/templates/block_transaction/index.html.eex:13
@ -1399,7 +1399,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:30
#: lib/block_scout_web/templates/address/_tabs.html.eex:106
#: lib/block_scout_web/templates/address/_tabs.html.eex:107
#: lib/block_scout_web/views/address_view.ex:299
msgid "Coin Balance History"
msgstr ""
@ -1714,7 +1714,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:67
#: lib/block_scout_web/templates/address/_tabs.html.eex:133
#: lib/block_scout_web/templates/address/_tabs.html.eex:134
msgid "Decompiled code"
msgstr ""
@ -1723,12 +1723,12 @@ msgstr ""
msgid "Decompiled contract code"
msgstr ""
#, elixir-format, fuzzy
#, elixir-format
#: lib/block_scout_web/templates/address_decompiled_contract/index.html.eex:11
msgid "Decompiler version"
msgstr ""
#, elixir-format, fuzzy
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:52
msgid "Optimization runs"
msgstr ""

@ -367,6 +367,59 @@ defmodule BlockScoutWeb.API.RPC.TransactionControllerTest do
refute response["result"]
end
test "paginates logs", %{conn: conn} do
block = insert(:block, hash: "0x30d522bcf2d8e0cabc286e6e40623c475c3bc05d0ec484ea239c103b1ac0ded9", number: 99)
transaction =
:transaction
|> insert(hash: "0x13b6bb8e06322096dc83e8d7e6332ca19919ea642212cd259c6b20e7523a0599")
|> with_block(block, status: :ok)
address = insert(:address)
Enum.each(1..100, fn _ ->
insert(:log,
address: address,
transaction: transaction,
first_topic: "first topic",
second_topic: "second topic"
)
end)
params1 = %{
"module" => "transaction",
"action" => "gettxinfo",
"txhash" => "#{transaction.hash}"
}
assert response1 =
conn
|> get("/api", params1)
|> json_response(200)
assert response1["status"] == "1"
assert response1["message"] == "OK"
assert %{
"action" => "gettxinfo",
"index" => _,
"module" => "transaction",
"txhash" => _
} = response1["result"]["next_page_params"]
params2 = response1["result"]["next_page_params"]
assert response2 =
conn
|> get("/api", params2)
|> json_response(200)
assert response2["status"] == "1"
assert response2["message"] == "OK"
assert is_nil(response2["result"]["next_page_params"])
assert response1["result"]["logs"] != response2["result"]["logs"]
end
test "with a txhash with ok status", %{conn: conn} do
block = insert(:block)
@ -409,7 +462,8 @@ defmodule BlockScoutWeb.API.RPC.TransactionControllerTest do
"data" => "#{log.data}",
"topics" => ["first topic", "second topic", nil, nil]
}
]
],
"next_page_params" => nil
}
assert response =

@ -0,0 +1,74 @@
defmodule BlockScoutWeb.AddressDecompiledContractViewTest do
use Explorer.DataCase
alias BlockScoutWeb.AddressDecompiledContractView
describe "highlight_decompiled_code/1" do
test "generate correct html code" do
code = """
#
# eveem.org 6 Feb 2019
# Decompiled source of 0x00Bd9e214FAb74d6fC21bf1aF34261765f57e875
#
# Let's make the world open source
# 
#
# I failed with these:
# - unknowne77c646d(?)
# - transferFromWithData(address _from, address _to, uint256 _value, bytes _data)
# All the rest is below.
#
# Storage definitions and getters
def storage:
allowance is uint256 => uint256 # mask(256, 0) at storage #2
stor4 is uint256 => uint8 # mask(8, 0) at storage #4
def allowance(address _owner, address _spender) payable: 
require (calldata.size - 4) >= 64
return allowance[sha3(((320 - 1) and (320 - 1) and _owner), 1), ((320 - 1) and _spender and (320 - 1))]
#
# Regular functions - see Tutorial for understanding quirks of the code
#
# folder failed in this function - may be terribly long, sorry
def unknownc47d033b(?) payable: 
if (calldata.size - 4) < 32:
revert
else:
if not (320 - 1) or not cd[4]:
revert
else:
mem[0] = (320 - 1) and (320 - 1) and cd[4]
mem[32] = 4
mem[96] = bool(stor4[((320 - 1) and (320 - 1) and cd[4])])
return bool(stor4[((320 - 1) and (320 - 1) and cd[4])])
def _fallback() payable: # default function
revert
"""
result = AddressDecompiledContractView.highlight_decompiled_code(code)
assert result ==
"<code> <span style=\"color:rgb(111, 110, 111)\">#</code>\n<code> # eveem.org 6 Feb 2019</code>\n<code> # Decompiled source of </span>0x00Bd9e214FAb74d6fC21bf1aF34261765f57e875<span style=\"color:rgb(111, 110, 111)\"></code>\n<code> #</code>\n<code> # Let's make the world open source</code>\n<code> # </span></code>\n<code> <span style=\"color:rgb(111, 110, 111)\">#</code>\n<code> # I failed with these:</code>\n<code> </span><span style=\"color:rgb(111, 110, 111)\"># - </span><span style=\"color:rgb(236, 89, 58)\">unknowne77c646d(?)</span><span style=\"color:rgb(111, 110, 111)\"></code>\n<code> </span><span style=\"color:rgb(111, 110, 111)\"># - </span><span style=\"color:rgb(236, 89, 58)\">transferFromWithData(address _from, address _to, uint256 _value, bytes _data)</span><span style=\"color:rgb(111, 110, 111)\"></code>\n<code> # All the rest is below.</code>\n<code> #</span></code>\n<code></code>\n<code></code>\n<code> <span style=\"color:rgb(111, 110, 111)\"># Storage definitions and getters</span></code>\n<code></code>\n<code> <span style=\"color:rgb(57, 115, 0)\">def</span> storage:</code>\n<code> <span style=\"color:rgb(57, 115, 0)\">allowance</span> is uint256 => uint256 <span style=\"color:rgb(111, 110, 111)\"># mask(256, 0) at storage #2</span></code>\n<code> <span style=\"color:rgb(57, 115, 0)\">stor4</span> is uint256 => uint8 <span style=\"color:rgb(111, 110, 111)\"># mask(8, 0) at storage #4</span></code>\n<code></code>\n<code> <span style=\"color:rgb(136, 0, 0)\">def </span>allowance(address <span style=\"color:rgb(57, 115, 0)\">_owner</span>, address <span style=\"color:rgb(57, 115, 0)\">_spender</span>) <span style=\"color:rgb(136, 0, 0)\">payable</span>: <span style=\"color:rgb(111, 110, 111)\"></span></code>\n<code> require (calldata.size - 4)<span style=\"font-weight:bold\"> >= </span>64</code>\n<code> return <span style=\"color:rgb(57, 115, 0)\">allowance</span><span style=\"color:rgb(57, 115, 0)\">[</span>sha3(((320 - 1)<span style=\"font-weight:bold\"> and </span>(320 - 1)<span style=\"font-weight:bold\"> and </span><span style=\"color:rgb(57, 115, 0)\">_owner</span>), 1), ((320 - 1)<span style=\"font-weight:bold\"> and </span><span style=\"color:rgb(57, 115, 0)\">_spender</span><span style=\"font-weight:bold\"> and </span>(320 - 1))<span style=\"color:rgb(57, 115, 0)\">]</span></code>\n<code></code>\n<code></code>\n<code> <span style=\"color:rgb(111, 110, 111)\">#</code>\n<code> # Regular functions - see Tutorial for understanding quirks of the code</code>\n<code> #</span></code>\n<code></code>\n<code></code>\n<code> <span style=\"color:rgb(111, 110, 111)\"># folder failed in this function - may be terribly long, sorry</span></code>\n<code> <span style=\"color:rgb(136, 0, 0)\">def </span>unknownc47d033b(?) <span style=\"color:rgb(136, 0, 0)\">payable</span>: <span style=\"color:rgb(111, 110, 111)\"></span></code>\n<code> if (calldata.size - 4)<span style=\"font-weight:bold\"> < </span>32:</code>\n<code> revert</code>\n<code> else:</code>\n<code> if not (320 - 1)<span style=\"font-weight:bold\"> or </span>not cd[4]:</code>\n<code> revert</code>\n<code> else:</code>\n<code> <span style=\"color:rgb(136, 0, 0)\">mem[</span>0<span style=\"color:rgb(136, 0, 0)\">]</span> = (320 - 1)<span style=\"font-weight:bold\"> and </span>(320 - 1)<span style=\"font-weight:bold\"> and </span>cd[4]</code>\n<code> <span style=\"color:rgb(136, 0, 0)\">mem[</span>32<span style=\"color:rgb(136, 0, 0)\">]</span> = 4</code>\n<code> <span style=\"color:rgb(136, 0, 0)\">mem[</span>96<span style=\"color:rgb(136, 0, 0)\">]</span> = bool(<span style=\"color:rgb(57, 115, 0)\">stor4</span><span style=\"color:rgb(57, 115, 0)\">[</span>((320 - 1)<span style=\"font-weight:bold\"> and </span>(320 - 1)<span style=\"font-weight:bold\"> and </span>cd[4])<span style=\"color:rgb(57, 115, 0)\">]</span>)</code>\n<code> return bool(<span style=\"color:rgb(57, 115, 0)\">stor4</span><span style=\"color:rgb(57, 115, 0)\">[</span>((320 - 1)<span style=\"font-weight:bold\"> and </span>(320 - 1)<span style=\"font-weight:bold\"> and </span>cd[4])<span style=\"color:rgb(57, 115, 0)\">]</span>)</code>\n<code></code>\n<code> <span style=\"color:rgb(136, 0, 0)\">def </span>_fallback() <span style=\"color:rgb(136, 0, 0)\">payable</span>: <span style=\"color:rgb(111, 110, 111)\"># default function</span></code>\n<code> revert</code>\n<code></code>\n"
end
end
describe "sort_contracts_by_version/1" do
test "sorts contracts in lexicographical order" do
contract2 = insert(:decompiled_smart_contract, decompiler_version: "v2")
contract1 = insert(:decompiled_smart_contract, decompiler_version: "v1")
contract3 = insert(:decompiled_smart_contract, decompiler_version: "v3")
result = AddressDecompiledContractView.sort_contracts_by_version([contract2, contract1, contract3])
assert result == [contract3, contract2, contract1]
end
end
end

@ -13,7 +13,18 @@ config :explorer,
config :explorer, Explorer.Counters.AverageBlockTime, enabled: true
config :explorer, Explorer.Counters.AddressesWithBalanceCounter, enabled: true, enable_consolidation: true
balances_update_interval =
if System.get_env("ADDRESS_WITH_BALANCES_UPDATE_INTERVAL") do
case Integer.parse(System.get_env("ADDRESS_WITH_BALANCES_UPDATE_INTERVAL")) do
{integer, ""} -> integer
_ -> nil
end
end
config :explorer, Explorer.Counters.AddressesWithBalanceCounter,
enabled: true,
enable_consolidation: true,
update_interval_in_seconds: balances_update_interval || 30 * 60
config :explorer, Explorer.ExchangeRates, enabled: true, store: :ets

@ -61,7 +61,7 @@ defmodule Explorer.Chain.BlockNumberCache do
defp update_cache do
current_time = current_time()
{min, max} = Chain.fetch_min_and_max_block_numbers()
{min, max} = min_and_max_from_db()
tuple = {min, max, current_time}
:ets.insert(@tab, {@key, tuple})
@ -85,6 +85,13 @@ defmodule Explorer.Chain.BlockNumberCache do
cache_period
end
defp min_and_max_from_db do
Chain.fetch_min_and_max_block_numbers()
rescue
_e ->
{0, 0}
end
defp current_time do
utc_now = DateTime.utc_now()

@ -143,7 +143,7 @@ defmodule Explorer.Chain.TransactionCountCache do
case System.get_env("TXS_COUNT_CACHE_PERIOD") do
value when is_binary(value) ->
case Integer.parse(value) do
{integer, ""} -> integer * 1_000 * 60 * 60
{integer, ""} -> integer * 1_000
_ -> nil
end

@ -29,6 +29,8 @@ defmodule Explorer.Counters.AddressesWithBalanceCounter do
config = Application.get_env(:explorer, Explorer.Counters.AddressesWithBalanceCounter)
@enable_consolidation Keyword.get(config, :enable_consolidation)
@update_interval_in_seconds Keyword.get(config, :update_interval_in_seconds)
@doc """
Starts a process to periodically update the counter of the token holders.
"""
@ -62,7 +64,7 @@ defmodule Explorer.Counters.AddressesWithBalanceCounter do
defp schedule_next_consolidation do
if enable_consolidation?() do
Process.send_after(self(), :consolidate, :timer.minutes(30))
Process.send_after(self(), :consolidate, :timer.seconds(@update_interval_in_seconds))
end
end

@ -11,6 +11,8 @@ defmodule Explorer.Counters.AverageBlockTime do
alias Explorer.Repo
alias Timex.Duration
@refresh_period 30 * 60 * 1_000
@doc """
Starts a process to periodically update the counter of the token holders.
"""
@ -19,27 +21,51 @@ defmodule Explorer.Counters.AverageBlockTime do
GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
end
def average_block_time(block \\ nil) do
def average_block_time do
enabled? =
:explorer
|> Application.fetch_env!(__MODULE__)
|> Keyword.fetch!(:enabled)
if enabled? do
block = if block, do: {block.number, DateTime.to_unix(block.timestamp, :millisecond)}
GenServer.call(__MODULE__, {:average_block_time, block})
GenServer.call(__MODULE__, :average_block_time)
else
{:error, :disabled}
end
end
def refresh do
GenServer.call(__MODULE__, :refresh_timestamps)
end
## Server
@impl true
def init(_) do
Process.send_after(self(), :refresh_timestamps, @refresh_period)
{:ok, refresh_timestamps()}
end
@impl true
def handle_call(:average_block_time, _from, %{average: average} = state), do: {:reply, average, state}
@impl true
def handle_call(:refresh_timestamps, _, _) do
{:reply, :ok, refresh_timestamps()}
end
@impl true
def handle_info(:refresh_timestamps, _) do
Process.send_after(self(), :refresh_timestamps, @refresh_period)
{:noreply, refresh_timestamps()}
end
defp refresh_timestamps do
timestamps_query =
from(block in Block,
limit: 100,
offset: 1,
offset: 0,
order_by: [desc: block.number],
select: {block.number, block.timestamp}
)
@ -51,30 +77,7 @@ defmodule Explorer.Counters.AverageBlockTime do
{number, DateTime.to_unix(timestamp, :millisecond)}
end)
{:ok, %{timestamps: timestamps, average: average_distance(timestamps)}}
end
@impl true
def handle_call({:average_block_time, nil}, _from, %{average: average} = state), do: {:reply, average, state}
def handle_call({:average_block_time, block}, _from, state) do
state = add_block(state, block)
{:reply, state.average, state}
end
# This is pretty naive, but we'll only ever be sorting 100 dates so I don't think
# complex logic is really necessary here.
defp add_block(%{timestamps: timestamps} = state, {new_number, _} = block) do
if Enum.any?(timestamps, fn {number, _} -> number == new_number end) do
state
else
timestamps =
[block | timestamps]
|> Enum.sort_by(fn {number, _} -> number end, &Kernel.>/2)
|> Enum.take(100)
%{state | timestamps: timestamps, average: average_distance(timestamps)}
end
%{timestamps: timestamps, average: average_distance(timestamps)}
end
defp average_distance([]), do: Duration.from_milliseconds(0)

@ -5,8 +5,6 @@ defmodule Explorer.Counters.AverageBlockTimeTest do
alias Explorer.Counters.AverageBlockTime
defp block(number, last, duration), do: %{number: number, timestamp: Timex.shift(last, seconds: duration)}
setup do
start_supervised!(AverageBlockTime)
Application.put_env(:explorer, AverageBlockTime, enabled: true)
@ -26,41 +24,5 @@ defmodule Explorer.Counters.AverageBlockTimeTest do
test "without blocks duration is 0" do
assert AverageBlockTime.average_block_time() == Timex.Duration.parse!("PT0S")
end
test "with only one block, the duration is 0" do
now = Timex.now()
block = block(0, now, 0)
assert AverageBlockTime.average_block_time(block) == Timex.Duration.parse!("PT0S")
end
test "once there are two blocks, the duration is the average distance between them all" do
now = Timex.now()
block0 = block(0, now, 0)
block1 = block(1, now, 2)
block2 = block(2, now, 6)
AverageBlockTime.average_block_time(block0)
assert AverageBlockTime.average_block_time(block1) == Timex.Duration.parse!("PT2S")
assert AverageBlockTime.average_block_time(block2) == Timex.Duration.parse!("PT3S")
end
test "only the last 100 blocks are considered" do
now = Timex.now()
block0 = block(0, now, 0)
block1 = block(1, now, 2000)
AverageBlockTime.average_block_time(block0)
AverageBlockTime.average_block_time(block1)
for i <- 1..100 do
block = block(i + 1, now, 2000 + i)
AverageBlockTime.average_block_time(block)
end
assert AverageBlockTime.average_block_time() == Timex.Duration.parse!("PT1S")
end
end
end

@ -5,8 +5,8 @@ use Mix.Config
import Bitwise
block_transformers = %{
"clique" => Indexer.Block.Transform.Clique,
"base" => Indexer.Block.Transform.Base
"clique" => Indexer.Transform.Blocks.Clique,
"base" => Indexer.Transform.Blocks.Base
}
# Compile time environment variable access requires recompilation.
@ -36,8 +36,8 @@ config :indexer,
memory_limit: 1 <<< 30,
first_block: System.get_env("FIRST_BLOCK") || 0
# config :indexer, Indexer.ReplacedTransaction.Supervisor, disabled?: true
# config :indexer, Indexer.Block.Reward.Supervisor, disabled?: true
# config :indexer, Indexer.Fetcher.ReplacedTransaction.Supervisor, disabled?: true
# config :indexer, Indexer.Fetcher.BlockReward.Supervisor, disabled?: true
config :indexer, Indexer.Tracer,
service: :indexer,

@ -5,10 +5,7 @@ defmodule Indexer.Application do
use Application
alias Indexer.{
Memory,
Shrinkable
}
alias Indexer.Memory
@impl Application
def start(_type, _args) do
@ -22,13 +19,13 @@ defmodule Indexer.Application do
children = [
{Memory.Monitor, [memory_monitor_options, [name: memory_monitor_name]]},
{Shrinkable.Supervisor, [%{memory_monitor: memory_monitor_name}]}
{Indexer.Supervisor, [%{memory_monitor: memory_monitor_name}]}
]
opts = [
# If the `Memory.Monitor` dies, it needs all the `Shrinkable`s to re-register, so restart them.
strategy: :rest_for_one,
name: Indexer.Supervisor
name: Indexer.Application
]
Supervisor.start_link(children, opts)

@ -11,16 +11,19 @@ defmodule Indexer.Block.Catchup.Fetcher do
only: [
async_import_block_rewards: 1,
async_import_coin_balances: 2,
async_import_created_contract_codes: 1,
async_import_internal_transactions: 2,
async_import_replaced_transactions: 1,
async_import_tokens: 1,
async_import_token_balances: 1,
async_import_uncles: 1,
fetch_and_import_range: 2,
async_import_replaced_transactions: 1
fetch_and_import_range: 2
]
alias Ecto.Changeset
alias Explorer.Chain
alias Explorer.Chain.{Hash, Transaction}
alias Indexer.{Block, Code, InternalTransaction, Sequence, TokenBalance, Tracer}
alias Indexer.{Block, Tracer}
alias Indexer.Block.Catchup.Sequence
alias Indexer.Memory.Shrinkable
@behaviour Block.Fetcher
@ -31,7 +34,6 @@ defmodule Indexer.Block.Catchup.Fetcher do
@blocks_batch_size 10
@blocks_concurrency 10
@sequence_name :block_catchup_sequencer
@geth_block_limit 128
defstruct blocks_batch_size: @blocks_batch_size,
blocks_concurrency: @blocks_concurrency,
@ -157,60 +159,6 @@ defmodule Indexer.Block.Catchup.Fetcher do
async_import_replaced_transactions(imported)
end
defp async_import_created_contract_codes(%{transactions: transactions}) do
transactions
|> Enum.flat_map(fn
%Transaction{
block_number: block_number,
hash: hash,
created_contract_address_hash: %Hash{} = created_contract_address_hash,
created_contract_code_indexed_at: nil,
internal_transactions_indexed_at: nil
} ->
[%{block_number: block_number, hash: hash, created_contract_address_hash: created_contract_address_hash}]
%Transaction{internal_transactions_indexed_at: %DateTime{}} ->
[]
%Transaction{created_contract_address_hash: nil} ->
[]
end)
|> Code.Fetcher.async_fetch(10_000)
end
defp async_import_created_contract_codes(_), do: :ok
defp async_import_internal_transactions(%{blocks: blocks}, EthereumJSONRPC.Parity) do
blocks
|> Enum.map(fn %Chain.Block{number: block_number} -> %{number: block_number} end)
|> InternalTransaction.Fetcher.async_block_fetch(10_000)
end
defp async_import_internal_transactions(%{transactions: transactions}, EthereumJSONRPC.Geth) do
{_, max_block_number} = Chain.fetch_min_and_max_block_numbers()
transactions
|> Enum.flat_map(fn
%Transaction{block_number: block_number, index: index, hash: hash, internal_transactions_indexed_at: nil} ->
[%{block_number: block_number, index: index, hash: hash}]
%Transaction{internal_transactions_indexed_at: %DateTime{}} ->
[]
end)
|> Enum.filter(fn %{block_number: block_number} ->
max_block_number - block_number < @geth_block_limit
end)
|> InternalTransaction.Fetcher.async_fetch(10_000)
end
defp async_import_internal_transactions(_, _), do: :ok
defp async_import_token_balances(%{address_token_balances: token_balances}) do
TokenBalance.Fetcher.async_fetch(token_balances)
end
defp async_import_token_balances(_), do: :ok
defp stream_fetch_and_import(%__MODULE__{blocks_concurrency: blocks_concurrency} = state, sequence)
when is_pid(sequence) do
sequence

@ -1,4 +1,4 @@
defmodule Indexer.Sequence do
defmodule Indexer.Block.Catchup.Sequence do
@moduledoc false
use GenServer
@ -71,11 +71,11 @@ defmodule Indexer.Sequence do
Infinite sequence
Indexer.Sequence.start_link(first: 100, step: 10)
Indexer.Block.Catchup.Sequence.start_link(first: 100, step: 10)
Finite sequence
Indexer.Sequence.start_link(ranges: [100..0])
Indexer.Block.Catchup.Sequence.start_link(ranges: [100..0])
"""
@spec start_link(options(), Keyword.t()) :: GenServer.on_start()

@ -10,11 +10,32 @@ defmodule Indexer.Block.Fetcher do
import EthereumJSONRPC, only: [quantity_to_integer: 1]
alias EthereumJSONRPC.{Blocks, FetchedBeneficiaries}
alias Explorer.Chain
alias Explorer.Chain.{Address, Block, Hash, Import, Transaction}
alias Indexer.{AddressExtraction, CoinBalance, MintTransfer, ReplacedTransaction, Token, TokenTransfers, Tracer}
alias Indexer.Address.{CoinBalances, TokenBalances}
alias Indexer.Block.Fetcher.Receipts
alias Indexer.Block.{Reward, Transform}
alias Indexer.Fetcher.{
BlockReward,
CoinBalance,
ContractCode,
InternalTransaction,
ReplacedTransaction,
Token,
TokenBalance,
UncleBlock
}
alias Indexer.Tracer
alias Indexer.Transform.{
AddressCoinBalances,
Addresses,
AddressTokenBalances,
MintTransfers,
TokenTransfers
}
alias Indexer.Transform.Blocks, as: TransformBlocks
@type address_hash_to_fetched_balance_block_number :: %{String.t() => Block.block_number()}
@ -46,6 +67,7 @@ defmodule Indexer.Block.Fetcher do
@receipts_batch_size 250
@receipts_concurrency 10
@geth_block_limit 128
@doc false
def default_receipts_batch_size, do: @receipts_batch_size
@ -102,16 +124,16 @@ defmodule Indexer.Block.Fetcher do
block_second_degree_relations_params: block_second_degree_relations_params,
errors: blocks_errors
}}} <- {:blocks, EthereumJSONRPC.fetch_blocks_by_range(range, json_rpc_named_arguments)},
blocks = Transform.transform_blocks(blocks_params),
blocks = TransformBlocks.transform_blocks(blocks_params),
{:receipts, {:ok, receipt_params}} <- {:receipts, Receipts.fetch(state, transactions_params_without_receipts)},
%{logs: logs, receipts: receipts} = receipt_params,
transactions_with_receipts = Receipts.put(transactions_params_without_receipts, receipts),
%{token_transfers: token_transfers, tokens: tokens} = TokenTransfers.parse(logs),
%{mint_transfers: mint_transfers} = MintTransfer.parse(logs),
%{mint_transfers: mint_transfers} = MintTransfers.parse(logs),
%FetchedBeneficiaries{params_set: beneficiary_params_set, errors: beneficiaries_errors} =
fetch_beneficiaries(blocks, json_rpc_named_arguments),
addresses =
AddressExtraction.extract_addresses(%{
Addresses.extract_addresses(%{
block_reward_contract_beneficiaries: MapSet.to_list(beneficiary_params_set),
blocks: blocks,
logs: logs,
@ -126,12 +148,12 @@ defmodule Indexer.Block.Fetcher do
logs_params: logs,
transactions_params: transactions_with_receipts
}
|> CoinBalances.params_set(),
|> AddressCoinBalances.params_set(),
beneficiaries_with_gas_payment <-
beneficiary_params_set
|> add_gas_payments(transactions_with_receipts)
|> Reward.Fetcher.reduce_uncle_rewards(),
address_token_balances = TokenBalances.params_set(%{token_transfers_params: token_transfers}),
|> BlockReward.reduce_uncle_rewards(),
address_token_balances = AddressTokenBalances.params_set(%{token_transfers_params: token_transfers}),
{:ok, inserted} <-
__MODULE__.import(
state,
@ -180,7 +202,7 @@ defmodule Indexer.Block.Fetcher do
def async_import_block_rewards(errors) when is_list(errors) do
errors
|> block_reward_errors_to_block_numbers()
|> Indexer.Block.Reward.Fetcher.async_fetch()
|> BlockReward.async_fetch()
end
def async_import_coin_balances(%{addresses: addresses}, %{
@ -191,23 +213,77 @@ defmodule Indexer.Block.Fetcher do
block_number = Map.fetch!(address_hash_to_block_number, to_string(address_hash))
%{address_hash: address_hash, block_number: block_number}
end)
|> CoinBalance.Fetcher.async_fetch_balances()
|> CoinBalance.async_fetch_balances()
end
def async_import_coin_balances(_, _), do: :ok
def async_import_created_contract_codes(%{transactions: transactions}) do
transactions
|> Enum.flat_map(fn
%Transaction{
block_number: block_number,
hash: hash,
created_contract_address_hash: %Hash{} = created_contract_address_hash,
created_contract_code_indexed_at: nil,
internal_transactions_indexed_at: nil
} ->
[%{block_number: block_number, hash: hash, created_contract_address_hash: created_contract_address_hash}]
%Transaction{internal_transactions_indexed_at: %DateTime{}} ->
[]
%Transaction{created_contract_address_hash: nil} ->
[]
end)
|> ContractCode.async_fetch(10_000)
end
def async_import_created_contract_codes(_), do: :ok
def async_import_internal_transactions(%{blocks: blocks}, EthereumJSONRPC.Parity) do
blocks
|> Enum.map(fn %Block{number: block_number} -> %{number: block_number} end)
|> InternalTransaction.async_block_fetch(10_000)
end
def async_import_internal_transactions(%{transactions: transactions}, EthereumJSONRPC.Geth) do
{_, max_block_number} = Chain.fetch_min_and_max_block_numbers()
transactions
|> Enum.flat_map(fn
%Transaction{block_number: block_number, index: index, hash: hash, internal_transactions_indexed_at: nil} ->
[%{block_number: block_number, index: index, hash: hash}]
%Transaction{internal_transactions_indexed_at: %DateTime{}} ->
[]
end)
|> Enum.filter(fn %{block_number: block_number} ->
max_block_number - block_number < @geth_block_limit
end)
|> InternalTransaction.async_fetch(10_000)
end
def async_import_internal_transactions(_, _), do: :ok
def async_import_tokens(%{tokens: tokens}) do
tokens
|> Enum.map(& &1.contract_address_hash)
|> Token.Fetcher.async_fetch()
|> Token.async_fetch()
end
def async_import_tokens(_), do: :ok
def async_import_token_balances(%{address_token_balances: token_balances}) do
TokenBalance.async_fetch(token_balances)
end
def async_import_token_balances(_), do: :ok
def async_import_uncles(%{block_second_degree_relations: block_second_degree_relations}) do
block_second_degree_relations
|> Enum.map(& &1.uncle_hash)
|> Indexer.Block.Uncle.Fetcher.async_fetch_blocks()
|> UncleBlock.async_fetch_blocks()
end
def async_import_uncles(_), do: :ok
@ -221,7 +297,7 @@ defmodule Indexer.Block.Fetcher do
%Transaction{block_hash: nil} ->
[]
end)
|> ReplacedTransaction.Fetcher.async_fetch(10_000)
|> ReplacedTransaction.async_fetch(10_000)
end
def async_import_replaced_transactions(_), do: :ok

@ -14,20 +14,22 @@ defmodule Indexer.Block.Realtime.Fetcher do
import Indexer.Block.Fetcher,
only: [
async_import_block_rewards: 1,
async_import_created_contract_codes: 1,
async_import_internal_transactions: 2,
async_import_replaced_transactions: 1,
async_import_tokens: 1,
async_import_token_balances: 1,
async_import_uncles: 1,
fetch_and_import_range: 2,
async_import_replaced_transactions: 1
fetch_and_import_range: 2
]
alias ABI.TypeDecoder
alias Ecto.Changeset
alias EthereumJSONRPC.{FetchedBalances, Subscription}
alias Explorer.Chain
alias Explorer.Chain.TokenTransfer
alias Explorer.Counters.AverageBlockTime
alias Indexer.{AddressExtraction, Block, TokenBalances, Tracer}
alias Indexer.{Block, Tracer}
alias Indexer.Block.Realtime.TaskSupervisor
alias Indexer.Transform.Addresses
alias Timex.Duration
@behaviour Block.Fetcher
@ -159,42 +161,21 @@ defmodule Indexer.Block.Realtime.Fetcher do
@impl Block.Fetcher
def import(
block_fetcher,
%Block.Fetcher{json_rpc_named_arguments: json_rpc_named_arguments} = block_fetcher,
%{
address_coin_balances: %{params: address_coin_balances_params},
address_hash_to_fetched_balance_block_number: address_hash_to_block_number,
address_token_balances: %{params: address_token_balances_params},
addresses: %{params: addresses_params},
blocks: %{params: blocks_params},
block_rewards: block_rewards,
transactions: %{params: transactions_params},
token_transfers: %{params: token_transfers_params}
block_rewards: block_rewards
} = options
) do
with {:internal_transactions,
{:ok,
%{
addresses_params: internal_transactions_addresses_params,
internal_transactions_params: internal_transactions_params,
internal_transactions_indexed_at_blocks_params: internal_transactions_indexed_at_blocks_params
}}} <-
{:internal_transactions,
internal_transactions(block_fetcher, %{
addresses_params: addresses_params,
blocks_params: blocks_params,
token_transfers_params: token_transfers_params,
transactions_params: transactions_params
})},
{:balances, {:ok, %{addresses_params: balances_addresses_params, balances_params: balances_params}}} <-
with {:balances, {:ok, %{addresses_params: balances_addresses_params, balances_params: balances_params}}} <-
{:balances,
balances(block_fetcher, %{
address_hash_to_block_number: address_hash_to_block_number,
addresses_params: internal_transactions_addresses_params,
addresses_params: addresses_params,
balances_params: address_coin_balances_params
})},
{:address_token_balances, {:ok, address_token_balances}} <-
{:address_token_balances, fetch_token_balances(address_token_balances_params)},
address_current_token_balances = TokenBalances.to_address_current_token_balances(address_token_balances),
{block_reward_errors, chain_import_block_rewards} = Map.pop(block_rewards, :errors),
chain_import_options =
options
@ -202,16 +183,14 @@ defmodule Indexer.Block.Realtime.Fetcher do
|> put_in([:addresses, :params], balances_addresses_params)
|> put_in([:blocks, :params, Access.all(), :consensus], true)
|> put_in([:block_rewards], chain_import_block_rewards)
|> put_in([Access.key(:address_coin_balances, %{}), :params], balances_params)
|> put_in([Access.key(:address_current_token_balances, %{}), :params], address_current_token_balances)
|> put_in([Access.key(:address_token_balances), :params], address_token_balances)
|> put_in([Access.key(:internal_transactions, %{}), :params], internal_transactions_params)
|> put_in([:internal_transactions_indexed_at_blocks], %{
params: internal_transactions_indexed_at_blocks_params,
with: :number_only_changeset
}),
|> put_in([Access.key(:address_coin_balances, %{}), :params], balances_params),
{:import, {:ok, imported} = ok} <- {:import, Chain.import(chain_import_options)} do
async_import_remaining_block_data(imported, %{block_rewards: %{errors: block_reward_errors}})
async_import_remaining_block_data(
imported,
%{block_rewards: %{errors: block_reward_errors}},
json_rpc_named_arguments
)
ok
end
end
@ -354,147 +333,20 @@ defmodule Indexer.Block.Realtime.Fetcher do
Enum.any?(changesets, &(Map.get(&1, :message) == "Unknown block number"))
end
defp async_import_remaining_block_data(imported, %{block_rewards: %{errors: block_reward_errors}}) do
defp async_import_remaining_block_data(
imported,
%{block_rewards: %{errors: block_reward_errors}},
json_rpc_named_arguments
) do
async_import_block_rewards(block_reward_errors)
async_import_created_contract_codes(imported)
async_import_internal_transactions(imported, Keyword.get(json_rpc_named_arguments, :variant))
async_import_tokens(imported)
async_import_token_balances(imported)
async_import_uncles(imported)
async_import_replaced_transactions(imported)
end
defp internal_transactions(
%Block.Fetcher{json_rpc_named_arguments: json_rpc_named_arguments},
%{
addresses_params: addresses_params,
blocks_params: blocks_params,
token_transfers_params: token_transfers_params,
transactions_params: transactions_params
}
) do
variant = Keyword.fetch!(json_rpc_named_arguments, :variant)
internal_transactions_indexed_at_blocks_params =
case variant do
EthereumJSONRPC.Parity -> blocks_params
_ -> []
end
variant
|> case do
EthereumJSONRPC.Parity ->
blocks_params
|> Enum.map(fn %{number: block_number} -> block_number end)
|> EthereumJSONRPC.fetch_block_internal_transactions(json_rpc_named_arguments)
_ ->
transactions_params
|> transactions_params_to_fetch_internal_transactions_params(token_transfers_params, json_rpc_named_arguments)
|> EthereumJSONRPC.fetch_internal_transactions(json_rpc_named_arguments)
end
|> case do
{:ok, internal_transactions_params} ->
merged_addresses_params =
%{internal_transactions: internal_transactions_params}
|> AddressExtraction.extract_addresses()
|> Kernel.++(addresses_params)
|> AddressExtraction.merge_addresses()
{:ok,
%{
addresses_params: merged_addresses_params,
internal_transactions_params: internal_transactions_params,
internal_transactions_indexed_at_blocks_params: internal_transactions_indexed_at_blocks_params
}}
:ignore ->
{:ok,
%{
addresses_params: addresses_params,
internal_transactions_params: [],
internal_transactions_indexed_at_blocks_params: []
}}
{:error, _reason} = error ->
error
end
end
defp transactions_params_to_fetch_internal_transactions_params(
transactions_params,
token_transfers_params,
json_rpc_named_arguments
) do
token_transfer_transaction_hash_set = MapSet.new(token_transfers_params, & &1.transaction_hash)
Enum.flat_map(
transactions_params,
&transaction_params_to_fetch_internal_transaction_params_list(
&1,
token_transfer_transaction_hash_set,
json_rpc_named_arguments
)
)
end
defp transaction_params_to_fetch_internal_transaction_params_list(
%{block_number: block_number, transaction_index: transaction_index, hash: hash} = transaction_params,
token_transfer_transaction_hash_set,
json_rpc_named_arguments
)
when is_integer(block_number) and is_integer(transaction_index) and is_binary(hash) do
token_transfer? = hash in token_transfer_transaction_hash_set
if fetch_internal_transactions?(transaction_params, token_transfer?, json_rpc_named_arguments) do
[%{block_number: block_number, transaction_index: transaction_index, hash_data: hash}]
else
[]
end
end
# 0xa9059cbb - signature of the transfer(address,uint256) function from the ERC-20 token specification.
# Although transaction input data can be faked we use this heuristics to filter simple token transfer internal transactions from indexing because they slow down realtime fetcher
defp fetch_internal_transactions?(
%{
status: :ok,
created_contract_address_hash: nil,
input: unquote(TokenTransfer.transfer_function_signature()) <> params,
value: 0
},
_,
_
) do
types = [:address, {:uint, 256}]
try do
[_address, _value] =
params
|> Base.decode16!(case: :mixed)
|> TypeDecoder.decode_raw(types)
false
rescue
_ -> true
end
end
defp fetch_internal_transactions?(
%{
status: :ok,
created_contract_address_hash: nil,
input: "0x",
to_address_hash: to_address_hash,
block_number: block_number
},
_,
json_rpc_named_arguments
) do
Chain.contract_address?(to_address_hash, block_number, json_rpc_named_arguments)
end
# Token transfers not transferred during contract creation don't need internal transactions as the token transfers
# derive completely from the logs.
defp fetch_internal_transactions?(%{status: :ok, created_contract_address_hash: nil}, true, _), do: false
defp fetch_internal_transactions?(_, _, _), do: true
defp balances(
%Block.Fetcher{json_rpc_named_arguments: json_rpc_named_arguments},
%{addresses_params: addresses_params} = options
@ -505,9 +357,9 @@ defmodule Indexer.Block.Realtime.Fetcher do
{:ok, %FetchedBalances{params_list: params_list, errors: []}} ->
merged_addresses_params =
%{address_coin_balances: params_list}
|> AddressExtraction.extract_addresses()
|> Addresses.extract_addresses()
|> Kernel.++(addresses_params)
|> AddressExtraction.merge_addresses()
|> Addresses.merge_addresses()
value_fetched_at = DateTime.utc_now()
@ -557,10 +409,4 @@ defmodule Indexer.Block.Realtime.Fetcher do
%{hash_data: address_hash, block_quantity: integer_to_quantity(block_number)}
end)
end
defp fetch_token_balances(address_token_balances_params) do
address_token_balances_params
|> MapSet.to_list()
|> TokenBalances.fetch_token_balances_from_blockchain()
end
end

@ -1,46 +0,0 @@
defmodule Indexer.Block.Reward.Supervisor do
@moduledoc """
Supervises `Indexer.Block.Reward.Fetcher` and its batch tasks through `Indexer.Block.Reward.TaskSupervisor`
"""
use Supervisor
alias Indexer.Block.Reward.Fetcher
def child_spec([init_arguments]) do
child_spec([init_arguments, []])
end
def child_spec([_init_arguments, _gen_server_options] = start_link_arguments) do
default = %{
id: __MODULE__,
start: {__MODULE__, :start_link, start_link_arguments},
type: :supervisor
}
Supervisor.child_spec(default, [])
end
def start_link(arguments, gen_server_options \\ []) do
if disabled?() do
:ignore
else
Supervisor.start_link(__MODULE__, arguments, Keyword.put_new(gen_server_options, :name, __MODULE__))
end
end
def disabled?() do
Application.get_env(:indexer, __MODULE__, [])[:disabled?] == true
end
@impl Supervisor
def init(fetcher_arguments) do
Supervisor.init(
[
{Task.Supervisor, name: Indexer.Block.Reward.TaskSupervisor},
{Fetcher, [fetcher_arguments, [name: Fetcher]]}
],
strategy: :one_for_one
)
end
end

@ -1,78 +0,0 @@
defmodule Indexer.Block.Supervisor do
@moduledoc """
Supervises `Indexer.Block.Catchup.Supervisor` and `Indexer.Block.Realtime.Supervisor`.
"""
alias Indexer.Block
alias Indexer.Block.{Catchup, Realtime, Reward, Uncle}
alias Indexer.Temporary.{AddressesWithoutCode, FailedCreatedAddresses}
use Supervisor
def start_link([arguments, gen_server_options]) do
Supervisor.start_link(__MODULE__, arguments, gen_server_options)
end
@impl Supervisor
def init(
%{
block_interval: block_interval,
json_rpc_named_arguments: json_rpc_named_arguments,
subscribe_named_arguments: subscribe_named_arguments,
realtime_overrides: realtime_overrides
} = named_arguments
) do
block_fetcher =
named_arguments
|> Map.drop(~w(block_interval memory_monitor subscribe_named_arguments realtime_overrides)a)
|> Block.Fetcher.new()
fixing_realtime_fetcher = %Block.Fetcher{
broadcast: false,
callback_module: Realtime.Fetcher,
json_rpc_named_arguments: json_rpc_named_arguments
}
realtime_block_fetcher =
named_arguments
|> Map.drop(~w(block_interval memory_monitor subscribe_named_arguments realtime_overrides)a)
|> Map.merge(Enum.into(realtime_overrides, %{}))
|> Block.Fetcher.new()
realtime_subscribe_named_arguments = realtime_overrides[:subscribe_named_arguments] || subscribe_named_arguments
memory_monitor = Map.get(named_arguments, :memory_monitor)
Supervisor.init(
[
{Catchup.Supervisor,
[
%{block_fetcher: block_fetcher, block_interval: block_interval, memory_monitor: memory_monitor},
[name: Catchup.Supervisor]
]},
{Realtime.Supervisor,
[
%{block_fetcher: realtime_block_fetcher, subscribe_named_arguments: realtime_subscribe_named_arguments},
[name: Realtime.Supervisor]
]},
{Uncle.Supervisor, [[block_fetcher: block_fetcher, memory_monitor: memory_monitor], [name: Uncle.Supervisor]]},
{Reward.Supervisor,
[
[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor],
[name: Reward.Supervisor]
]},
{FailedCreatedAddresses.Supervisor,
[
json_rpc_named_arguments,
[name: FailedCreatedAddresses.Supervisor]
]},
{AddressesWithoutCode.Supervisor,
[
fixing_realtime_fetcher,
[name: AddressesWithoutCode.Supervisor]
]}
],
strategy: :one_for_one
)
end
end

@ -1,21 +0,0 @@
defmodule Indexer.Block.Transform do
@moduledoc """
Protocol for transforming blocks.
"""
@type block :: map()
@doc """
Transforms a block.
"""
@callback transform(block :: block()) :: block()
@doc """
Runs a list of blocks through the configured block transformer.
"""
def transform_blocks(blocks) when is_list(blocks) do
transformer = Application.get_env(:indexer, :block_transformer)
Enum.map(blocks, &transformer.transform/1)
end
end

@ -1,38 +0,0 @@
defmodule Indexer.Block.Uncle.Supervisor do
@moduledoc """
Supervises `Indexer.Block.Uncle.Fetcher`.
"""
use Supervisor
alias Indexer.Block.Uncle.Fetcher
def child_spec([init_arguments]) do
child_spec([init_arguments, []])
end
def child_spec([_init_arguments, _gen_server_options] = start_link_arguments) do
default = %{
id: __MODULE__,
start: {__MODULE__, :start_link, start_link_arguments},
type: :supervisor
}
Supervisor.child_spec(default, [])
end
def start_link(arguments, gen_server_options \\ []) do
Supervisor.start_link(__MODULE__, arguments, gen_server_options)
end
@impl Supervisor
def init(fetcher_arguments) do
Supervisor.init(
[
{Task.Supervisor, name: Indexer.Block.Uncle.TaskSupervisor},
{Fetcher, [fetcher_arguments, [name: Fetcher]]}
],
strategy: :rest_for_one
)
end
end

@ -1,39 +0,0 @@
defmodule Indexer.Code.Supervisor do
@moduledoc """
Supervises `Indexer.Code.Fetcher` and its batch tasks through
`Indexer.Code.TaskSupervisor`.
"""
use Supervisor
alias Indexer.Code.Fetcher
def child_spec([init_arguments]) do
child_spec([init_arguments, []])
end
def child_spec([_init_arguments, _gen_server_options] = start_link_arguments) do
default = %{
id: __MODULE__,
start: {__MODULE__, :start_link, start_link_arguments},
type: :supervisor
}
Supervisor.child_spec(default, [])
end
def start_link(arguments, gen_server_options \\ []) do
Supervisor.start_link(__MODULE__, arguments, Keyword.put_new(gen_server_options, :name, __MODULE__))
end
@impl Supervisor
def init(fetcher_arguments) do
Supervisor.init(
[
{Task.Supervisor, name: Indexer.Code.TaskSupervisor},
{Fetcher, [fetcher_arguments, [name: Fetcher]]}
],
strategy: :one_for_one
)
end
end

@ -1,39 +0,0 @@
defmodule Indexer.CoinBalance.Supervisor do
@moduledoc """
Supervises `Indexer.CoinBalance.Fetcher` and its batch tasks through `Indexer.CoinBalance.TaskSupervisor`
"""
use Supervisor
alias Indexer.CoinBalance.{Fetcher, OnDemandFetcher}
def child_spec([init_arguments]) do
child_spec([init_arguments, []])
end
def child_spec([_init_arguments, _gen_server_options] = start_link_arguments) do
default = %{
id: __MODULE__,
start: {__MODULE__, :start_link, start_link_arguments},
type: :supervisor
}
Supervisor.child_spec(default, [])
end
def start_link(arguments, gen_server_options \\ []) do
Supervisor.start_link(__MODULE__, arguments, Keyword.put_new(gen_server_options, :name, __MODULE__))
end
@impl Supervisor
def init(fetcher_arguments) do
Supervisor.init(
[
{Task.Supervisor, name: Indexer.CoinBalance.TaskSupervisor},
{Fetcher, [fetcher_arguments, [name: Fetcher]]},
{OnDemandFetcher, [fetcher_arguments[:json_rpc_named_arguments], [name: OnDemandFetcher]]}
],
strategy: :one_for_one
)
end
end

@ -0,0 +1,77 @@
defmodule Indexer.Fetcher do
@moduledoc """
General fetcher infrastructure.
"""
alias Macro.Env
defmacro __using__(opts \\ []) do
quote do
require Indexer.Fetcher
Indexer.Fetcher.defsupervisor(unquote(opts))
end
end
# credo:disable-for-next-line Credo.Check.Refactor.CyclomaticComplexity
defmacro defsupervisor(opts \\ []) do
quote location: :keep do
opts = unquote(opts)
strategy = Keyword.get(opts, :strategy, :one_for_one)
fetcher = __MODULE__
supervisor = Keyword.get(opts, :supervisor, Module.concat(fetcher, Supervisor))
task_supervisor = Keyword.get(opts, :task_supervisor, Module.concat(fetcher, TaskSupervisor))
Module.create(
supervisor,
quote bind_quoted: [strategy: strategy, fetcher: fetcher, task_supervisor: task_supervisor] do
use Supervisor
def child_spec([]), do: child_spec([[], []])
def child_spec([init_arguments]), do: child_spec([init_arguments, []])
def child_spec([_init_arguments, _gen_server_options] = start_link_arguments) do
default = %{
id: __MODULE__,
start: {__MODULE__, :start_link, start_link_arguments},
restart: :transient,
type: :supervisor
}
Supervisor.child_spec(default, [])
end
def start_link(arguments, gen_server_options \\ []) do
if disabled?() do
:ignore
else
Supervisor.start_link(__MODULE__, arguments, Keyword.put_new(gen_server_options, :name, __MODULE__))
end
end
def disabled?() do
Application.get_env(:indexer, __MODULE__, [])[:disabled?] == true
end
@impl Supervisor
def init(fetcher_arguments) do
children = [
{Task.Supervisor, name: unquote(task_supervisor)},
{unquote(fetcher), [put_supervisor_when_is_list(fetcher_arguments), [name: unquote(fetcher)]]}
]
Supervisor.init(children, strategy: unquote(strategy))
end
defp put_supervisor_when_is_list(arguments) when is_list(arguments) do
Keyword.put(arguments, :supervisor, self())
end
defp put_supervisor_when_is_list(arguments), do: arguments
end,
Env.location(__ENV__)
)
end
end
end

@ -1,4 +1,4 @@
defmodule Indexer.Block.Reward.Fetcher do
defmodule Indexer.Fetcher.BlockReward do
@moduledoc """
Fetches `t:Explorer.Chain.Block.Reward.t/0` for a given `t:Explorer.Chain.Block.block_number/0`.
@ -6,6 +6,7 @@ defmodule Indexer.Block.Reward.Fetcher do
retrieved from the database and compared against that returned from `EthereumJSONRPC.`
"""
use Indexer.Fetcher
use Spandex.Decorators
require Logger
@ -16,9 +17,10 @@ defmodule Indexer.Block.Reward.Fetcher do
alias EthereumJSONRPC.FetchedBeneficiaries
alias Explorer.Chain
alias Explorer.Chain.{Block, Wei}
alias Indexer.Address.CoinBalances
alias Indexer.{AddressExtraction, BufferedTask, CoinBalance, Tracer}
alias Indexer.Block.Reward.Supervisor, as: BlockRewardSupervisor
alias Indexer.{BufferedTask, Tracer}
alias Indexer.Fetcher.BlockReward.Supervisor, as: BlockRewardSupervisor
alias Indexer.Fetcher.CoinBalance
alias Indexer.Transform.{AddressCoinBalances, Addresses}
@behaviour BufferedTask
@ -26,7 +28,7 @@ defmodule Indexer.Block.Reward.Fetcher do
flush_interval: :timer.seconds(3),
max_batch_size: 10,
max_concurrency: 4,
task_supervisor: Indexer.Block.Reward.TaskSupervisor,
task_supervisor: Indexer.Fetcher.BlockReward.TaskSupervisor,
metadata: [fetcher: :block_reward]
]
@ -72,7 +74,7 @@ defmodule Indexer.Block.Reward.Fetcher do
end
@impl BufferedTask
@decorate trace(name: "fetch", resource: "Indexer.Block.Reward.Fetcher.run/2", service: :indexer, tracer: Tracer)
@decorate trace(name: "fetch", resource: "Indexer.Fetcher.BlockReward.run/2", service: :indexer, tracer: Tracer)
def run(entries, json_rpc_named_arguments) do
hash_string_by_number =
entries
@ -129,7 +131,7 @@ defmodule Indexer.Block.Reward.Fetcher do
|> import_block_reward_params()
|> case do
{:ok, %{address_coin_balances: address_coin_balances}} ->
CoinBalance.Fetcher.async_fetch_balances(address_coin_balances)
CoinBalance.async_fetch_balances(address_coin_balances)
retry_errors(errors)
@ -237,8 +239,8 @@ defmodule Indexer.Block.Reward.Fetcher do
end
defp import_block_reward_params(block_rewards_params) when is_list(block_rewards_params) do
addresses_params = AddressExtraction.extract_addresses(%{block_reward_contract_beneficiaries: block_rewards_params})
address_coin_balances_params_set = CoinBalances.params_set(%{beneficiary_params: block_rewards_params})
addresses_params = Addresses.extract_addresses(%{block_reward_contract_beneficiaries: block_rewards_params})
address_coin_balances_params_set = AddressCoinBalances.params_set(%{beneficiary_params: block_rewards_params})
Chain.import(%{
addresses: %{params: addresses_params},

@ -1,9 +1,10 @@
defmodule Indexer.CoinBalance.Fetcher do
defmodule Indexer.Fetcher.CoinBalance do
@moduledoc """
Fetches `t:Explorer.Chain.Address.CoinBalance.t/0` and updates `t:Explorer.Chain.Address.t/0` `fetched_coin_balance` and
`fetched_coin_balance_block_number` to value at max `t:Explorer.Chain.Address.CoinBalance.t/0` `block_number` for the given `t:Explorer.Chain.Address.t/` `hash`.
"""
use Indexer.Fetcher
use Spandex.Decorators
require Logger
@ -21,7 +22,7 @@ defmodule Indexer.CoinBalance.Fetcher do
flush_interval: :timer.seconds(3),
max_batch_size: 500,
max_concurrency: 4,
task_supervisor: Indexer.CoinBalance.TaskSupervisor,
task_supervisor: Indexer.Fetcher.CoinBalance.TaskSupervisor,
metadata: [fetcher: :coin_balance]
]
@ -69,7 +70,7 @@ defmodule Indexer.CoinBalance.Fetcher do
end
@impl BufferedTask
@decorate trace(name: "fetch", resource: "Indexer.CoinBalance.Fetcher.run/2", service: :indexer, tracer: Tracer)
@decorate trace(name: "fetch", resource: "Indexer.Fetcher.CoinBalance.run/2", service: :indexer, tracer: Tracer)
def run(entries, json_rpc_named_arguments) do
# the same address may be used more than once in the same block, but we only want one `Balance` for a given
# `{address, block}`, so take unique params only

@ -1,4 +1,4 @@
defmodule Indexer.CoinBalance.OnDemandFetcher do
defmodule Indexer.Fetcher.CoinBalanceOnDemand do
@moduledoc """
Ensures that we have a reasonably up to date coin balance for a given address.
@ -10,6 +10,7 @@ defmodule Indexer.CoinBalance.OnDemandFetcher do
@latest_balance_stale_threshold :timer.hours(24)
use GenServer
use Indexer.Fetcher
import Ecto.Query, only: [from: 2]
import EthereumJSONRPC, only: [integer_to_quantity: 1]
@ -19,7 +20,7 @@ defmodule Indexer.CoinBalance.OnDemandFetcher do
alias Explorer.Chain.{Address, BlockNumberCache}
alias Explorer.Chain.Address.CoinBalance
alias Explorer.Counters.AverageBlockTime
alias Indexer.CoinBalance.Fetcher
alias Indexer.Fetcher.CoinBalance, as: CoinBalanceFetcher
alias Timex.Duration
@type block_number :: integer
@ -42,7 +43,7 @@ defmodule Indexer.CoinBalance.OnDemandFetcher do
latest_block_number = latest_block_number()
case stale_balance_window(latest_block_number) do
{:error, :no_average_block_time} ->
{:error, _} ->
:current
stale_balance_window ->
@ -134,7 +135,7 @@ defmodule Indexer.CoinBalance.OnDemandFetcher do
:ok
{:ok, %{params_list: params_list}} ->
address_params = Fetcher.balances_params_to_address_params(params_list)
address_params = CoinBalanceFetcher.balances_params_to_address_params(params_list)
Chain.import(%{
addresses: %{params: address_params, with: :balance_changeset},
@ -153,7 +154,7 @@ defmodule Indexer.CoinBalance.OnDemandFetcher do
end
defp do_import(%FetchedBalances{} = fetched_balances) do
case Fetcher.import_fetched_balances(fetched_balances, :on_demand) do
case CoinBalanceFetcher.import_fetched_balances(fetched_balances, :on_demand) do
{:ok, %{addresses: [address]}} -> {:ok, address}
_ -> :error
end
@ -174,7 +175,11 @@ defmodule Indexer.CoinBalance.OnDemandFetcher do
|> Duration.to_milliseconds()
|> round()
if average_block_time == 0 do
{:error, :empty_database}
else
block_number - div(@latest_balance_stale_threshold, average_block_time)
end
end
end
end

@ -1,8 +1,9 @@
defmodule Indexer.Code.Fetcher do
defmodule Indexer.Fetcher.ContractCode do
@moduledoc """
Fetches `contract_code` `t:Explorer.Chain.Address.t/0`.
"""
use Indexer.Fetcher
use Spandex.Decorators
require Logger
@ -11,7 +12,8 @@ defmodule Indexer.Code.Fetcher do
alias Explorer.Chain
alias Explorer.Chain.{Block, Hash}
alias Indexer.{AddressExtraction, BufferedTask, Tracer}
alias Indexer.{BufferedTask, Tracer}
alias Indexer.Transform.Addresses
@behaviour BufferedTask
@ -21,7 +23,7 @@ defmodule Indexer.Code.Fetcher do
flush_interval: :timer.seconds(3),
max_concurrency: @max_concurrency,
max_batch_size: @max_batch_size,
task_supervisor: Indexer.Code.TaskSupervisor,
task_supervisor: Indexer.Fetcher.ContractCode.TaskSupervisor,
metadata: [fetcher: :code]
]
@ -85,7 +87,7 @@ defmodule Indexer.Code.Fetcher do
@impl BufferedTask
@decorate trace(
name: "fetch",
resource: "Indexer.Code.Fetcher.run/2",
resource: "Indexer.Fetcher.ContractCode.run/2",
service: :indexer,
tracer: Tracer
)
@ -97,7 +99,7 @@ defmodule Indexer.Code.Fetcher do
|> EthereumJSONRPC.fetch_codes(json_rpc_named_arguments)
|> case do
{:ok, create_address_codes} ->
addresses_params = AddressExtraction.extract_addresses(%{codes: create_address_codes.params_list})
addresses_params = Addresses.extract_addresses(%{codes: create_address_codes.params_list})
import_with_balances(addresses_params, entries, json_rpc_named_arguments)
@ -118,7 +120,7 @@ defmodule Indexer.Code.Fetcher do
{:ok, fetched_balances} ->
balance_addresses_params = balances_params_to_address_params(fetched_balances.params_list)
merged_addresses_params = AddressExtraction.merge_addresses(addresses_params ++ balance_addresses_params)
merged_addresses_params = Addresses.merge_addresses(addresses_params ++ balance_addresses_params)
case Chain.import(%{
addresses: %{params: merged_addresses_params},

@ -1,10 +1,11 @@
defmodule Indexer.InternalTransaction.Fetcher do
defmodule Indexer.Fetcher.InternalTransaction do
@moduledoc """
Fetches and indexes `t:Explorer.Chain.InternalTransaction.t/0`.
See `async_fetch/1` for details on configuring limits.
"""
use Indexer.Fetcher
use Spandex.Decorators
require Logger
@ -13,7 +14,8 @@ defmodule Indexer.InternalTransaction.Fetcher do
alias Explorer.Chain
alias Explorer.Chain.{Block, Hash}
alias Indexer.{AddressExtraction, BufferedTask, Tracer}
alias Indexer.{BufferedTask, Tracer}
alias Indexer.Transform.Addresses
@behaviour BufferedTask
@ -23,7 +25,7 @@ defmodule Indexer.InternalTransaction.Fetcher do
flush_interval: :timer.seconds(3),
max_concurrency: @max_concurrency,
max_batch_size: @max_batch_size,
task_supervisor: Indexer.InternalTransaction.TaskSupervisor,
task_supervisor: Indexer.Fetcher.InternalTransaction.TaskSupervisor,
metadata: [fetcher: :internal_transaction]
]
@ -141,7 +143,7 @@ defmodule Indexer.InternalTransaction.Fetcher do
@impl BufferedTask
@decorate trace(
name: "fetch",
resource: "Indexer.InternalTransaction.Fetcher.run/2",
resource: "Indexer.Fetcher.InternalTransaction.run/2",
service: :indexer,
tracer: Tracer
)
@ -177,7 +179,7 @@ defmodule Indexer.InternalTransaction.Fetcher do
internal_transactions_params_without_failed_creations = remove_failed_creations(internal_transactions_params)
addresses_params =
AddressExtraction.extract_addresses(%{
Addresses.extract_addresses(%{
internal_transactions: internal_transactions_params_without_failed_creations
})

@ -1,4 +1,4 @@
defmodule Indexer.PendingTransaction.Fetcher do
defmodule Indexer.Fetcher.PendingTransaction do
@moduledoc """
Fetches pending transactions and imports them.
@ -6,6 +6,7 @@ defmodule Indexer.PendingTransaction.Fetcher do
validated version that may make it to the database first.
"""
use GenServer
use Indexer.Fetcher
require Logger
@ -13,7 +14,8 @@ defmodule Indexer.PendingTransaction.Fetcher do
alias Ecto.Changeset
alias Explorer.Chain
alias Indexer.{AddressExtraction, PendingTransaction}
alias Indexer.Fetcher.PendingTransaction
alias Indexer.Transform.Addresses
@chunk_size 250
@ -70,7 +72,7 @@ defmodule Indexer.PendingTransaction.Fetcher do
|> Keyword.merge(opts)
state =
%PendingTransaction.Fetcher{
%__MODULE__{
json_rpc_named_arguments: Keyword.fetch!(opts, :json_rpc_named_arguments),
interval: opts[:pending_transaction_interval] || @default_interval
}
@ -80,12 +82,12 @@ defmodule Indexer.PendingTransaction.Fetcher do
end
@impl GenServer
def handle_info(:fetch, %PendingTransaction.Fetcher{} = state) do
def handle_info(:fetch, %__MODULE__{} = state) do
task = Task.Supervisor.async_nolink(PendingTransaction.TaskSupervisor, fn -> task(state) end)
{:noreply, %PendingTransaction.Fetcher{state | task: task}}
{:noreply, %__MODULE__{state | task: task}}
end
def handle_info({ref, result}, %PendingTransaction.Fetcher{task: %Task{ref: ref}} = state) do
def handle_info({ref, result}, %__MODULE__{task: %Task{ref: ref}} = state) do
Process.demonitor(ref, [:flush])
case result do
@ -99,19 +101,19 @@ defmodule Indexer.PendingTransaction.Fetcher do
def handle_info(
{:DOWN, ref, :process, pid, reason},
%PendingTransaction.Fetcher{task: %Task{pid: pid, ref: ref}} = state
%__MODULE__{task: %Task{pid: pid, ref: ref}} = state
) do
Logger.error(fn -> "pending transaction fetcher task exited due to #{inspect(reason)}. Rescheduling." end)
{:noreply, schedule_fetch(state)}
end
defp schedule_fetch(%PendingTransaction.Fetcher{interval: interval} = state) do
defp schedule_fetch(%__MODULE__{interval: interval} = state) do
Process.send_after(self(), :fetch, interval)
%PendingTransaction.Fetcher{state | task: nil}
%__MODULE__{state | task: nil}
end
defp task(%PendingTransaction.Fetcher{json_rpc_named_arguments: json_rpc_named_arguments} = _state) do
defp task(%__MODULE__{json_rpc_named_arguments: json_rpc_named_arguments} = _state) do
Logger.metadata(fetcher: :pending_transaction)
case fetch_pending_transactions(json_rpc_named_arguments) do
@ -136,7 +138,7 @@ defmodule Indexer.PendingTransaction.Fetcher do
end
defp import_chunk(transactions_params) do
addresses_params = AddressExtraction.extract_addresses(%{transactions: transactions_params}, pending: true)
addresses_params = Addresses.extract_addresses(%{transactions: transactions_params}, pending: true)
# There's no need to queue up fetching the address balance since theses are pending transactions and cannot have
# affected the address balance yet since address balance is a balance at a given block and these transactions are

@ -1,8 +1,9 @@
defmodule Indexer.ReplacedTransaction.Fetcher do
defmodule Indexer.Fetcher.ReplacedTransaction do
@moduledoc """
Finds and updates replaced transactions.
"""
use Indexer.Fetcher
use Spandex.Decorators
require Logger
@ -10,7 +11,7 @@ defmodule Indexer.ReplacedTransaction.Fetcher do
alias Explorer.Chain
alias Explorer.Chain.Hash
alias Indexer.{BufferedTask, Tracer}
alias Indexer.ReplacedTransaction.Supervisor, as: ReplacedTransactionSupervisor
alias Indexer.Fetcher.ReplacedTransaction.Supervisor, as: ReplacedTransactionSupervisor
@behaviour BufferedTask
@ -20,7 +21,7 @@ defmodule Indexer.ReplacedTransaction.Fetcher do
flush_interval: :timer.seconds(3),
max_concurrency: @max_concurrency,
max_batch_size: @max_batch_size,
task_supervisor: Indexer.ReplacedTransaction.TaskSupervisor,
task_supervisor: Indexer.Fetcher.ReplacedTransaction.TaskSupervisor,
metadata: [fetcher: :replaced_transaction]
]
@ -94,7 +95,7 @@ defmodule Indexer.ReplacedTransaction.Fetcher do
@impl BufferedTask
@decorate trace(
name: "fetch",
resource: "Indexer.ReplacedTransaction.Fetcher.run/2",
resource: "Indexer.Fetcher.ReplacedTransaction.run/2",
service: :indexer,
tracer: Tracer
)

@ -1,8 +1,9 @@
defmodule Indexer.Token.Fetcher do
defmodule Indexer.Fetcher.Token do
@moduledoc """
Fetches information about a token.
"""
use Indexer.Fetcher
use Spandex.Decorators
alias Explorer.Chain
@ -17,7 +18,7 @@ defmodule Indexer.Token.Fetcher do
flush_interval: 300,
max_batch_size: 1,
max_concurrency: 10,
task_supervisor: Indexer.Token.TaskSupervisor
task_supervisor: Indexer.Fetcher.Token.TaskSupervisor
]
@doc false
@ -49,7 +50,7 @@ defmodule Indexer.Token.Fetcher do
end
@impl BufferedTask
@decorate trace(name: "fetch", resource: "Indexer.Token.Fetcher.run/2", service: :indexer, tracer: Tracer)
@decorate trace(name: "fetch", resource: "Indexer.Fetcher.Token.run/2", service: :indexer, tracer: Tracer)
def run([token_contract_address], _json_rpc_named_arguments) do
case Chain.token_from_address_hash(token_contract_address) do
{:ok, %Token{} = token} ->

@ -1,4 +1,4 @@
defmodule Indexer.TokenBalance.Fetcher do
defmodule Indexer.Fetcher.TokenBalance do
@moduledoc """
Fetches token balances and send the ones that were fetched to be imported in `Address.CurrentTokenBalance` and
`Address.TokenBalance`.
@ -13,6 +13,7 @@ defmodule Indexer.TokenBalance.Fetcher do
that always raise errors interacting with the Smart Contract.
"""
use Indexer.Fetcher
use Spandex.Decorators
require Logger
@ -27,7 +28,7 @@ defmodule Indexer.TokenBalance.Fetcher do
flush_interval: 300,
max_batch_size: 100,
max_concurrency: 10,
task_supervisor: Indexer.TokenBalance.TaskSupervisor
task_supervisor: Indexer.Fetcher.TokenBalance.TaskSupervisor
]
@max_retries 3
@ -75,7 +76,7 @@ defmodule Indexer.TokenBalance.Fetcher do
when reading their balance in the Smart Contract.
"""
@impl BufferedTask
@decorate trace(name: "fetch", resource: "Indexer.TokenBalance.Fetcher.run/2", tracer: Tracer, service: :indexer)
@decorate trace(name: "fetch", resource: "Indexer.Fetcher.TokenBalance.run/2", tracer: Tracer, service: :indexer)
def run(entries, _json_rpc_named_arguments) do
result =
entries

@ -1,16 +1,17 @@
defmodule Indexer.Token.MetadataUpdater do
defmodule Indexer.Fetcher.TokenUpdater do
@moduledoc """
Updates metadata for cataloged tokens
"""
use GenServer
use Indexer.Fetcher
alias Explorer.Chain
alias Explorer.Chain.Token
alias Explorer.Token.MetadataRetriever
def start_link(initial_state) do
GenServer.start_link(__MODULE__, initial_state, name: __MODULE__)
def start_link([initial_state, gen_server_options]) do
GenServer.start_link(__MODULE__, initial_state, gen_server_options)
end
@impl true

@ -1,9 +1,10 @@
defmodule Indexer.Block.Uncle.Fetcher do
defmodule Indexer.Fetcher.UncleBlock do
@moduledoc """
Fetches `t:Explorer.Chain.Block.t/0` by `hash` and updates `t:Explorer.Chain.Block.SecondDegreeRelation.t/0`
`uncle_fetched_at` where the `uncle_hash` matches `hash`.
"""
use Indexer.Fetcher
use Spandex.Decorators
require Logger
@ -12,7 +13,9 @@ defmodule Indexer.Block.Uncle.Fetcher do
alias EthereumJSONRPC.Blocks
alias Explorer.Chain
alias Explorer.Chain.Hash
alias Indexer.{AddressExtraction, Block, BufferedTask, Tracer}
alias Indexer.{Block, BufferedTask, Tracer}
alias Indexer.Fetcher.UncleBlock
alias Indexer.Transform.Addresses
@behaviour Block.Fetcher
@behaviour BufferedTask
@ -21,7 +24,7 @@ defmodule Indexer.Block.Uncle.Fetcher do
flush_interval: :timer.seconds(3),
max_batch_size: 10,
max_concurrency: 10,
task_supervisor: Indexer.Block.Uncle.TaskSupervisor,
task_supervisor: Indexer.Fetcher.UncleBlock.TaskSupervisor,
metadata: [fetcher: :block_uncle]
]
@ -70,7 +73,7 @@ defmodule Indexer.Block.Uncle.Fetcher do
end
@impl BufferedTask
@decorate trace(name: "fetch", resource: "Indexer.Block.Uncle.Fetcher.run/2", service: :indexer, tracer: Tracer)
@decorate trace(name: "fetch", resource: "Indexer.Fetcher.UncleBlock.run/2", service: :indexer, tracer: Tracer)
def run(hashes, %Block.Fetcher{json_rpc_named_arguments: json_rpc_named_arguments} = block_fetcher) do
# the same block could be included as an uncle on multiple blocks, but we only want to fetch it once
unique_hashes = Enum.uniq(hashes)
@ -108,7 +111,7 @@ defmodule Indexer.Block.Uncle.Fetcher do
block_fetcher,
original_entries
) do
addresses_params = AddressExtraction.extract_addresses(%{blocks: blocks_params, transactions: transactions_params})
addresses_params = Addresses.extract_addresses(%{blocks: blocks_params, transactions: transactions_params})
case Block.Fetcher.import(block_fetcher, %{
addresses: %{params: addresses_params},
@ -149,15 +152,15 @@ defmodule Indexer.Block.Uncle.Fetcher do
|> uncle_blocks()
|> fork_transactions()
|> Chain.import() do
# * CoinBalance.Fetcher.async_fetch_balances is not called because uncles don't affect balances
# * InternalTransaction.Fetcher.async_fetch is not called because internal transactions are based on transaction
# * CoinBalance.async_fetch_balances is not called because uncles don't affect balances
# * InternalTransaction.async_fetch is not called because internal transactions are based on transaction
# hash, which is shared with transaction on consensus blocks.
# * Token.Fetcher.async_fetch is not called because the tokens only matter on consensus blocks
# * TokenBalance.Fetcher.async_fetch is not called because it uses block numbers from consensus, not uncles
# * Token.async_fetch is not called because the tokens only matter on consensus blocks
# * TokenBalance.async_fetch is not called because it uses block numbers from consensus, not uncles
block_second_degree_relations
|> Enum.map(& &1.uncle_hash)
|> Block.Uncle.Fetcher.async_fetch_blocks()
|> UncleBlock.async_fetch_blocks()
ok
end

@ -1,39 +0,0 @@
defmodule Indexer.InternalTransaction.Supervisor do
@moduledoc """
Supervises `Indexer.InternalTransaction.Fetcher` and its batch tasks through
`Indexer.InternalTransaction.TaskSupervisor`.
"""
use Supervisor
alias Indexer.InternalTransaction.Fetcher
def child_spec([init_arguments]) do
child_spec([init_arguments, []])
end
def child_spec([_init_arguments, _gen_server_options] = start_link_arguments) do
default = %{
id: __MODULE__,
start: {__MODULE__, :start_link, start_link_arguments},
type: :supervisor
}
Supervisor.child_spec(default, [])
end
def start_link(arguments, gen_server_options \\ []) do
Supervisor.start_link(__MODULE__, arguments, Keyword.put_new(gen_server_options, :name, __MODULE__))
end
@impl Supervisor
def init(fetcher_arguments) do
Supervisor.init(
[
{Task.Supervisor, name: Indexer.InternalTransaction.TaskSupervisor},
{Fetcher, [fetcher_arguments, [name: Fetcher]]}
],
strategy: :one_for_one
)
end
end

@ -1,39 +0,0 @@
defmodule Indexer.PendingTransaction.Supervisor do
@moduledoc """
Supervises `Indexer.PendingTransaction.Fetcher` and its batch tasks through
`Indexer.PendingTransaction.TaskSupervisor`.
"""
use Supervisor
alias Indexer.PendingTransaction.Fetcher
def child_spec([init_arguments]) do
child_spec([init_arguments, []])
end
def child_spec([_init_arguments, _gen_server_options] = start_link_arguments) do
default = %{
id: __MODULE__,
start: {__MODULE__, :start_link, start_link_arguments},
type: :supervisor
}
Supervisor.child_spec(default, [])
end
def start_link(arguments, gen_server_options \\ []) do
Supervisor.start_link(__MODULE__, arguments, Keyword.put_new(gen_server_options, :name, __MODULE__))
end
@impl Supervisor
def init(fetcher_arguments) do
Supervisor.init(
[
{Task.Supervisor, name: Indexer.PendingTransaction.TaskSupervisor},
{Fetcher, [fetcher_arguments, [name: Fetcher]]}
],
strategy: :one_for_one
)
end
end

@ -1,47 +0,0 @@
defmodule Indexer.ReplacedTransaction.Supervisor do
@moduledoc """
Supervises `Indexer.ReplacedTransaction.Fetcher` and its batch tasks through
`Indexer.ReplacedTransaction.TaskSupervisor`.
"""
use Supervisor
alias Indexer.ReplacedTransaction.Fetcher
def child_spec([init_arguments]) do
child_spec([init_arguments, []])
end
def child_spec([_init_arguments, _gen_server_options] = start_link_arguments) do
default = %{
id: __MODULE__,
start: {__MODULE__, :start_link, start_link_arguments},
type: :supervisor
}
Supervisor.child_spec(default, [])
end
def start_link(arguments, gen_server_options \\ []) do
if disabled?() do
:ignore
else
Supervisor.start_link(__MODULE__, arguments, Keyword.put_new(gen_server_options, :name, __MODULE__))
end
end
def disabled?() do
Application.get_env(:indexer, __MODULE__, [])[:disabled?] == true
end
@impl Supervisor
def init(fetcher_arguments) do
Supervisor.init(
[
{Task.Supervisor, name: Indexer.ReplacedTransaction.TaskSupervisor},
{Fetcher, [fetcher_arguments, [name: Fetcher]]}
],
strategy: :one_for_one
)
end
end

@ -1,97 +0,0 @@
defmodule Indexer.Shrinkable.Supervisor do
@moduledoc """
Supervisor of all supervision trees that depend on `Indexer.Alarm.Supervisor`.
"""
use Supervisor
alias Indexer.{
Block,
Code,
CoinBalance,
InternalTransaction,
PendingTransaction,
ReplacedTransaction,
Token,
TokenBalance,
TokenTransfer
}
def child_spec([]) do
child_spec([[]])
end
def child_spec([init_arguments]) do
child_spec([init_arguments, []])
end
def child_spec([_init_arguments, _gen_server_options] = start_link_arguments) do
default = %{
id: __MODULE__,
start: {__MODULE__, :start_link, start_link_arguments},
type: :supervisor
}
Supervisor.child_spec(default, [])
end
def start_link(arguments, gen_server_options \\ []) do
Supervisor.start_link(__MODULE__, arguments, Keyword.put_new(gen_server_options, :name, __MODULE__))
end
@impl Supervisor
def init(%{memory_monitor: memory_monitor}) do
json_rpc_named_arguments = Application.fetch_env!(:indexer, :json_rpc_named_arguments)
block_fetcher_supervisor_named_arguments =
:indexer
|> Application.get_all_env()
|> Keyword.take(
~w(blocks_batch_size blocks_concurrency block_interval json_rpc_named_arguments receipts_batch_size
receipts_concurrency subscribe_named_arguments realtime_overrides)a
)
|> Enum.into(%{})
|> Map.put(:memory_monitor, memory_monitor)
|> Map.put_new(:realtime_overrides, %{})
Supervisor.init(
[
{CoinBalance.Supervisor,
[
[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor],
[name: CoinBalance.Supervisor]
]},
{PendingTransaction.Supervisor,
[[json_rpc_named_arguments: json_rpc_named_arguments], [name: PendingTransactionFetcher]]},
{Code.Supervisor,
[
[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor],
[name: Code.Supervisor]
]},
{ReplacedTransaction.Supervisor,
[
[memory_monitor: memory_monitor],
[name: ReplacedTransaction.Supervisor]
]},
{InternalTransaction.Supervisor,
[
[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor],
[name: InternalTransaction.Supervisor]
]},
{Token.Supervisor,
[
[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor],
[name: Token.Supervisor]
]},
{TokenBalance.Supervisor,
[
[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor],
[name: TokenBalance.Supervisor]
]},
{Block.Supervisor, [block_fetcher_supervisor_named_arguments, [name: Block.Supervisor]]},
{TokenTransfer.Uncataloged.Supervisor, [[], [name: TokenTransfer.Uncataloged.Supervisor]]}
],
strategy: :one_for_one
)
end
end

@ -0,0 +1,137 @@
defmodule Indexer.Supervisor do
@moduledoc """
Supervisor of all indexer worker supervision trees
"""
use Supervisor
alias Indexer.Block
alias Indexer.Block.{Catchup, Realtime}
alias Indexer.Fetcher.{
BlockReward,
CoinBalance,
CoinBalanceOnDemand,
ContractCode,
InternalTransaction,
PendingTransaction,
ReplacedTransaction,
Token,
TokenBalance,
TokenUpdater,
UncleBlock
}
alias Indexer.Temporary.{
AddressesWithoutCode,
FailedCreatedAddresses,
UncatalogedTokenTransfers
}
def child_spec([]) do
child_spec([[]])
end
def child_spec([init_arguments]) do
child_spec([init_arguments, []])
end
def child_spec([_init_arguments, _gen_server_options] = start_link_arguments) do
default = %{
id: __MODULE__,
start: {__MODULE__, :start_link, start_link_arguments},
type: :supervisor
}
Supervisor.child_spec(default, [])
end
def start_link(arguments, gen_server_options \\ []) do
Supervisor.start_link(__MODULE__, arguments, Keyword.put_new(gen_server_options, :name, __MODULE__))
end
@impl Supervisor
def init(%{memory_monitor: memory_monitor}) do
json_rpc_named_arguments = Application.fetch_env!(:indexer, :json_rpc_named_arguments)
named_arguments =
:indexer
|> Application.get_all_env()
|> Keyword.take(
~w(blocks_batch_size blocks_concurrency block_interval json_rpc_named_arguments receipts_batch_size
receipts_concurrency subscribe_named_arguments realtime_overrides)a
)
|> Enum.into(%{})
|> Map.put(:memory_monitor, memory_monitor)
|> Map.put_new(:realtime_overrides, %{})
%{
block_interval: block_interval,
realtime_overrides: realtime_overrides,
subscribe_named_arguments: subscribe_named_arguments
} = named_arguments
metadata_updater_inverval = Application.get_env(:indexer, :metadata_updater_days_interval)
block_fetcher =
named_arguments
|> Map.drop(~w(block_interval memory_monitor subscribe_named_arguments realtime_overrides)a)
|> Block.Fetcher.new()
fixing_realtime_fetcher = %Block.Fetcher{
broadcast: false,
callback_module: Realtime.Fetcher,
json_rpc_named_arguments: json_rpc_named_arguments
}
realtime_block_fetcher =
named_arguments
|> Map.drop(~w(block_interval memory_monitor subscribe_named_arguments realtime_overrides)a)
|> Map.merge(Enum.into(realtime_overrides, %{}))
|> Block.Fetcher.new()
realtime_subscribe_named_arguments = realtime_overrides[:subscribe_named_arguments] || subscribe_named_arguments
Supervisor.init(
[
# Root fetchers
{PendingTransaction.Supervisor, [[json_rpc_named_arguments: json_rpc_named_arguments]]},
{Realtime.Supervisor,
[
%{block_fetcher: realtime_block_fetcher, subscribe_named_arguments: realtime_subscribe_named_arguments},
[name: Realtime.Supervisor]
]},
{Catchup.Supervisor,
[
%{block_fetcher: block_fetcher, block_interval: block_interval, memory_monitor: memory_monitor},
[name: Catchup.Supervisor]
]},
# Async catchup fetchers
{UncleBlock.Supervisor, [[block_fetcher: block_fetcher, memory_monitor: memory_monitor]]},
{BlockReward.Supervisor,
[[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]},
{InternalTransaction.Supervisor,
[[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]},
{CoinBalance.Supervisor,
[[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]},
{Token.Supervisor, [[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]},
{ContractCode.Supervisor,
[[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]},
{TokenBalance.Supervisor,
[[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]},
{ReplacedTransaction.Supervisor, [[memory_monitor: memory_monitor]]},
# Out-of-band fetchers
{CoinBalanceOnDemand.Supervisor, [json_rpc_named_arguments]},
{TokenUpdater.Supervisor, [%{update_interval: metadata_updater_inverval}]},
# Temporary workers
{AddressesWithoutCode.Supervisor, [fixing_realtime_fetcher]},
{FailedCreatedAddresses.Supervisor, [json_rpc_named_arguments]},
{UncatalogedTokenTransfers.Supervisor, [[]]}
],
strategy: :one_for_one
)
end
end

@ -4,6 +4,7 @@ defmodule Indexer.Temporary.AddressesWithoutCode do
"""
use GenServer
use Indexer.Fetcher
require Logger

@ -1,38 +0,0 @@
defmodule Indexer.Temporary.AddressesWithoutCode.Supervisor do
@moduledoc """
Supervises `Indexer.Temporary.FailedCreatedAddresses`.
"""
use Supervisor
alias Indexer.Temporary.AddressesWithoutCode
def child_spec([init_arguments]) do
child_spec([init_arguments, []])
end
def child_spec([_init_arguments, _gen_server_options] = start_link_arguments) do
default = %{
id: __MODULE__,
start: {__MODULE__, :start_link, start_link_arguments},
type: :supervisor
}
Supervisor.child_spec(default, [])
end
def start_link(fetcher, gen_server_options \\ []) do
Supervisor.start_link(__MODULE__, fetcher, gen_server_options)
end
@impl Supervisor
def init(fetcher) do
Supervisor.init(
[
{Task.Supervisor, name: Indexer.Temporary.AddressesWithoutCode.TaskSupervisor},
{AddressesWithoutCode, [fetcher, [name: FailedCreatedAddresses]]}
],
strategy: :rest_for_one
)
end
end

@ -3,6 +3,7 @@ defmodule Indexer.Temporary.FailedCreatedAddresses do
Temporary module to fix internal transactions and their created transactions if a parent transaction has failed.
"""
use GenServer
use Indexer.Fetcher
require Logger
@ -10,6 +11,7 @@ defmodule Indexer.Temporary.FailedCreatedAddresses do
alias Explorer.Chain.{Address, Data, InternalTransaction, Transaction}
alias Explorer.Repo
alias Indexer.Fetcher.ContractCode
alias Indexer.Temporary.FailedCreatedAddresses.TaskSupervisor
@task_options [max_concurrency: 3, timeout: :infinity]
@ -97,7 +99,7 @@ defmodule Indexer.Temporary.FailedCreatedAddresses do
:ok =
internal_transaction
|> code_entry()
|> Indexer.Code.Fetcher.run(json_rpc_named_arguments)
|> ContractCode.run(json_rpc_named_arguments)
end)
Logger.debug(

@ -1,38 +0,0 @@
defmodule Indexer.Temporary.FailedCreatedAddresses.Supervisor do
@moduledoc """
Supervises `Indexer.Temporary.FailedCreatedAddresses`.
"""
use Supervisor
alias Indexer.Temporary.FailedCreatedAddresses
def child_spec([init_arguments]) do
child_spec([init_arguments, []])
end
def child_spec([_init_arguments, _gen_server_options] = start_link_arguments) do
default = %{
id: __MODULE__,
start: {__MODULE__, :start_link, start_link_arguments},
type: :supervisor
}
Supervisor.child_spec(default, [])
end
def start_link(json_rpc_named_arguments, gen_server_options \\ []) do
Supervisor.start_link(__MODULE__, json_rpc_named_arguments, gen_server_options)
end
@impl Supervisor
def init(json_rpc_named_arguments) do
Supervisor.init(
[
{Task.Supervisor, name: Indexer.Temporary.FailedCreatedAddresses.TaskSupervisor},
{FailedCreatedAddresses, [json_rpc_named_arguments, [name: FailedCreatedAddresses]]}
],
strategy: :rest_for_one
)
end
end

@ -1,4 +1,4 @@
defmodule Indexer.TokenTransfer.Uncataloged.Worker do
defmodule Indexer.Temporary.UncatalogedTokenTransfers do
@moduledoc """
Catalogs token tranfer logs missing an accompanying token transfer record.
@ -8,12 +8,13 @@ defmodule Indexer.TokenTransfer.Uncataloged.Worker do
"""
use GenServer
use Indexer.Fetcher
require Logger
alias Explorer.Chain
alias Indexer.Block.Catchup.Fetcher
alias Indexer.TokenTransfer.Uncataloged
alias Indexer.Temporary.UncatalogedTokenTransfers
def child_spec([init_arguments]) do
child_spec([init_arguments, []])
@ -93,6 +94,6 @@ defmodule Indexer.TokenTransfer.Uncataloged.Worker do
end
defp async_push_front(block_numbers) do
Task.Supervisor.async_nolink(Uncataloged.TaskSupervisor, Fetcher, :push_front, [block_numbers])
Task.Supervisor.async_nolink(UncatalogedTokenTransfers.TaskSupervisor, Fetcher, :push_front, [block_numbers])
end
end

@ -1,41 +0,0 @@
defmodule Indexer.Token.Supervisor do
@moduledoc """
Supervises `Indexer.Token.Fetcher` and its batch tasks through `Indexer.Token.TaskSupervisor`.
"""
use Supervisor
alias Indexer.Token.{Fetcher, MetadataUpdater}
def child_spec([init_arguments]) do
child_spec([init_arguments, []])
end
def child_spec([_init_arguments, _gen_server_options] = start_link_arguments) do
default = %{
id: __MODULE__,
start: {__MODULE__, :start_link, start_link_arguments},
type: :supervisor
}
Supervisor.child_spec(default, [])
end
def start_link(arguments, gen_server_options \\ []) do
Supervisor.start_link(__MODULE__, arguments, Keyword.put_new(gen_server_options, :name, __MODULE__))
end
@impl Supervisor
def init(fetcher_arguments) do
metadata_updater_inverval = Application.get_env(:indexer, :metadata_updater_days_interval)
Supervisor.init(
[
{Task.Supervisor, name: Indexer.Token.TaskSupervisor},
{Fetcher, [fetcher_arguments, [name: Fetcher]]},
{MetadataUpdater, %{update_interval: metadata_updater_inverval}}
],
strategy: :one_for_one
)
end
end

@ -1,38 +0,0 @@
defmodule Indexer.TokenBalance.Supervisor do
@moduledoc """
Supervises `Indexer.TokenBalance.Fetcher` and its batch tasks through `Indexer.TokenBalance.TaskSupervisor`
"""
use Supervisor
alias Indexer.TokenBalance.Fetcher
def child_spec([init_arguments]) do
child_spec([init_arguments, []])
end
def child_spec([_init_arguments, _gen_server_options] = start_link_arguments) do
default = %{
id: __MODULE__,
start: {__MODULE__, :start_link, start_link_arguments},
type: :supervisor
}
Supervisor.child_spec(default, [])
end
def start_link(arguments, gen_server_options \\ []) do
Supervisor.start_link(__MODULE__, arguments, Keyword.put_new(gen_server_options, :name, __MODULE__))
end
@impl Supervisor
def init(fetcher_arguments) do
Supervisor.init(
[
{Task.Supervisor, name: Indexer.TokenBalance.TaskSupervisor},
{Fetcher, [fetcher_arguments, [name: Fetcher]]}
],
strategy: :one_for_one
)
end
end

@ -10,13 +10,14 @@ defmodule Indexer.TokenBalances do
alias Explorer.Chain
alias Explorer.Token.BalanceReader
alias Indexer.{TokenBalance, Tracer}
alias Indexer.Fetcher.TokenBalance
alias Indexer.Tracer
@doc """
Fetches TokenBalances from specific Addresses and Blocks in the Blockchain
In case an exception is raised during the RPC call the particular TokenBalance request
is ignored and sent to `TokenBalance.Fetcher` to be fetched again.
is ignored and sent to `TokenBalance` to be fetched again.
## token_balances
@ -84,7 +85,7 @@ defmodule Indexer.TokenBalances do
block_number: token_balance.block_number
})
end)
|> TokenBalance.Fetcher.async_fetch()
|> TokenBalance.async_fetch()
end
defp ignore_request_with_errors(%{value: nil, value_fetched_at: nil, error: _error}), do: false

@ -1,44 +0,0 @@
defmodule Indexer.TokenTransfer.Uncataloged.Supervisor do
@moduledoc """
Supervises process for ensuring uncataloged token transfers get queued for indexing.
"""
use Supervisor
alias Indexer.TokenTransfer.Uncataloged.Worker
def child_spec([]) do
child_spec([[]])
end
def child_spec([init_arguments]) do
child_spec([init_arguments, [name: __MODULE__]])
end
def child_spec([_init_arguments, _gen_server_options] = start_link_arguments) do
spec = %{
id: __MODULE__,
start: {__MODULE__, :start_link, start_link_arguments},
restart: :transient,
type: :supervisor
}
Supervisor.child_spec(spec, [])
end
def start_link(init_arguments, gen_server_options \\ []) do
Supervisor.start_link(__MODULE__, init_arguments, gen_server_options)
end
@impl Supervisor
def init(_) do
children = [
{Worker, [[supervisor: self()], [name: Worker]]},
{Task.Supervisor, name: Indexer.TokenTransfer.Uncataloged.TaskSupervisor}
]
opts = [strategy: :one_for_all]
Supervisor.init(children, opts)
end
end

@ -1,9 +0,0 @@
defmodule Indexer.TokenTransfers do
@moduledoc """
Context for working with token transfers.
"""
alias Indexer.TokenTransfer.Parser
defdelegate parse(items), to: Parser
end

@ -1,4 +1,4 @@
defmodule Indexer.Address.CoinBalances do
defmodule Indexer.Transform.AddressCoinBalances do
@moduledoc """
Extracts `Explorer.Chain.Address.CoinBalance` params from other schema's params.
"""

@ -1,4 +1,4 @@
defmodule Indexer.Address.TokenBalances do
defmodule Indexer.Transform.AddressTokenBalances do
@moduledoc """
Extracts `Explorer.Address.TokenBalance` params from other schema's params.
"""

@ -1,4 +1,4 @@
defmodule Indexer.AddressExtraction do
defmodule Indexer.Transform.Addresses do
@moduledoc """
Extract Addresses from data fetched from the Blockchain and structured as Blocks, InternalTransactions,
Transactions and Logs.
@ -86,8 +86,7 @@ defmodule Indexer.AddressExtraction do
transactions: [
[
%{from: :block_number, to: :fetched_coin_balance_block_number},
%{from: :created_contract_address_hash, to: :hash},
%{from: :input, to: :contract_code}
%{from: :created_contract_address_hash, to: :hash}
],
[
%{from: :block_number, to: :fetched_coin_balance_block_number},
@ -155,7 +154,7 @@ defmodule Indexer.AddressExtraction do
Blocks have their `miner_hash` extracted.
iex> Indexer.AddressExtraction.extract_addresses(
iex> Indexer.Addresses.extract_addresses(
...> %{
...> blocks: [
...> %{
@ -175,7 +174,7 @@ defmodule Indexer.AddressExtraction do
Internal transactions can have their `from_address_hash`, `to_address_hash` and/or `created_contract_address_hash`
extracted.
iex> Indexer.AddressExtraction.extract_addresses(
iex> Indexer.Addresses.extract_addresses(
...> %{
...> internal_transactions: [
...> %{
@ -212,7 +211,7 @@ defmodule Indexer.AddressExtraction do
Transactions can have their `from_address_hash` and/or `to_address_hash` extracted.
iex> Indexer.AddressExtraction.extract_addresses(
iex> Indexer.Addresses.extract_addresses(
...> %{
...> transactions: [
...> %{
@ -248,7 +247,7 @@ defmodule Indexer.AddressExtraction do
Logs can have their `address_hash` extracted.
iex> Indexer.AddressExtraction.extract_addresses(
iex> Indexer.Addresses.extract_addresses(
...> %{
...> logs: [
...> %{
@ -267,7 +266,7 @@ defmodule Indexer.AddressExtraction do
When the same address is mentioned multiple times, the greatest `block_number` is used
iex> Indexer.AddressExtraction.extract_addresses(
iex> Indexer.Addresses.extract_addresses(
...> %{
...> blocks: [
...> %{
@ -320,7 +319,7 @@ defmodule Indexer.AddressExtraction do
When a contract is created and then used in internal transactions and transaction in the same fetched data, the
`created_contract_code` is merged with the greatest `block_number`
iex> Indexer.AddressExtraction.extract_addresses(
iex> Indexer.Addresses.extract_addresses(
...> %{
...> internal_transactions: [
...> %{

@ -1,8 +1,24 @@
defmodule Indexer.Block.Util do
defmodule Indexer.Transform.Blocks do
@moduledoc """
Helper functions for parsing block information.
Protocol for transforming blocks.
"""
@type block :: map()
@doc """
Transforms a block.
"""
@callback transform(block :: block()) :: block()
@doc """
Runs a list of blocks through the configured block transformer.
"""
def transform_blocks(blocks) when is_list(blocks) do
transformer = Application.get_env(:indexer, :block_transformer)
Enum.map(blocks, &transformer.transform/1)
end
@doc """
Calculates the signer's address by recovering the ECDSA public key.

@ -1,13 +1,13 @@
defmodule Indexer.Block.Transform.Base do
defmodule Indexer.Transform.Blocks.Base do
@moduledoc """
Default block transformer to be used.
"""
alias Indexer.Block.Transform
alias Indexer.Transform.Blocks
@behaviour Transform
@behaviour Blocks
@impl Transform
@impl Blocks
def transform(block) when is_map(block) do
block
end

@ -1,17 +1,17 @@
defmodule Indexer.Block.Transform.Clique do
defmodule Indexer.Transform.Blocks.Clique do
@moduledoc """
Handles block transforms for Clique chain.
"""
alias Indexer.Block.{Transform, Util}
alias Indexer.Transform.Blocks
@behaviour Transform
@behaviour Blocks
@impl Transform
@impl Blocks
def transform(%{number: 0} = block), do: block
def transform(block) when is_map(block) do
miner_address = Util.signer(block)
miner_address = Blocks.signer(block)
%{block | miner_hash: miner_address}
end

@ -1,4 +1,4 @@
defmodule Indexer.MintTransfer do
defmodule Indexer.Transform.MintTransfers do
@moduledoc """
Helper functions to parse addresses from mint transfers.
@ -14,7 +14,7 @@ defmodule Indexer.MintTransfer do
## Examples
iex> Indexer.MintTransfer.parse([
iex> Indexer.Transform.MintTransfers.parse([
...> %{
...> address_hash: "0x867305d19606aadba405ce534e303d0e225f9556",
...> block_number: 137_194,

@ -1,4 +1,4 @@
defmodule Indexer.TokenTransfer.Parser do
defmodule Indexer.Transform.TokenTransfers do
@moduledoc """
Helper functions for transforming data for ERC-20 and ERC-721 token transfers.
"""

@ -7,8 +7,18 @@ defmodule Indexer.Block.Catchup.BoundIntervalSupervisorTest do
import EthereumJSONRPC, only: [integer_to_quantity: 1]
alias Explorer.Chain.Block
alias Indexer.{BoundInterval, Code, CoinBalance, InternalTransaction, ReplacedTransaction, Token, TokenBalance}
alias Indexer.Block.{Catchup, Uncle}
alias Indexer.BoundInterval
alias Indexer.Block.Catchup
alias Indexer.Fetcher.{
CoinBalance,
ContractCode,
InternalTransaction,
ReplacedTransaction,
Token,
TokenBalance,
UncleBlock
}
@moduletag capture_log: true
@ -204,12 +214,12 @@ defmodule Indexer.Block.Catchup.BoundIntervalSupervisorTest do
CoinBalance.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
InternalTransaction.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
Code.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
ContractCode.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
Token.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
TokenBalance.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
ReplacedTransaction.Supervisor.Case.start_supervised!()
Uncle.Supervisor.Case.start_supervised!(
UncleBlock.Supervisor.Case.start_supervised!(
block_fetcher: %Indexer.Block.Fetcher{json_rpc_named_arguments: json_rpc_named_arguments}
)
@ -410,7 +420,7 @@ defmodule Indexer.Block.Catchup.BoundIntervalSupervisorTest do
start_supervised!({Task.Supervisor, name: Indexer.Block.Catchup.TaskSupervisor})
CoinBalance.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
Code.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
ContractCode.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
InternalTransaction.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
Token.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
TokenBalance.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
@ -504,12 +514,12 @@ defmodule Indexer.Block.Catchup.BoundIntervalSupervisorTest do
start_supervised({Task.Supervisor, name: Indexer.Block.Catchup.TaskSupervisor})
CoinBalance.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
InternalTransaction.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
Code.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
ContractCode.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
Token.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
TokenBalance.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
ReplacedTransaction.Supervisor.Case.start_supervised!()
Uncle.Supervisor.Case.start_supervised!(
UncleBlock.Supervisor.Case.start_supervised!(
block_fetcher: %Indexer.Block.Fetcher{json_rpc_named_arguments: json_rpc_named_arguments}
)

@ -7,8 +7,9 @@ defmodule Indexer.Block.Catchup.FetcherTest do
alias Explorer.Chain
alias Explorer.Chain.Block.Reward
alias Indexer.{Block, CoinBalance, InternalTransaction, Token, TokenBalance}
alias Indexer.Block
alias Indexer.Block.Catchup.Fetcher
alias Indexer.Fetcher.{BlockReward, CoinBalance, InternalTransaction, Token, TokenBalance, UncleBlock}
@moduletag capture_log: true
@ -48,7 +49,7 @@ defmodule Indexer.Block.Catchup.FetcherTest do
end
end)
Process.register(pid, Block.Uncle.Fetcher)
Process.register(pid, UncleBlock)
nephew_hash = block_hash() |> to_string()
uncle_hash = block_hash() |> to_string()
@ -315,7 +316,7 @@ defmodule Indexer.Block.Catchup.FetcherTest do
end
end)
Process.register(pid, Indexer.Block.Reward.Fetcher)
Process.register(pid, BlockReward)
assert %{first_block_number: ^block_number, missing_block_count: 1, shrunk: false} =
Fetcher.task(%Fetcher{
@ -414,7 +415,7 @@ defmodule Indexer.Block.Catchup.FetcherTest do
end
end)
Process.register(pid, Indexer.Block.Reward.Fetcher)
Process.register(pid, BlockReward)
assert %{first_block_number: ^block_number, missing_block_count: 1, shrunk: false} =
Fetcher.task(%Fetcher{

@ -1,8 +1,8 @@
defmodule Indexer.SequenceTest do
defmodule Indexer.Block.Catchup.SequenceTest do
use ExUnit.Case
alias Indexer.Block.Catchup.Sequence
alias Indexer.Memory.Shrinkable
alias Indexer.Sequence
describe "start_link/1" do
test "without :ranges with :first with positive step pops infinitely" do

@ -9,8 +9,18 @@ defmodule Indexer.Block.FetcherTest do
alias Explorer.Chain
alias Explorer.Chain.{Address, Log, Transaction, Wei}
alias Indexer.{CoinBalance, BufferedTask, Code, InternalTransaction, ReplacedTransaction, Token, TokenBalance}
alias Indexer.Block.{Fetcher, Uncle}
alias Indexer.Block.Fetcher
alias Indexer.BufferedTask
alias Indexer.Fetcher.{
CoinBalance,
ContractCode,
InternalTransaction,
ReplacedTransaction,
Token,
TokenBalance,
UncleBlock
}
@moduletag capture_log: true
@ -41,13 +51,13 @@ defmodule Indexer.Block.FetcherTest do
describe "import_range/2" do
setup %{json_rpc_named_arguments: json_rpc_named_arguments} do
CoinBalance.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
Code.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
ContractCode.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
InternalTransaction.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
Token.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
TokenBalance.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
ReplacedTransaction.Supervisor.Case.start_supervised!()
Uncle.Supervisor.Case.start_supervised!(
UncleBlock.Supervisor.Case.start_supervised!(
block_fetcher: %Fetcher{json_rpc_named_arguments: json_rpc_named_arguments}
)
@ -226,8 +236,8 @@ defmodule Indexer.Block.FetcherTest do
errors: []
}} = result
wait_for_tasks(InternalTransaction.Fetcher)
wait_for_tasks(CoinBalance.Fetcher)
wait_for_tasks(InternalTransaction)
wait_for_tasks(CoinBalance)
assert Repo.aggregate(Chain.Block, :count, :hash) == 1
assert Repo.aggregate(Address, :count, :hash) == 1
@ -492,8 +502,8 @@ defmodule Indexer.Block.FetcherTest do
]
}} = Fetcher.fetch_and_import_range(block_fetcher, block_number..block_number)
wait_for_tasks(InternalTransaction.Fetcher)
wait_for_tasks(CoinBalance.Fetcher)
wait_for_tasks(InternalTransaction)
wait_for_tasks(CoinBalance)
assert Repo.aggregate(Block, :count, :hash) == 1
assert Repo.aggregate(Address, :count, :hash) == 5
@ -587,8 +597,8 @@ defmodule Indexer.Block.FetcherTest do
errors: []
}} = Fetcher.fetch_and_import_range(block_fetcher, block_number..block_number)
wait_for_tasks(InternalTransaction.Fetcher)
wait_for_tasks(CoinBalance.Fetcher)
wait_for_tasks(InternalTransaction)
wait_for_tasks(CoinBalance)
assert Repo.aggregate(Chain.Block, :count, :hash) == 1
assert Repo.aggregate(Address, :count, :hash) == 2

@ -6,8 +6,9 @@ defmodule Indexer.Block.Realtime.FetcherTest do
alias Explorer.Chain
alias Explorer.Chain.{Address, Transaction}
alias Indexer.{Sequence, Token, TokenBalance, ReplacedTransaction}
alias Indexer.Block.{Realtime, Uncle}
alias Indexer.Block.Catchup.Sequence
alias Indexer.Block.Realtime
alias Indexer.Fetcher.{ContractCode, InternalTransaction, ReplacedTransaction, Token, TokenBalance, UncleBlock}
@moduletag capture_log: true
@ -50,7 +51,11 @@ defmodule Indexer.Block.Realtime.FetcherTest do
Token.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
Uncle.Supervisor.Case.start_supervised!(
ContractCode.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
InternalTransaction.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
UncleBlock.Supervisor.Case.start_supervised!(
block_fetcher: %Indexer.Block.Fetcher{json_rpc_named_arguments: json_rpc_named_arguments}
)
@ -200,11 +205,18 @@ defmodule Indexer.Block.Realtime.FetcherTest do
}
]}
end)
|> expect(:json_rpc, fn [%{method: "trace_block"}, %{method: "trace_block"}] = requests, _options ->
responses = Enum.map(requests, fn %{id: id} -> %{id: id, result: []} end)
{:ok, responses}
end)
|> expect(:json_rpc, 2, fn
[
%{id: 0, jsonrpc: "2.0", method: "trace_block", params: ["0x3C365F"]},
%{id: 1, jsonrpc: "2.0", method: "trace_block", params: ["0x3C3660"]}
],
_ ->
{:ok,
[
%{id: 0, jsonrpc: "2.0", result: []},
%{id: 1, jsonrpc: "2.0", result: []}
]}
[
%{
id: 0,
@ -346,28 +358,22 @@ defmodule Indexer.Block.Realtime.FetcherTest do
id: 0,
jsonrpc: "2.0",
method: "eth_getBalance",
params: ["0x11c4469d974f8af5ba9ec99f3c42c07c848c861c", "0x3C365F"]
},
%{
id: 1,
jsonrpc: "2.0",
method: "eth_getBalance",
params: ["0x40b18103537c0f15d5e137dd8ddd019b84949d16", "0x3C365F"]
},
%{
id: 2,
id: 1,
jsonrpc: "2.0",
method: "eth_getBalance",
params: ["0x5ee341ac44d344ade1ca3a771c59b98eb2a77df2", "0x3C365F"]
},
%{
id: 3,
id: 2,
jsonrpc: "2.0",
method: "eth_getBalance",
params: ["0x66c9343c7e8ca673a1fedf9dbf2cd7936dbbf7e3", "0x3C3660"]
},
%{
id: 4,
id: 3,
jsonrpc: "2.0",
method: "eth_getBalance",
params: ["0x698bf6943bab687b2756394624aa183f434f65da", "0x3C365F"]
@ -376,11 +382,10 @@ defmodule Indexer.Block.Realtime.FetcherTest do
_ ->
{:ok,
[
%{id: 0, jsonrpc: "2.0", result: "0x49e3de5187cf037d127"},
%{id: 1, jsonrpc: "2.0", result: "0x148adc763b603291685"},
%{id: 2, jsonrpc: "2.0", result: "0x53474fa377a46000"},
%{id: 3, jsonrpc: "2.0", result: "0x53507afe51f28000"},
%{id: 4, jsonrpc: "2.0", result: "0x3e1a95d7517dc197108"}
%{id: 0, jsonrpc: "2.0", result: "0x148adc763b603291685"},
%{id: 1, jsonrpc: "2.0", result: "0x53474fa377a46000"},
%{id: 2, jsonrpc: "2.0", result: "0x53507afe51f28000"},
%{id: 3, jsonrpc: "2.0", result: "0x3e1a95d7517dc197108"}
]}
end)
end
@ -391,9 +396,8 @@ defmodule Indexer.Block.Realtime.FetcherTest do
addresses: [
%Address{hash: first_address_hash, fetched_coin_balance_block_number: 3_946_079},
%Address{hash: second_address_hash, fetched_coin_balance_block_number: 3_946_079},
%Address{hash: third_address_hash, fetched_coin_balance_block_number: 3_946_079},
%Address{hash: fourth_address_hash, fetched_coin_balance_block_number: 3_946_080},
%Address{hash: fifth_address_hash, fetched_coin_balance_block_number: 3_946_079}
%Address{hash: third_address_hash, fetched_coin_balance_block_number: 3_946_080},
%Address{hash: fourth_address_hash, fetched_coin_balance_block_number: 3_946_079}
],
address_coin_balances: [
%{
@ -406,26 +410,14 @@ defmodule Indexer.Block.Realtime.FetcherTest do
},
%{
address_hash: third_address_hash,
block_number: 3_946_079
},
%{
address_hash: fourth_address_hash,
block_number: 3_946_080
},
%{
address_hash: fifth_address_hash,
address_hash: fourth_address_hash,
block_number: 3_946_079
}
],
blocks: [%Chain.Block{number: 3_946_079}, %Chain.Block{number: 3_946_080}],
internal_transactions: [
%{index: 0, transaction_hash: transaction_hash},
%{index: 1, transaction_hash: transaction_hash},
%{index: 2, transaction_hash: transaction_hash},
%{index: 3, transaction_hash: transaction_hash},
%{index: 4, transaction_hash: transaction_hash},
%{index: 5, transaction_hash: transaction_hash}
],
transactions: [%Transaction{hash: transaction_hash}]
},
errors: []

@ -1,40 +0,0 @@
defmodule Indexer.Block.UtilTest do
use ExUnit.Case
alias Indexer.Block.Util
test "signer/1" do
data = %{
difficulty: 1,
extra_data:
"0xd68301080d846765746886676f312e3130856c696e7578000000000000000000773ab2ca8f47904a14739ad80a75b71d9d29b9fff8b7ecdcb73efffa6f74122f17d304b5dc8e6e5f256c9474dd115c8d4dae31b7a3d409e5c3270f8fde41cd8c00",
gas_limit: 7_753_377,
gas_used: 1_810_195,
hash: "0x7004c895e812c55b0c2be8a46d72ca300a683dc27d1d7917ee7742d4d0359c1f",
logs_bloom:
"0x00000000000000020000000000002000000400000000000000000000000000000000000000000000040000080004000020000010000000000000000000000000000000000000000008000008000000000000000000200000000000000000000000000000020000000000000000000800000000000000804000000010080000000800000000000000000000000000000000000000000000800000000000080000000008000400000000404000000000000000000000000200000000000000000000000002000000000000001002000000000000002000000008000000000020000000000000000000000000000000000000000000000000400000800000000000",
miner_hash: "0x0000000000000000000000000000000000000000",
mix_hash: "0x0000000000000000000000000000000000000000000000000000000000000000",
nonce: "0x0000000000000000",
number: 2_848_394,
parent_hash: "0x20350fc367e19d3865be1ea7da72ab81f8f9941c43ac6bb24a34a0a7caa2f3df",
receipts_root: "0x6ade4ac1079ea50cfadcce2b75ffbe4f9b14bf69b4607bbf1739463076ca6246",
sha3_uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
size: 6437,
state_root: "0x23f63347851bcd109059d007d71e19c4f5e73b7f0862bebcd04458333a004d92",
timestamp: DateTime.from_unix!(1_534_796_040),
total_difficulty: 5_353_647,
transactions: [
"0x7e3bb851fc74a436826d2af6b96e4db9484431811ef0d9c9e78370488d33d4e5",
"0x3976fd1e3d2a715c3cfcfde9bd3210798c26c017b8edb841d319227ecb3322fb",
"0xd8db124005bb8b6fda7b71fd56ac782552a66af58fe843ba3c4930423b87d1d2",
"0x10c1a1ca4d9f4b2bd5b89f7bbcbbc2d69e166fe23662b8db4f6beae0f50ac9fd",
"0xaa58a6545677c796a56b8bc874174c8cfd31a6c6e6ca3a87e086d4f66d52858a"
],
transactions_root: "0xde8d25c0b9b54310128a21601331094b43f910f9f96102869c2e2dca94884bf4",
uncles: []
}
assert Util.signer(data) == "0xfc18cbc391de84dbd87db83b20935d3e89f5dd91"
end
end

@ -1,4 +1,4 @@
defmodule Indexer.Block.Reward.FetcherTest do
defmodule Indexer.Fetcher.BlockRewardTest do
# MUST be `async: false` so that {:shared, pid} is set for connection to allow CoinBalanceFetcher's self-send to have
# connection allowed immediately.
use EthereumJSONRPC.Case, async: false
@ -9,8 +9,8 @@ defmodule Indexer.Block.Reward.FetcherTest do
alias Explorer.Chain
alias Explorer.Chain.{Block, Hash, Wei}
alias Indexer.Block.Reward
alias Indexer.BufferedTask
alias Indexer.Fetcher.BlockReward
@moduletag :capture_log
@ -37,32 +37,32 @@ defmodule Indexer.Block.Reward.FetcherTest do
describe "init/3" do
test "without blocks" do
assert [] = Reward.Fetcher.init([], &[&1 | &2], nil)
assert [] = BlockReward.init([], &[&1 | &2], nil)
end
test "with consensus block without reward" do
%Block{number: block_number} = insert(:block)
assert [^block_number] = Reward.Fetcher.init([], &[&1 | &2], nil)
assert [^block_number] = BlockReward.init([], &[&1 | &2], nil)
end
test "with consensus block with reward" do
block = insert(:block)
insert(:reward, address_hash: block.miner_hash, block_hash: block.hash)
assert [] = Reward.Fetcher.init([], &[&1 | &2], nil)
assert [] = BlockReward.init([], &[&1 | &2], nil)
end
test "with non-consensus block" do
insert(:block, consensus: false)
assert [] = Reward.Fetcher.init([], &[&1 | &2], nil)
assert [] = BlockReward.init([], &[&1 | &2], nil)
end
end
describe "async_fetch/1" do
setup %{json_rpc_named_arguments: json_rpc_named_arguments} do
Reward.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
BlockReward.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
block = insert(:block)
@ -130,11 +130,11 @@ defmodule Indexer.Block.Reward.FetcherTest do
end
end)
Process.register(pid, Indexer.CoinBalance.Fetcher)
Process.register(pid, Indexer.Fetcher.CoinBalance)
assert :ok = Reward.Fetcher.async_fetch([block_number])
assert :ok = BlockReward.async_fetch([block_number])
wait_for_tasks(Reward.Fetcher)
wait_for_tasks(BlockReward)
assert count(Chain.Block.Reward) == 1
assert_receive {:balance_fields, [{^miner_hash_bytes, ^block_number}]}, 500
@ -201,11 +201,11 @@ defmodule Indexer.Block.Reward.FetcherTest do
end
end)
Process.register(pid, Indexer.CoinBalance.Fetcher)
Process.register(pid, Indexer.Fetcher.CoinBalance)
assert :ok = Reward.Fetcher.async_fetch([block_number])
assert :ok = BlockReward.async_fetch([block_number])
wait_for_tasks(Reward.Fetcher)
wait_for_tasks(BlockReward)
assert count(Chain.Block.Reward) == 1
assert_receive {:balance_fields, [{^miner_hash_bytes, ^block_number}]}, 500
@ -256,9 +256,9 @@ defmodule Indexer.Block.Reward.FetcherTest do
}
end)
assert :ok = Reward.Fetcher.async_fetch([block_number])
assert :ok = BlockReward.async_fetch([block_number])
wait_for_tasks(Reward.Fetcher)
wait_for_tasks(BlockReward)
assert count(Chain.Block.Reward) == 0
end
@ -334,9 +334,9 @@ defmodule Indexer.Block.Reward.FetcherTest do
end
end)
Process.register(pid, Indexer.CoinBalance.Fetcher)
Process.register(pid, Indexer.Fetcher.CoinBalance)
assert :ok = Reward.Fetcher.run([block_number], json_rpc_named_arguments)
assert :ok = BlockReward.run([block_number], json_rpc_named_arguments)
assert count(Chain.Block.Reward) == 1
assert count(Chain.Address.CoinBalance) == 1
@ -422,9 +422,9 @@ defmodule Indexer.Block.Reward.FetcherTest do
end
end)
Process.register(pid, Indexer.CoinBalance.Fetcher)
Process.register(pid, Indexer.Fetcher.CoinBalance)
assert :ok = Reward.Fetcher.run([block_number], json_rpc_named_arguments)
assert :ok = BlockReward.run([block_number], json_rpc_named_arguments)
assert count(Chain.Block.Reward) == 2
assert count(Chain.Address.CoinBalance) == 2
@ -504,9 +504,9 @@ defmodule Indexer.Block.Reward.FetcherTest do
end
end)
Process.register(pid, Indexer.CoinBalance.Fetcher)
Process.register(pid, Indexer.Fetcher.CoinBalance)
assert :ok = Reward.Fetcher.run([block_number], json_rpc_named_arguments)
assert :ok = BlockReward.run([block_number], json_rpc_named_arguments)
assert count(Chain.Block.Reward) == 1
assert count(Chain.Address.CoinBalance) == 1
@ -565,7 +565,7 @@ defmodule Indexer.Block.Reward.FetcherTest do
}
end)
assert :ok = Reward.Fetcher.run([block_number], json_rpc_named_arguments)
assert :ok = BlockReward.run([block_number], json_rpc_named_arguments)
assert count(Chain.Block.Reward) == 0
assert count(Chain.Address.CoinBalance) == 0
@ -639,10 +639,10 @@ defmodule Indexer.Block.Reward.FetcherTest do
end
end)
Process.register(pid, Indexer.CoinBalance.Fetcher)
Process.register(pid, Indexer.Fetcher.CoinBalance)
assert {:retry, [^error_block_number]} =
Reward.Fetcher.run([block_number, error_block_number], json_rpc_named_arguments)
BlockReward.run([block_number, error_block_number], json_rpc_named_arguments)
assert count(Chain.Block.Reward) == 1
assert count(Chain.Address.CoinBalance) == 1

@ -1,4 +1,4 @@
defmodule Indexer.CoinBalance.OnDemandFetcherTest do
defmodule Indexer.Fetcher.CoinBalanceOnDemandTest do
# MUST be `async: false` so that {:shared, pid} is set for connection to allow CoinBalanceFetcher's self-send to have
# connection allowed immediately.
use EthereumJSONRPC.Case, async: false
@ -9,7 +9,7 @@ defmodule Indexer.CoinBalance.OnDemandFetcherTest do
alias Explorer.Chain.Events.Subscriber
alias Explorer.Chain.Wei
alias Explorer.Counters.AverageBlockTime
alias Indexer.CoinBalance.OnDemandFetcher
alias Indexer.Fetcher.CoinBalanceOnDemand
@moduletag :capture_log
@ -24,7 +24,7 @@ defmodule Indexer.CoinBalance.OnDemandFetcherTest do
start_supervised!({Task.Supervisor, name: Indexer.TaskSupervisor})
start_supervised!(AverageBlockTime)
start_supervised!({OnDemandFetcher, [mocked_json_rpc_named_arguments, [name: OnDemandFetcher]]})
start_supervised!({CoinBalanceOnDemand, [mocked_json_rpc_named_arguments, [name: CoinBalanceOnDemand]]})
Application.put_env(:explorer, AverageBlockTime, enabled: true)
@ -41,10 +41,9 @@ defmodule Indexer.CoinBalance.OnDemandFetcherTest do
# we space these very far apart so that we know it will consider the 0th block stale (it calculates how far
# back we'd need to go to get 24 hours in the past)
block_0 = insert(:block, number: 0, timestamp: Timex.shift(now, hours: -50))
AverageBlockTime.average_block_time(block_0)
block_1 = insert(:block, number: 1, timestamp: now)
AverageBlockTime.average_block_time(block_1)
insert(:block, number: 0, timestamp: Timex.shift(now, hours: -50))
insert(:block, number: 1, timestamp: now)
AverageBlockTime.refresh()
stale_address = insert(:address, fetched_coin_balance: 1, fetched_coin_balance_block_number: 0)
current_address = insert(:address, fetched_coin_balance: 1, fetched_coin_balance_block_number: 1)
@ -58,25 +57,25 @@ defmodule Indexer.CoinBalance.OnDemandFetcherTest do
test "treats all addresses as current if the average block time is disabled", %{stale_address: address} do
Application.put_env(:explorer, AverageBlockTime, enabled: false)
assert OnDemandFetcher.trigger_fetch(address) == :current
assert CoinBalanceOnDemand.trigger_fetch(address) == :current
end
test "if the address has not been fetched within the last 24 hours of blocks it is considered stale", %{
stale_address: address
} do
assert OnDemandFetcher.trigger_fetch(address) == {:stale, 1}
assert CoinBalanceOnDemand.trigger_fetch(address) == {:stale, 1}
end
test "if the address has been fetched within the last 24 hours of blocks it is considered current", %{
current_address: address
} do
assert OnDemandFetcher.trigger_fetch(address) == :current
assert CoinBalanceOnDemand.trigger_fetch(address) == :current
end
test "if there is an unfetched balance within the window for an address, it is considered pending", %{
pending_address: pending_address
} do
assert OnDemandFetcher.trigger_fetch(pending_address) == {:pending, 2}
assert CoinBalanceOnDemand.trigger_fetch(pending_address) == {:pending, 2}
end
end
@ -89,10 +88,9 @@ defmodule Indexer.CoinBalance.OnDemandFetcherTest do
# we space these very far apart so that we know it will consider the 0th block stale (it calculates how far
# back we'd need to go to get 24 hours in the past)
block_0 = insert(:block, number: 0, timestamp: Timex.shift(now, hours: -50))
AverageBlockTime.average_block_time(block_0)
block_1 = insert(:block, number: 1, timestamp: now)
AverageBlockTime.average_block_time(block_1)
insert(:block, number: 0, timestamp: Timex.shift(now, hours: -50))
insert(:block, number: 1, timestamp: now)
AverageBlockTime.refresh()
:ok
end
@ -113,7 +111,7 @@ defmodule Indexer.CoinBalance.OnDemandFetcherTest do
{:ok, [%{id: id, jsonrpc: "2.0", result: "0x02"}]}
end)
assert OnDemandFetcher.trigger_fetch(address) == {:stale, 1}
assert CoinBalanceOnDemand.trigger_fetch(address) == {:stale, 1}
{:ok, expected_wei} = Wei.cast(2)
@ -140,7 +138,7 @@ defmodule Indexer.CoinBalance.OnDemandFetcherTest do
{:ok, [%{id: id, jsonrpc: "2.0", result: "0x02"}]}
end)
assert OnDemandFetcher.trigger_fetch(address) == {:pending, 2}
assert CoinBalanceOnDemand.trigger_fetch(address) == {:pending, 2}
{:ok, expected_wei} = Wei.cast(2)

@ -1,4 +1,4 @@
defmodule Indexer.CoinBalance.FetcherTest do
defmodule Indexer.Fetcher.CoinBalanceTest do
# MUST be `async: false` so that {:shared, pid} is set for connection to allow CoinBalanceFetcher's self-send to have
# connection allowed immediately.
use EthereumJSONRPC.Case, async: false
@ -8,7 +8,7 @@ defmodule Indexer.CoinBalance.FetcherTest do
import Mox
alias Explorer.Chain.{Address, Hash, Wei}
alias Indexer.CoinBalance
alias Indexer.Fetcher.CoinBalance
@moduletag :capture_log
@ -182,7 +182,7 @@ defmodule Indexer.CoinBalance.FetcherTest do
CoinBalance.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
assert :ok = CoinBalance.Fetcher.async_fetch_balances([%{address_hash: hash, block_number: block_number}])
assert :ok = CoinBalance.async_fetch_balances([%{address_hash: hash, block_number: block_number}])
address =
wait(fn ->
@ -254,7 +254,7 @@ defmodule Indexer.CoinBalance.FetcherTest do
{:ok, %Hash{bytes: address_hash_bytes}} = Hash.Address.cast(hash_data)
entries = Enum.map(block_quantities, &{address_hash_bytes, quantity_to_integer(&1)})
case CoinBalance.Fetcher.run(entries, json_rpc_named_arguments) do
case CoinBalance.run(entries, json_rpc_named_arguments) do
:ok ->
balances = Repo.all(from(balance in Address.CoinBalance, where: balance.address_hash == ^hash_data))
@ -309,7 +309,7 @@ defmodule Indexer.CoinBalance.FetcherTest do
{:ok, [%{id: id, error: %{code: 1, message: "Bad"}}]}
end)
assert {:retry, ^entries} = CoinBalance.Fetcher.run(entries, json_rpc_named_arguments)
assert {:retry, ^entries} = CoinBalance.run(entries, json_rpc_named_arguments)
end
test "retries none if all imported and no fetch errors", %{json_rpc_named_arguments: json_rpc_named_arguments} do
@ -320,7 +320,7 @@ defmodule Indexer.CoinBalance.FetcherTest do
{:ok, [%{id: id, result: "0x1"}]}
end)
assert :ok = CoinBalance.Fetcher.run(entries, json_rpc_named_arguments)
assert :ok = CoinBalance.run(entries, json_rpc_named_arguments)
end
test "retries retries fetch errors if all imported", %{json_rpc_named_arguments: json_rpc_named_arguments} do
@ -360,7 +360,7 @@ defmodule Indexer.CoinBalance.FetcherTest do
end)
assert {:retry, [{^address_hash_bytes, ^bad_block_number}]} =
CoinBalance.Fetcher.run(
CoinBalance.run(
[{address_hash_bytes, good_block_number}, {address_hash_bytes, bad_block_number}],
json_rpc_named_arguments
)

@ -1,4 +1,4 @@
defmodule Indexer.Code.FetcherTest do
defmodule Indexer.Fetcher.ContractCodeTest do
use EthereumJSONRPC.Case, async: false
use Explorer.DataCase
@ -7,7 +7,7 @@ defmodule Indexer.Code.FetcherTest do
import Mox
alias Explorer.Chain.{Address, Transaction}
alias Indexer.Code
alias Indexer.Fetcher.ContractCode
@moduletag :capture_log
@ -83,10 +83,10 @@ defmodule Indexer.Code.FetcherTest do
insert(:address, hash: address)
insert(:transaction, hash: hash, created_contract_address_hash: address)
Code.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
ContractCode.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
assert :ok =
Code.Fetcher.async_fetch([
ContractCode.async_fetch([
%{created_contract_address_hash: address, block_number: block_number, hash: hash}
])

@ -1,4 +1,4 @@
defmodule Indexer.InternalTransaction.FetcherTest do
defmodule Indexer.Fetcher.InternalTransactionTest do
use EthereumJSONRPC.Case, async: false
use Explorer.DataCase
@ -6,8 +6,7 @@ defmodule Indexer.InternalTransaction.FetcherTest do
import Mox
alias Explorer.Chain.{Address, Hash, Transaction}
alias Indexer.{CoinBalance, InternalTransaction, PendingTransaction}
alias Indexer.Fetcher.{CoinBalance, InternalTransaction, PendingTransaction}
# MUST use global mode because we aren't guaranteed to get PendingTransactionFetcher's pid back fast enough to `allow`
# it to use expectations and stubs from test's pid.
@ -17,7 +16,7 @@ defmodule Indexer.InternalTransaction.FetcherTest do
@moduletag [capture_log: true, no_geth: true]
test "does not try to fetch pending transactions from Indexer.PendingTransaction.Fetcher", %{
test "does not try to fetch pending transactions from Indexer.Fetcher.PendingTransaction", %{
json_rpc_named_arguments: json_rpc_named_arguments
} do
if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do
@ -71,9 +70,9 @@ defmodule Indexer.InternalTransaction.FetcherTest do
end)
hash_strings =
InternalTransaction.Fetcher.init([], fn hash_string, acc -> [hash_string | acc] end, json_rpc_named_arguments)
InternalTransaction.init([], fn hash_string, acc -> [hash_string | acc] end, json_rpc_named_arguments)
assert :ok = InternalTransaction.Fetcher.run(hash_strings, json_rpc_named_arguments)
assert :ok = InternalTransaction.run(hash_strings, json_rpc_named_arguments)
end
@tag :no_geth
@ -102,9 +101,9 @@ defmodule Indexer.InternalTransaction.FetcherTest do
block_number = 1_000_006
insert(:block, number: block_number)
assert :ok = InternalTransaction.Fetcher.run([block_number], json_rpc_named_arguments)
assert :ok = InternalTransaction.run([block_number], json_rpc_named_arguments)
assert InternalTransaction.Fetcher.init(
assert InternalTransaction.init(
[],
fn block_number, acc -> [block_number | acc] end,
json_rpc_named_arguments
@ -115,7 +114,7 @@ defmodule Indexer.InternalTransaction.FetcherTest do
test "does not buffer pending transactions", %{json_rpc_named_arguments: json_rpc_named_arguments} do
insert(:transaction)
assert InternalTransaction.Fetcher.init(
assert InternalTransaction.init(
[],
fn hash_string, acc -> [hash_string | acc] end,
json_rpc_named_arguments
@ -133,7 +132,7 @@ defmodule Indexer.InternalTransaction.FetcherTest do
|> insert()
|> with_block(block)
assert InternalTransaction.Fetcher.init(
assert InternalTransaction.init(
[],
fn hash_string, acc -> [hash_string | acc] end,
json_rpc_named_arguments
@ -148,7 +147,7 @@ defmodule Indexer.InternalTransaction.FetcherTest do
|> insert()
|> with_block(internal_transactions_indexed_at: DateTime.utc_now())
assert InternalTransaction.Fetcher.init(
assert InternalTransaction.init(
[],
fn hash_string, acc -> [hash_string | acc] end,
json_rpc_named_arguments
@ -161,7 +160,7 @@ defmodule Indexer.InternalTransaction.FetcherTest do
} do
block = insert(:block)
assert InternalTransaction.Fetcher.init(
assert InternalTransaction.init(
[],
fn block_number, acc -> [block_number | acc] end,
json_rpc_named_arguments
@ -174,7 +173,7 @@ defmodule Indexer.InternalTransaction.FetcherTest do
} do
insert(:block, internal_transactions_indexed_at: DateTime.utc_now())
assert InternalTransaction.Fetcher.init(
assert InternalTransaction.init(
[],
fn block_number, acc -> [block_number | acc] end,
json_rpc_named_arguments
@ -198,7 +197,7 @@ defmodule Indexer.InternalTransaction.FetcherTest do
log =
capture_log(fn ->
InternalTransaction.Fetcher.run(
InternalTransaction.run(
[
{1, bytes, 0},
{1, bytes, 0}
@ -276,7 +275,7 @@ defmodule Indexer.InternalTransaction.FetcherTest do
|> with_block()
:ok =
InternalTransaction.Fetcher.run(
InternalTransaction.run(
[
{7_202_692, bytes, 0}
],
@ -305,7 +304,7 @@ defmodule Indexer.InternalTransaction.FetcherTest do
%Transaction{hash: %Hash{bytes: bytes}} =
insert(:transaction, hash: "0x0000000000000000000000000000000000000000000000000000000000000001")
assert InternalTransaction.Fetcher.run(
assert InternalTransaction.run(
[
{1, bytes, 0},
{1, bytes, 0}

@ -1,4 +1,4 @@
defmodule Indexer.PendingTransactionFetcherTest do
defmodule Indexer.Fetcher.PendingTransactionTest do
# `async: false` due to use of named GenServer
use EthereumJSONRPC.Case, async: false
use Explorer.DataCase
@ -6,7 +6,7 @@ defmodule Indexer.PendingTransactionFetcherTest do
import Mox
alias Explorer.Chain.Transaction
alias Indexer.PendingTransaction
alias Indexer.Fetcher.PendingTransaction
# MUST use global mode because we aren't guaranteed to get PendingTransactionFetcher's pid back fast enough to `allow`
# it to use expectations and stubs from test's pid.

@ -1,8 +1,8 @@
defmodule Indexer.ReplacedTransaction.FetcherTest do
defmodule Indexer.Fetcher.ReplacedTransactionTest do
use Explorer.DataCase
alias Explorer.Chain.{Transaction}
alias Indexer.ReplacedTransaction
alias Indexer.Fetcher.ReplacedTransaction
@moduletag :capture_log
@ -71,7 +71,7 @@ defmodule Indexer.ReplacedTransaction.FetcherTest do
ReplacedTransaction.Supervisor.Case.start_supervised!()
assert :ok =
ReplacedTransaction.Fetcher.async_fetch([
ReplacedTransaction.async_fetch([
%{
block_hash: mined_transaction.block_hash,
nonce: mined_transaction.nonce,
@ -160,7 +160,7 @@ defmodule Indexer.ReplacedTransaction.FetcherTest do
ReplacedTransaction.Supervisor.Case.start_supervised!()
# assert :ok =
# ReplacedTransaction.Fetcher.async_fetch([
# ReplacedTransaction.async_fetch([
# %{
# block_hash: mined_transaction.block_hash,
# nonce: mined_transaction.nonce,

@ -1,11 +1,11 @@
defmodule Indexer.TokenBalance.FetcherTest do
defmodule Indexer.Fetcher.TokenBalanceTest do
use EthereumJSONRPC.Case
use Explorer.DataCase
import Mox
alias Explorer.Chain.{Address, Hash}
alias Indexer.TokenBalance
alias Indexer.Fetcher.TokenBalance
@moduletag :capture_log
@ -22,7 +22,7 @@ defmodule Indexer.TokenBalance.FetcherTest do
insert(:token_balance, value_fetched_at: DateTime.utc_now())
assert TokenBalance.Fetcher.init([], &[&1 | &2], nil) == [
assert TokenBalance.init([], &[&1 | &2], nil) == [
{address_hash_bytes, token_contract_address_hash_bytes, block_number, 0}
]
end
@ -57,7 +57,7 @@ defmodule Indexer.TokenBalance.FetcherTest do
end
)
assert TokenBalance.Fetcher.run(
assert TokenBalance.run(
[{address_hash_bytes, token_contract_address_hash_bytes, block_number, 0}],
nil
) == :ok
@ -109,13 +109,13 @@ defmodule Indexer.TokenBalance.FetcherTest do
}
]
assert TokenBalance.Fetcher.run(token_balances, nil) == :ok
assert TokenBalance.run(token_balances, nil) == :ok
end
end
describe "import_token_balances/1" do
test "ignores when it receives a empty list" do
assert TokenBalance.Fetcher.import_token_balances([]) == :ok
assert TokenBalance.import_token_balances([]) == :ok
end
test "returns :error when the token balances has invalid data" do
@ -131,7 +131,7 @@ defmodule Indexer.TokenBalance.FetcherTest do
}
]
assert TokenBalance.Fetcher.import_token_balances(token_balances_params) == :error
assert TokenBalance.import_token_balances(token_balances_params) == :error
end
test "insert the missing address, import the token balances and return :ok when the address does not exist yet" do
@ -147,7 +147,7 @@ defmodule Indexer.TokenBalance.FetcherTest do
]
{:ok, address_hash} = Explorer.Chain.string_to_address_hash("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
assert TokenBalance.Fetcher.import_token_balances(token_balances_params) == :ok
assert TokenBalance.import_token_balances(token_balances_params) == :ok
assert {:ok, _} = Explorer.Chain.hash_to_address(address_hash)
end
@ -169,7 +169,7 @@ defmodule Indexer.TokenBalance.FetcherTest do
}
]
assert TokenBalance.Fetcher.import_token_balances(token_balances_params) == :ok
assert TokenBalance.import_token_balances(token_balances_params) == :ok
end
end
end

@ -1,4 +1,4 @@
defmodule Indexer.Token.FetcherTest do
defmodule Indexer.Fetcher.TokenTest do
use EthereumJSONRPC.Case
use Explorer.DataCase
@ -6,7 +6,7 @@ defmodule Indexer.Token.FetcherTest do
alias Explorer.Chain
alias Explorer.Chain.Token
alias Indexer.Token.Fetcher
alias Indexer.Fetcher.Token, as: TokenFetcher
setup :verify_on_exit!
@ -15,7 +15,7 @@ defmodule Indexer.Token.FetcherTest do
insert(:token, cataloged: true)
%Token{contract_address_hash: uncatalog_address} = insert(:token, cataloged: false)
assert Fetcher.init([], &[&1 | &2], json_rpc_named_arguments) == [uncatalog_address]
assert TokenFetcher.init([], &[&1 | &2], json_rpc_named_arguments) == [uncatalog_address]
end
end
@ -61,7 +61,7 @@ defmodule Indexer.Token.FetcherTest do
end
)
assert Fetcher.run([contract_address_hash], json_rpc_named_arguments) == :ok
assert TokenFetcher.run([contract_address_hash], json_rpc_named_arguments) == :ok
expected_supply = Decimal.new(1_000_000_000_000_000_000)

@ -1,11 +1,11 @@
defmodule Indexer.Token.MetadataUpdaterTest do
defmodule Indexer.Fetcher.TokenUpdaterTest do
use Explorer.DataCase
import Mox
alias Explorer.Chain
alias Explorer.Chain.Token
alias Indexer.Token.MetadataUpdater
alias Indexer.Fetcher.TokenUpdater
setup :verify_on_exit!
setup :set_mox_global
@ -49,7 +49,7 @@ defmodule Indexer.Token.MetadataUpdaterTest do
end
)
pid = start_supervised!({MetadataUpdater, %{update_interval: 1}})
pid = start_supervised!({TokenUpdater, [%{update_interval: 1}, []]})
wait_for_results(fn ->
updated = Repo.one!(from(t in Token, where: t.cataloged == true and not is_nil(t.name), limit: 1))
@ -102,7 +102,7 @@ defmodule Indexer.Token.MetadataUpdaterTest do
end
)
MetadataUpdater.update_metadata([token.contract_address_hash])
TokenUpdater.update_metadata([token.contract_address_hash])
expected_supply = Decimal.new(1_000_000_000_000_000_000)

@ -1,4 +1,4 @@
defmodule Indexer.Block.Uncle.FetcherTest do
defmodule Indexer.Fetcher.UncleBlockTest do
# MUST be `async: false` so that {:shared, pid} is set for connection to allow CoinBalanceFetcher's self-send to have
# connection allowed immediately.
use EthereumJSONRPC.Case, async: false
@ -6,6 +6,7 @@ defmodule Indexer.Block.Uncle.FetcherTest do
alias Explorer.Chain
alias Indexer.Block
alias Indexer.Fetcher.UncleBlock
import Mox
@ -32,10 +33,10 @@ defmodule Indexer.Block.Uncle.FetcherTest do
describe "child_spec/1" do
test "raises ArgumentError is `json_rpc_named_arguments is not provided" do
assert_raise ArgumentError,
":json_rpc_named_arguments must be provided to `Elixir.Indexer.Block.Uncle.Fetcher.child_spec " <>
":json_rpc_named_arguments must be provided to `Elixir.Indexer.Fetcher.UncleBlock.child_spec " <>
"to allow for json_rpc calls when running.",
fn ->
start_supervised({Block.Uncle.Fetcher, [[], []]})
start_supervised({UncleBlock, [[], []]})
end
end
end
@ -104,7 +105,7 @@ defmodule Indexer.Block.Uncle.FetcherTest do
]}
end)
Block.Uncle.Supervisor.Case.start_supervised!(
UncleBlock.Supervisor.Case.start_supervised!(
block_fetcher: %Block.Fetcher{json_rpc_named_arguments: json_rpc_named_arguments}
)

@ -8,11 +8,10 @@ defmodule Indexer.Temporary.AddressesWithoutCodeTest do
alias Explorer.Repo
alias Explorer.Chain.{Address, Transaction}
alias Indexer.Temporary.AddressesWithoutCode.Supervisor
alias Indexer.CoinBalance
alias Indexer.Block.Fetcher
alias Indexer.Block.Realtime.Fetcher, as: RealtimeFetcher
alias Indexer.{CoinBalance, Code, InternalTransaction, ReplacedTransaction, Token, TokenBalance}
alias Indexer.Fetcher.{CoinBalance, ContractCode, InternalTransaction, ReplacedTransaction, Token, TokenBalance}
alias Indexer.Temporary.AddressesWithoutCode.Supervisor
@moduletag capture_log: true
@ -23,7 +22,7 @@ defmodule Indexer.Temporary.AddressesWithoutCodeTest do
describe "run/1" do
setup %{json_rpc_named_arguments: json_rpc_named_arguments} do
CoinBalance.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
Code.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
ContractCode.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
InternalTransaction.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
Token.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)
TokenBalance.Supervisor.Case.start_supervised!(json_rpc_named_arguments: json_rpc_named_arguments)

@ -8,8 +8,8 @@ defmodule Indexer.Temporary.FailedCreatedAddressesTest do
alias Explorer.Repo
alias Explorer.Chain.Address
alias Indexer.Fetcher.CoinBalance
alias Indexer.Temporary.FailedCreatedAddresses.Supervisor
alias Indexer.CoinBalance
@moduletag capture_log: true

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save