Merge branch 'master' into ab-fix-usd-fee

pull/1613/head
Ayrat Badykov 6 years ago committed by GitHub
commit 0257b3cce2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 26
      CHANGELOG.md
  2. 2
      README.md
  3. 21
      apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/address_controller.ex
  4. 66
      apps/block_scout_web/lib/block_scout_web/etherscan.ex
  5. 12
      apps/block_scout_web/lib/block_scout_web/views/api/rpc/address_view.ex
  6. 43
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs
  7. 3
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/http.ex
  8. 14
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transaction.ex
  9. 3
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/transactions.ex
  10. 92
      apps/ethereum_jsonrpc/test/ethereum_jsonrpc/http/mox_test.exs
  11. 13
      apps/explorer/lib/explorer/chain.ex
  12. 7
      apps/explorer/priv/repo/migrations/20190318151809_add_inserted_at_index_to_accounts.exs
  13. 5
      apps/indexer/config/dev.exs
  14. 5
      apps/indexer/config/prod.exs
  15. 5
      apps/indexer/config/test.exs
  16. 3
      apps/indexer/lib/indexer/address_extraction.ex
  17. 7
      apps/indexer/lib/indexer/block/fetcher.ex
  18. 9
      apps/indexer/lib/indexer/block/fetcher/receipts.ex
  19. 2
      apps/indexer/lib/indexer/block/realtime/consensus_ensurer.ex
  20. 2
      apps/indexer/lib/indexer/block/reward/fetcher.ex
  21. 13
      apps/indexer/lib/indexer/block/supervisor.ex
  22. 103
      apps/indexer/lib/indexer/temporary/addresses_without_code.ex
  23. 38
      apps/indexer/lib/indexer/temporary/addresses_without_code/supervisor.ex
  24. 96
      apps/indexer/test/indexer/block/fetcher_test.exs
  25. 390
      apps/indexer/test/indexer/temporary/addresses_without_code_test.exs
  26. 1
      config/config.exs

@ -4,6 +4,32 @@
### Fixes ### Fixes
### Chore
## 1.3.6-beta
### Features
- [#1589](https://github.com/poanetwork/blockscout/pull/1589) - RPC endpoint to list addresses
- [#1567](https://github.com/poanetwork/blockscout/pull/1567) - Allow setting different configuration just for realtime fetcher
- [#1562](https://github.com/poanetwork/blockscout/pull/1562) - Add incoming transactions count to contract view
### Fixes
- [#1595](https://github.com/poanetwork/blockscout/pull/1595) - Reduce block_rewards in the catchup fetcher
- [#1590](https://github.com/poanetwork/blockscout/pull/1590) - Added guard for fetching blocks with invalid number
- [#1588](https://github.com/poanetwork/blockscout/pull/1588) - Fix usd value on address page
- [#1586](https://github.com/poanetwork/blockscout/pull/1586) - Exact timestamp display
- [#1581](https://github.com/poanetwork/blockscout/pull/1581) - Consider `creates` param when fetching transactions
- [#1559](https://github.com/poanetwork/blockscout/pull/1559) - Change v column type for Transactions table
### Chore
- [#1579](https://github.com/poanetwork/blockscout/pull/1579) - Add SpringChain to the list of Additional Chains Utilizing BlockScout
- [#1578](https://github.com/poanetwork/blockscout/pull/1578) - Refine contributing procedure
- [#1572](https://github.com/poanetwork/blockscout/pull/1572) - Add option to disable block rewards in indexer config
## 1.3.5-beta ## 1.3.5-beta

@ -60,6 +60,8 @@ Currently available block explorers (i.e. Etherscan and Etherchain) are closed s
* [ARTIS](https://explorer.sigma1.artis.network) * [ARTIS](https://explorer.sigma1.artis.network)
* [SafeChain](https://explorer.safechain.io) * [SafeChain](https://explorer.safechain.io)
* [SpringChain](https://explorer.springrole.com/) * [SpringChain](https://explorer.springrole.com/)
* [PIRL](http://pirl.es/)
### Visual Interface ### Visual Interface

@ -4,6 +4,20 @@ defmodule BlockScoutWeb.API.RPC.AddressController do
alias Explorer.{Chain, Etherscan} alias Explorer.{Chain, Etherscan}
alias Explorer.Chain.{Address, Wei} alias Explorer.Chain.{Address, Wei}
def listaccounts(conn, params) do
options =
params
|> optional_params()
|> Map.put_new(:page_number, 0)
|> Map.put_new(:page_size, 10)
accounts = list_accounts(options)
conn
|> put_status(200)
|> render(:listaccounts, %{accounts: accounts})
end
def balance(conn, params, template \\ :balance) do def balance(conn, params, template \\ :balance) do
with {:address_param, {:ok, address_param}} <- fetch_address(params), with {:address_param, {:ok, address_param}} <- fetch_address(params),
{:format, {:ok, address_hashes}} <- to_address_hashes(address_param) do {:format, {:ok, address_hashes}} <- to_address_hashes(address_param) do
@ -260,6 +274,13 @@ defmodule BlockScoutWeb.API.RPC.AddressController do
Enum.any?(address_hashes, &(&1 == :error)) Enum.any?(address_hashes, &(&1 == :error))
end end
defp list_accounts(%{page_number: page_number, page_size: page_size}) do
offset = (max(page_number, 1) - 1) * page_size
# limit is just page_size
Chain.list_ordered_addresses(offset, page_size)
end
defp hashes_to_addresses(address_hashes) do defp hashes_to_addresses(address_hashes) do
address_hashes address_hashes
|> Chain.hashes_to_addresses() |> Chain.hashes_to_addresses()

@ -168,6 +168,17 @@ defmodule BlockScoutWeb.Etherscan do
] ]
} }
@account_listaccounts_example_value %{
"status" => "1",
"message" => "OK",
"result" => [
%{
"address" => "0x0000000000000000000000000000000000000000",
"balance" => "135499"
}
]
}
@account_getminedblocks_example_value_error %{ @account_getminedblocks_example_value_error %{
"status" => "0", "status" => "0",
"message" => "No blocks found", "message" => "No blocks found",
@ -720,6 +731,14 @@ defmodule BlockScoutWeb.Etherscan do
} }
} }
@account_model %{
name: "Account",
fields: %{
"address" => @address_hash_type,
"balance" => @wei_type
}
}
@contract_model %{ @contract_model %{
name: "Contract", name: "Contract",
fields: %{ fields: %{
@ -1289,6 +1308,50 @@ defmodule BlockScoutWeb.Etherscan do
] ]
} }
@account_listaccounts_action %{
name: "listaccounts",
description:
"Get a list of accounts and their balances, sorted ascending by the time they were first seen by the explorer.",
required_params: [],
optional_params: [
%{
key: "page",
type: "integer",
description:
"A nonnegative integer that represents the page number to be used for pagination. 'offset' must be provided in conjunction."
},
%{
key: "offset",
type: "integer",
description:
"A nonnegative integer that represents the maximum number of records to return when paginating. 'page' must be provided in conjunction."
}
],
responses: [
%{
code: "200",
description: "successful operation",
example_value: Jason.encode!(@account_listaccounts_example_value),
model: %{
name: "Result",
fields: %{
status: @status_type,
message: @message_type,
result: %{
type: "array",
array_type: @account_model
}
}
}
},
%{
code: "200",
description: "error",
example_value: Jason.encode!(@account_getminedblocks_example_value_error)
}
]
}
@logs_getlogs_action %{ @logs_getlogs_action %{
name: "getLogs", name: "getLogs",
description: "Get event logs for an address and/or topics. Up to a maximum of 1,000 event logs.", description: "Get event logs for an address and/or topics. Up to a maximum of 1,000 event logs.",
@ -1767,7 +1830,8 @@ defmodule BlockScoutWeb.Etherscan do
@account_tokentx_action, @account_tokentx_action,
@account_tokenbalance_action, @account_tokenbalance_action,
@account_tokenlist_action, @account_tokenlist_action,
@account_getminedblocks_action @account_getminedblocks_action,
@account_listaccounts_action
] ]
} }

@ -3,6 +3,11 @@ defmodule BlockScoutWeb.API.RPC.AddressView do
alias BlockScoutWeb.API.RPC.RPCView alias BlockScoutWeb.API.RPC.RPCView
def render("listaccounts.json", %{accounts: accounts}) do
accounts = Enum.map(accounts, &prepare_account/1)
RPCView.render("show.json", data: accounts)
end
def render("balance.json", %{addresses: [address]}) do def render("balance.json", %{addresses: [address]}) do
RPCView.render("show.json", data: "#{address.fetched_coin_balance.value}") RPCView.render("show.json", data: "#{address.fetched_coin_balance.value}")
end end
@ -56,6 +61,13 @@ defmodule BlockScoutWeb.API.RPC.AddressView do
RPCView.render("error.json", assigns) RPCView.render("error.json", assigns)
end end
defp prepare_account(address) do
%{
"balance" => to_string(address.fetched_coin_balance.value),
"address" => to_string(address.hash)
}
end
defp prepare_transaction(transaction) do defp prepare_transaction(transaction) do
%{ %{
"blockNumber" => "#{transaction.block_number}", "blockNumber" => "#{transaction.block_number}",

@ -6,6 +6,49 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
alias Explorer.Chain.{Transaction, Wei} alias Explorer.Chain.{Transaction, Wei}
alias BlockScoutWeb.API.RPC.AddressController alias BlockScoutWeb.API.RPC.AddressController
describe "listaccounts" do
setup do
%{params: %{"module" => "account", "action" => "listaccounts"}}
end
test "with no addresses", %{params: params, conn: conn} do
response =
conn
|> get("/api", params)
|> json_response(200)
assert response["message"] == "OK"
assert response["status"] == "1"
assert response["result"] == []
end
test "with existing addresses", %{params: params, conn: conn} do
first_address = insert(:address, fetched_coin_balance: 10, inserted_at: Timex.shift(Timex.now(), minutes: -10))
second_address = insert(:address, fetched_coin_balance: 100, inserted_at: Timex.shift(Timex.now(), minutes: -5))
first_address_hash = to_string(first_address.hash)
second_address_hash = to_string(second_address.hash)
response =
conn
|> get("/api", params)
|> json_response(200)
assert response["message"] == "OK"
assert response["status"] == "1"
assert [
%{
"address" => ^first_address_hash,
"balance" => "10"
},
%{
"address" => ^second_address_hash,
"balance" => "100"
}
] = response["result"]
end
end
describe "balance" do describe "balance" do
test "with missing address hash", %{conn: conn} do test "with missing address hash", %{conn: conn} do
params = %{ params = %{

@ -68,6 +68,9 @@ defmodule EthereumJSONRPC.HTTP do
chunked_json_rpc(tail, options, [decoded_body | decoded_response_bodies]) chunked_json_rpc(tail, options, [decoded_body | decoded_response_bodies])
end end
{:error, :timeout} ->
rechunk_json_rpc(chunks, options, :timeout, decoded_response_bodies)
{:error, _} = error -> {:error, _} = error ->
error error
end end

@ -149,7 +149,8 @@ defmodule EthereumJSONRPC.Transaction do
elixir_to_params(%{transaction | "input" => "0x"}) elixir_to_params(%{transaction | "input" => "0x"})
end end
def elixir_to_params(%{ def elixir_to_params(
%{
"blockHash" => block_hash, "blockHash" => block_hash,
"blockNumber" => block_number, "blockNumber" => block_number,
"from" => from_address_hash, "from" => from_address_hash,
@ -164,8 +165,9 @@ defmodule EthereumJSONRPC.Transaction do
"transactionIndex" => index, "transactionIndex" => index,
"v" => v, "v" => v,
"value" => value "value" => value
}) do } = transaction
%{ ) do
result = %{
block_hash: block_hash, block_hash: block_hash,
block_number: block_number, block_number: block_number,
from_address_hash: from_address_hash, from_address_hash: from_address_hash,
@ -182,6 +184,12 @@ defmodule EthereumJSONRPC.Transaction do
value: value, value: value,
transaction_index: index transaction_index: index
} }
if transaction["creates"] do
Map.put(result, :created_contract_address_hash, transaction["creates"])
else
result
end
end end
# Ganache bug. it return `to: "0x0"` except of `to: null` # Ganache bug. it return `to: "0x0"` except of `to: null`

@ -56,7 +56,8 @@ defmodule EthereumJSONRPC.Transactions do
to_address_hash: nil, to_address_hash: nil,
v: "0xbd", v: "0xbd",
value: 0, value: 0,
transaction_index: 0 transaction_index: 0,
created_contract_address_hash: "0xffc87239eb0267bc3ca2cd51d12fbf278e02ccb4"
} }
] ]

@ -189,6 +189,98 @@ defmodule EthereumJSONRPC.HTTP.MoxTest do
assert MapSet.equal?(response_block_number_set, block_number_set) assert MapSet.equal?(response_block_number_set, block_number_set)
end end
@tag :no_geth
# Regression test for https://github.com/poanetwork/blockscout/issues/418
test "transparently splits batch payloads that would trigger a request timeout", %{
json_rpc_named_arguments: json_rpc_named_arguments
} do
block_numbers = [862_272, 862_273, 862_274, 862_275, 862_276, 862_277, 862_278, 862_279, 862_280, 862_281]
if json_rpc_named_arguments[:transport_options][:http] == EthereumJSONRPC.HTTP.Mox do
EthereumJSONRPC.HTTP.Mox
|> expect(:json_rpc, fn _url, _json, _options ->
{:error, :timeout}
end)
|> expect(:json_rpc, fn _url, json, _options ->
json_binary = IO.iodata_to_binary(json)
refute json_binary =~ "0xD2849"
assert json_binary =~ "0xD2844"
body =
0..4
|> Enum.map(fn id ->
%{
jsonrpc: "2.0",
id: id,
result: [
%{
"trace" => [
%{
"type" => "create",
"action" => %{"from" => "0x", "gas" => "0x0", "init" => "0x", "value" => "0x0"},
"traceAddress" => "0x",
"result" => %{"address" => "0x", "code" => "0x", "gasUsed" => "0x0"}
}
],
"transactionHash" => "0x221aaf59f7a05702f0f53744b4fdb5f74e3c6fdade7324fda342cc1ebc73e01c"
}
]
}
end)
|> Jason.encode!()
{:ok, %{body: body, status_code: 200}}
end)
|> expect(:json_rpc, fn _url, json, _options ->
json_binary = IO.iodata_to_binary(json)
refute json_binary =~ "0xD2844"
assert json_binary =~ "0xD2845"
assert json_binary =~ "0xD2849"
body =
5..9
|> Enum.map(fn id ->
%{
jsonrpc: "2.0",
id: id,
result: [
%{
"trace" => [
%{
"type" => "create",
"action" => %{"from" => "0x", "gas" => "0x0", "init" => "0x", "value" => "0x0"},
"traceAddress" => "0x",
"result" => %{"address" => "0x", "code" => "0x", "gasUsed" => "0x0"}
}
],
"transactionHash" => "0x221aaf59f7a05702f0f53744b4fdb5f74e3c6fdade7324fda342cc1ebc73e01c"
}
]
}
end)
|> Jason.encode!()
{:ok, %{body: body, status_code: 200}}
end)
end
assert {:ok, responses} =
EthereumJSONRPC.fetch_block_internal_transactions(block_numbers, json_rpc_named_arguments)
assert Enum.count(responses) == Enum.count(block_numbers)
block_number_set = MapSet.new(block_numbers)
response_block_number_set =
Enum.into(responses, MapSet.new(), fn %{block_number: block_number} ->
block_number
end)
assert MapSet.equal?(response_block_number_set, block_number_set)
end
end end
defp assert_payload_too_large(payload, json_rpc_named_arguments) do defp assert_payload_too_large(payload, json_rpc_named_arguments) do

@ -724,6 +724,19 @@ defmodule Explorer.Chain do
Repo.all(query) Repo.all(query)
end end
@spec list_ordered_addresses(non_neg_integer(), non_neg_integer()) :: [Address.t()]
def list_ordered_addresses(offset, limit) do
query =
from(
address in Address,
order_by: [asc: address.inserted_at],
offset: ^offset,
limit: ^limit
)
Repo.all(query)
end
def find_contract_address(%Hash{byte_count: unquote(Hash.Address.byte_count())} = hash) do def find_contract_address(%Hash{byte_count: unquote(Hash.Address.byte_count())} = hash) do
query = query =
from( from(

@ -0,0 +1,7 @@
defmodule Explorer.Repo.Migrations.AddInsertedAtIndexToAccounts do
use Ecto.Migration
def change do
create(index(:addresses, :inserted_at))
end
end

@ -16,6 +16,11 @@ config :logger, :failed_contract_creations,
path: Path.absname("logs/dev/indexer/failed_contract_creations.log"), path: Path.absname("logs/dev/indexer/failed_contract_creations.log"),
metadata_filter: [fetcher: :failed_created_addresses] metadata_filter: [fetcher: :failed_created_addresses]
config :logger, :addresses_without_code,
level: :debug,
path: Path.absname("logs/dev/indexer/addresses_without_code.log"),
metadata_filter: [fetcher: :addresses_without_code]
variant = variant =
if is_nil(System.get_env("ETHEREUM_JSONRPC_VARIANT")) do if is_nil(System.get_env("ETHEREUM_JSONRPC_VARIANT")) do
"ganache" "ganache"

@ -18,6 +18,11 @@ config :logger, :failed_contract_creations,
path: Path.absname("logs/prod/indexer/failed_contract_creations.log"), path: Path.absname("logs/prod/indexer/failed_contract_creations.log"),
metadata_filter: [fetcher: :failed_created_addresses] metadata_filter: [fetcher: :failed_created_addresses]
config :logger, :addresses_without_code,
level: :debug,
path: Path.absname("logs/prod/indexer/addresses_without_code.log"),
metadata_filter: [fetcher: :addresses_without_code]
variant = variant =
if is_nil(System.get_env("ETHEREUM_JSONRPC_VARIANT")) do if is_nil(System.get_env("ETHEREUM_JSONRPC_VARIANT")) do
"parity" "parity"

@ -15,3 +15,8 @@ config :logger, :failed_contract_creations,
level: :debug, level: :debug,
path: Path.absname("logs/test/indexer/failed_contract_creations.log"), path: Path.absname("logs/test/indexer/failed_contract_creations.log"),
metadata_filter: [fetcher: :failed_created_addresses] metadata_filter: [fetcher: :failed_created_addresses]
config :logger, :addresses_without_code,
level: :debug,
path: Path.absname("logs/test/indexer/addresses_without_code.log"),
metadata_filter: [fetcher: :addresses_without_code]

@ -86,7 +86,8 @@ defmodule Indexer.AddressExtraction do
transactions: [ transactions: [
[ [
%{from: :block_number, to: :fetched_coin_balance_block_number}, %{from: :block_number, to: :fetched_coin_balance_block_number},
%{from: :created_contract_address_hash, to: :hash} %{from: :created_contract_address_hash, to: :hash},
%{from: :input, to: :contract_code}
], ],
[ [
%{from: :block_number, to: :fetched_coin_balance_block_number}, %{from: :block_number, to: :fetched_coin_balance_block_number},

@ -14,7 +14,7 @@ defmodule Indexer.Block.Fetcher do
alias Indexer.{AddressExtraction, CoinBalance, MintTransfer, ReplacedTransaction, Token, TokenTransfers, Tracer} alias Indexer.{AddressExtraction, CoinBalance, MintTransfer, ReplacedTransaction, Token, TokenTransfers, Tracer}
alias Indexer.Address.{CoinBalances, TokenBalances} alias Indexer.Address.{CoinBalances, TokenBalances}
alias Indexer.Block.Fetcher.Receipts alias Indexer.Block.Fetcher.Receipts
alias Indexer.Block.Transform alias Indexer.Block.{Reward, Transform}
@type address_hash_to_fetched_balance_block_number :: %{String.t() => Block.block_number()} @type address_hash_to_fetched_balance_block_number :: %{String.t() => Block.block_number()}
@ -127,7 +127,10 @@ defmodule Indexer.Block.Fetcher do
transactions_params: transactions_with_receipts transactions_params: transactions_with_receipts
} }
|> CoinBalances.params_set(), |> CoinBalances.params_set(),
beneficiaries_with_gas_payment <- add_gas_payments(beneficiary_params_set, transactions_with_receipts), 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}), address_token_balances = TokenBalances.params_set(%{token_transfers_params: token_transfers}),
{:ok, inserted} <- {:ok, inserted} <-
__MODULE__.import( __MODULE__.import(

@ -42,7 +42,14 @@ defmodule Indexer.Block.Fetcher.Receipts do
end) end)
Enum.map(transactions_params, fn %{hash: transaction_hash} = transaction_params -> Enum.map(transactions_params, fn %{hash: transaction_hash} = transaction_params ->
Map.merge(transaction_params, Map.fetch!(transaction_hash_to_receipt_params, transaction_hash)) receipts_params = Map.fetch!(transaction_hash_to_receipt_params, transaction_hash)
merged_params = Map.merge(transaction_params, receipts_params)
if transaction_params[:created_contract_address_hash] && is_nil(receipts_params[:created_contract_address_hash]) do
Map.put(merged_params, :created_contract_address_hash, transaction_params[:created_contract_address_hash])
else
merged_params
end
end) end)
end end

@ -9,6 +9,8 @@ defmodule Indexer.Block.Realtime.ConsensusEnsurer do
alias Explorer.Chain.Hash alias Explorer.Chain.Hash
alias Indexer.Block.Realtime.Fetcher alias Indexer.Block.Realtime.Fetcher
def perform(_, number, _) when not is_integer(number) or number < 0, do: :ok
def perform(%Hash{byte_count: unquote(Hash.Full.byte_count())} = block_hash, number, block_fetcher) do def perform(%Hash{byte_count: unquote(Hash.Full.byte_count())} = block_hash, number, block_fetcher) do
case Chain.hash_to_block(block_hash) do case Chain.hash_to_block(block_hash) do
{:ok, %{consensus: true} = _block} -> {:ok, %{consensus: true} = _block} ->

@ -204,7 +204,7 @@ defmodule Indexer.Block.Reward.Fetcher do
end) end)
end end
defp reduce_uncle_rewards(beneficiaries_params) do def reduce_uncle_rewards(beneficiaries_params) do
beneficiaries_params beneficiaries_params
|> Enum.reduce([], fn %{address_type: address_type} = beneficiary, acc -> |> Enum.reduce([], fn %{address_type: address_type} = beneficiary, acc ->
current = current =

@ -5,7 +5,7 @@ defmodule Indexer.Block.Supervisor do
alias Indexer.Block alias Indexer.Block
alias Indexer.Block.{Catchup, InvalidConsensus, Realtime, Reward, Uncle} alias Indexer.Block.{Catchup, InvalidConsensus, Realtime, Reward, Uncle}
alias Indexer.Temporary.FailedCreatedAddresses alias Indexer.Temporary.{AddressesWithoutCode, FailedCreatedAddresses}
use Supervisor use Supervisor
@ -27,6 +27,12 @@ defmodule Indexer.Block.Supervisor do
|> Map.drop(~w(block_interval memory_monitor subscribe_named_arguments realtime_overrides)a) |> Map.drop(~w(block_interval memory_monitor subscribe_named_arguments realtime_overrides)a)
|> Block.Fetcher.new() |> 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 = realtime_block_fetcher =
named_arguments named_arguments
|> Map.drop(~w(block_interval memory_monitor subscribe_named_arguments realtime_overrides)a) |> Map.drop(~w(block_interval memory_monitor subscribe_named_arguments realtime_overrides)a)
@ -60,6 +66,11 @@ defmodule Indexer.Block.Supervisor do
[ [
json_rpc_named_arguments, json_rpc_named_arguments,
[name: FailedCreatedAddresses.Supervisor] [name: FailedCreatedAddresses.Supervisor]
]},
{AddressesWithoutCode.Supervisor,
[
fixing_realtime_fetcher,
[name: AddressesWithoutCode.Supervisor]
]} ]}
], ],
strategy: :one_for_one strategy: :one_for_one

@ -0,0 +1,103 @@
defmodule Indexer.Temporary.AddressesWithoutCode do
@moduledoc """
Temporary module to fetch contract code for addresses without it.
"""
use GenServer
require Logger
import Ecto.Query
alias Explorer.Chain.{Address, Block, Transaction}
alias Explorer.Repo
alias Indexer.Block.Realtime.Fetcher
alias Indexer.Temporary.AddressesWithoutCode.TaskSupervisor
@task_options [max_concurrency: 3, timeout: :infinity]
@batch_size 500
def start_link([fetcher, gen_server_options]) do
GenServer.start_link(__MODULE__, fetcher, gen_server_options)
end
@impl GenServer
def init(fetcher) do
schedule_work()
{:ok, fetcher}
end
def schedule_work do
Process.send_after(self(), :run, 1_000)
end
@impl GenServer
def handle_info(:run, fetcher) do
run(fetcher)
{:noreply, fetcher}
end
def run(fetcher) do
fix_transaction_without_to_address_and_created_contract_address(fetcher)
fix_addresses_with_creation_transaction_but_without_code(fetcher)
end
def fix_transaction_without_to_address_and_created_contract_address(fetcher) do
query =
from(block in Block,
left_join: transaction in Transaction,
on: block.hash == transaction.block_hash,
where:
is_nil(transaction.to_address_hash) and is_nil(transaction.created_contract_address_hash) and
block.consensus == true and is_nil(transaction.error) and not is_nil(transaction.hash),
distinct: block.hash
)
process_query(query, fetcher)
end
defp process_query(query, fetcher) do
query_stream = Repo.stream(query, max_rows: @batch_size)
stream =
TaskSupervisor
|> Task.Supervisor.async_stream_nolink(
query_stream,
fn block -> refetch_block(block, fetcher) end,
@task_options
)
Repo.transaction(fn -> Stream.run(stream) end)
end
def fix_addresses_with_creation_transaction_but_without_code(fetcher) do
second_query =
from(block in Block,
left_join: transaction in Transaction,
on: transaction.block_hash == block.hash,
left_join: address in Address,
on: address.hash == transaction.created_contract_address_hash,
where:
not is_nil(transaction.block_hash) and not is_nil(transaction.created_contract_address_hash) and
is_nil(address.contract_code) and
block.consensus == true and is_nil(transaction.error) and not is_nil(transaction.hash),
distinct: block.hash
)
process_query(second_query, fetcher)
end
def refetch_block(block, fetcher) do
Fetcher.fetch_and_import_block(block.number, fetcher, false)
rescue
e ->
Logger.debug(
[
"Failed to fetch block #{to_string(block.hash)} #{block.number} because of #{inspect(e)}"
],
fetcher: :addresses_without_code
)
end
end

@ -0,0 +1,38 @@
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

@ -609,6 +609,102 @@ defmodule Indexer.Block.FetcherTest do
raise ArgumentError, "Unsupported variant (#{variant})" raise ArgumentError, "Unsupported variant (#{variant})"
end end
end end
@tag :no_geth
test "correctly imports blocks with multiple uncle rewards for the same address", %{
block_fetcher: %Fetcher{json_rpc_named_arguments: json_rpc_named_arguments} = block_fetcher
} do
block_number = 7_374_455
if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do
EthereumJSONRPC.Mox
|> expect(:json_rpc, 2, fn requests, _options ->
{:ok,
Enum.map(requests, fn
%{id: id, method: "eth_getBlockByNumber", params: ["0x708677", true]} ->
%{
id: id,
result: %{
"author" => "0x5a0b54d5dc17e0aadc383d2db43b0a0d3e029c4c",
"difficulty" => "0x6bc767dd80781",
"extraData" => "0x5050594520737061726b706f6f6c2d6574682d7477",
"gasLimit" => "0x7a121d",
"gasUsed" => "0x79cbe9",
"hash" => "0x1b6fb99af0b51af6685a191b2f7bcba684f8565629bf084c70b2530479407455",
"logsBloom" =>
"0x044d42d008801488400e1809190200a80d06105bc0c4100b047895c0d518327048496108388040140010b8208006288102e206160e21052322440924002090c1c808a0817405ab238086d028211014058e949401012403210314896702d06880c815c3060a0f0809987c81044488292cc11d57882c912a808ca10471c84460460040000c0001012804022000a42106591881d34407420ba401e1c08a8d00a000a34c11821a80222818a4102152c8a0c044032080c6462644223104d618e0e544072008120104408205c60510542264808488220403000106281a0290404220112c10b080145028c8000300b18a2c8280701c882e702210b00410834840108084",
"miner" => "0x5a0b54d5dc17e0aadc383d2db43b0a0d3e029c4c",
"mixHash" => "0xda53ae7c2b3c529783d6cdacdb90587fd70eb651c0f04253e8ff17de97844010",
"nonce" => "0x0946e5f01fce12bc",
"number" => "0x708677",
"parentHash" => "0x62543e836e0ef7edfa9e38f26526092c4be97efdf5ba9e0f53a4b0b7d5bc930a",
"receiptsRoot" => "0xa7d2b82bd8526de11736c18bd5cc8cfe2692106c4364526f3310ad56d78669c4",
"sealFields" => [
"0xa0da53ae7c2b3c529783d6cdacdb90587fd70eb651c0f04253e8ff17de97844010",
"0x880946e5f01fce12bc"
],
"sha3Uncles" => "0x483a8a21a5825ad270f358b3ea56e060bbb8b3082d9a92ec8fa17a5c7e6fc1b6",
"size" => "0x544c",
"stateRoot" => "0x85daa9cd528004c1609d4cb3520fd958e85983bb4183124a4a9f7137fd39c691",
"timestamp" => "0x5c8bc76e",
"totalDifficulty" => "0x201a42c35142ae94458",
"transactions" => [],
"transactionsRoot" => "0xcd6c12fa43cd4e92ad5c0bf232b30488bbcbfe273c5b4af0366fced0767d54db",
"uncles" => []
}
}
%{id: id, method: "trace_block"} ->
%{
id: id,
result: [
%{
"action" => %{
"author" => "0x5a0b54d5dc17e0aadc383d2db43b0a0d3e029c4c",
"rewardType" => "block",
"value" => "0x1d7d843dc3b48000"
},
"blockHash" => "0x1b6fb99af0b51af6685a191b2f7bcba684f8565629bf084c70b2530479407455",
"blockNumber" => block_number,
"subtraces" => 0,
"traceAddress" => [],
"type" => "reward"
},
%{
"action" => %{
"author" => "0xea674fdde714fd979de3edf0f56aa9716b898ec8",
"rewardType" => "uncle",
"value" => "0x14d1120d7b160000"
},
"blockHash" => "0x1b6fb99af0b51af6685a191b2f7bcba684f8565629bf084c70b2530479407455",
"blockNumber" => block_number,
"subtraces" => 0,
"traceAddress" => [],
"type" => "reward"
},
%{
"action" => %{
"author" => "0xea674fdde714fd979de3edf0f56aa9716b898ec8",
"rewardType" => "uncle",
"value" => "0x18493fba64ef0000"
},
"blockHash" => "0x1b6fb99af0b51af6685a191b2f7bcba684f8565629bf084c70b2530479407455",
"blockNumber" => block_number,
"subtraces" => 0,
"traceAddress" => [],
"type" => "reward"
}
]
}
end)}
end)
end
assert {:ok, %{errors: [], inserted: %{block_rewards: block_rewards}}} =
Fetcher.fetch_and_import_range(block_fetcher, block_number..block_number)
assert Repo.one!(select(Chain.Block.Reward, fragment("COUNT(*)"))) == 2
end
end end
defp wait_until(timeout, producer) do defp wait_until(timeout, producer) do

@ -0,0 +1,390 @@
defmodule Indexer.Temporary.AddressesWithoutCodeTest do
use Explorer.DataCase, async: false
use EthereumJSONRPC.Case, async: false
import Mox
import Ecto.Query
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}
@moduletag capture_log: true
setup :set_mox_global
setup :verify_on_exit!
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)
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!()
[name: Indexer.Block.Realtime.TaskSupervisor]
|> Task.Supervisor.child_spec()
|> ExUnit.Callbacks.start_supervised!()
fetcher = %Fetcher{
broadcast: false,
callback_module: RealtimeFetcher,
json_rpc_named_arguments: json_rpc_named_arguments
}
{:ok, %{fetcher: fetcher}}
end
@tag :no_parity
@tag :no_geth
test "refetches blocks setting created address and code", %{
fetcher: %{json_rpc_named_arguments: json_rpc_named_arguments} = fetcher
} do
block = insert(:block, consensus: true)
transaction =
:transaction
|> insert(
status: 0,
to_address: nil,
created_contract_address_hash: nil,
block: block,
block_number: block.number,
block_hash: block.hash,
cumulative_gas_used: 200,
gas_used: 100,
index: 0
)
address = insert(:address, contract_code: nil)
if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do
EthereumJSONRPC.Mox
|> expect(:json_rpc, fn [%{id: id, method: "eth_getBlockByNumber", params: [_block_quantity, true]}],
_options ->
{:ok,
[
%{
id: id,
jsonrpc: "2.0",
result: %{
"author" => "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca",
"difficulty" => "0xfffffffffffffffffffffffffffffffe",
"extraData" => "0xd5830108048650617269747986312e32322e31826c69",
"gasLimit" => "0x69fe20",
"gasUsed" => "0xc512",
"hash" => "0xf6b4b8c88df3ebd252ec476328334dc026cf66606a84fb769b3d3cbccc8471bd",
"logsBloom" =>
"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000200000000000000000000020000000000000000200000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner" => "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca",
"number" => "0x25",
"parentHash" => "0xc37bbad7057945d1bf128c1ff009fb1ad632110bf6a000aac025a80f7766b66e",
"receiptsRoot" => "0xd300311aab7dcc98c05ac3f1893629b2c9082c189a0a0c76f4f63e292ac419d5",
"sealFields" => [
"0x84120a71de",
"0xb841fcdb570511ec61edda93849bb7c6b3232af60feb2ea74e4035f0143ab66dfdd00f67eb3eda1adddbb6b572db1e0abd39ce00f9b3ccacb9f47973279ff306fe5401"
],
"sha3Uncles" => "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"signature" =>
"fcdb570511ec61edda93849bb7c6b3232af60feb2ea74e4035f0143ab66dfdd00f67eb3eda1adddbb6b572db1e0abd39ce00f9b3ccacb9f47973279ff306fe5401",
"size" => "0x2cf",
"stateRoot" => "0x2cd84079b0d0c267ed387e3895fd1c1dc21ff82717beb1132adac64276886e19",
"step" => "302674398",
"timestamp" => "0x5a343956",
"totalDifficulty" => "0x24ffffffffffffffffffffffffedf78dfd",
"transactions" => [
%{
"blockHash" => "0xf6b4b8c88df3ebd252ec476328334dc026cf66606a84fb769b3d3cbccc8471bd",
"blockNumber" => "0x25",
"chainId" => "0x4d",
"condition" => "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca",
"creates" => to_string(address.hash),
"from" => "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca",
"to" => nil,
"gas" => "0x47b760",
"gasPrice" => "0x174876e800",
"hash" => to_string(transaction.hash),
"input" => "0x10855269000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef",
"nonce" => "0x4",
"publicKey" =>
"0xe5d196ad4ceada719d9e592f7166d0c75700f6eab2e3c3de34ba751ea786527cb3f6eb96ad9fdfdb9989ff572df50f1c42ef800af9c5207a38b929aff969b5c9",
"r" => "0xa7f8f45cce375bb7af8750416e1b03e0473f93c256da2285d1134fc97a700e01",
"raw" =>
"0xf88a0485174876e8008347b760948bf38d4764929064f2d4d3a56520a76ab3df415b80a410855269000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef81bea0a7f8f45cce375bb7af8750416e1b03e0473f93c256da2285d1134fc97a700e01a01f87a076f13824f4be8963e3dffd7300dae64d5f23c9a062af0c6ead347c135f",
"s" => "0x1f87a076f13824f4be8963e3dffd7300dae64d5f23c9a062af0c6ead347c135f",
"standardV" => "0x1",
"transactionIndex" => "0x0",
"v" => "0xbe",
"value" => "0x0"
}
],
"transactionsRoot" => "0x68e314a05495f390f9cd0c36267159522e5450d2adf254a74567b452e767bf34",
"uncles" => []
}
}
]}
end)
|> expect(:json_rpc, fn [
%{
id: id,
method: "eth_getTransactionReceipt",
params: _
}
],
_options ->
{:ok,
[
%{
id: id,
jsonrpc: "2.0",
result: %{
"blockHash" => "0xf6b4b8c88df3ebd252ec476328334dc026cf66606a84fb769b3d3cbccc8471bd",
"blockNumber" => "0x25",
"contractAddress" => to_string(address.hash),
"cumulativeGasUsed" => "0xc512",
"gasUsed" => "0xc512",
"logs" => [
%{
"address" => "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca",
"blockHash" => "0xf6b4b8c88df3ebd252ec476328334dc026cf66606a84fb769b3d3cbccc8471bd",
"blockNumber" => "0x25",
"data" => "0x000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef",
"logIndex" => "0x0",
"topics" => ["0x600bcf04a13e752d1e3670a5a9f1c21177ca2a93c6f5391d4f1298d098097c22"],
"transactionHash" => to_string(transaction.hash),
"transactionIndex" => "0x0",
"transactionLogIndex" => "0x0",
"type" => "mined"
}
],
"logsBloom" =>
"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000200000000000000000000020000000000000000200000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"root" => nil,
"status" => "0x1",
"transactionHash" => to_string(transaction.hash),
"transactionIndex" => "0x0"
}
}
]}
end)
|> expect(:json_rpc, fn [%{id: id, method: "trace_block", params: _}], _options ->
{:ok, [%{id: id, result: []}]}
end)
|> expect(:json_rpc, fn [%{id: id, method: "trace_replayBlockTransactions", params: _}], _options ->
{:ok, [%{id: id, result: []}]}
end)
|> expect(:json_rpc, fn [
%{
id: 0,
jsonrpc: "2.0",
method: "eth_getBalance",
params: ["0x0000000000000000000000000000000000000003", "0x25"]
},
%{
id: 1,
jsonrpc: "2.0",
method: "eth_getBalance",
params: ["0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca", "0x25"]
}
],
_options ->
{:ok, [%{id: 0, jsonrpc: "2.0", result: "0x0"}, %{id: 1, jsonrpc: "2.0", result: "0x0"}]}
end)
end
[fetcher, [name: AddressesWithoutCodeTest]]
|> Supervisor.child_spec()
|> ExUnit.Callbacks.start_supervised!()
Process.sleep(5_000)
updated_address =
from(a in Address, where: a.hash == ^address.hash, preload: :contracts_creation_transaction) |> Repo.one()
assert updated_address.contracts_creation_transaction.hash == transaction.hash
updated_transaction =
from(t in Transaction, where: t.hash == ^transaction.hash, preload: :created_contract_address) |> Repo.one()
assert updated_transaction.created_contract_address.hash == address.hash
assert updated_address.contract_code == updated_transaction.input
end
@tag :no_parity
@tag :no_geth
test "doesn't set contract code if contract wasn't create", %{
fetcher: %{json_rpc_named_arguments: json_rpc_named_arguments} = fetcher
} do
block = insert(:block, consensus: true)
transaction =
:transaction
|> insert(
status: 0,
to_address: nil,
created_contract_address_hash: nil,
block: block,
block_number: block.number,
block_hash: block.hash,
cumulative_gas_used: 200,
gas_used: 100,
index: 0
)
address = insert(:address, contract_code: nil)
if json_rpc_named_arguments[:transport] == EthereumJSONRPC.Mox do
EthereumJSONRPC.Mox
|> expect(:json_rpc, fn [%{id: id, method: "eth_getBlockByNumber", params: [_block_quantity, true]}],
_options ->
{:ok,
[
%{
id: id,
jsonrpc: "2.0",
result: %{
"author" => "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca",
"difficulty" => "0xfffffffffffffffffffffffffffffffe",
"extraData" => "0xd5830108048650617269747986312e32322e31826c69",
"gasLimit" => "0x69fe20",
"gasUsed" => "0xc512",
"hash" => "0xf6b4b8c88df3ebd252ec476328334dc026cf66606a84fb769b3d3cbccc8471bd",
"logsBloom" =>
"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000200000000000000000000020000000000000000200000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner" => "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca",
"number" => "0x25",
"parentHash" => "0xc37bbad7057945d1bf128c1ff009fb1ad632110bf6a000aac025a80f7766b66e",
"receiptsRoot" => "0xd300311aab7dcc98c05ac3f1893629b2c9082c189a0a0c76f4f63e292ac419d5",
"sealFields" => [
"0x84120a71de",
"0xb841fcdb570511ec61edda93849bb7c6b3232af60feb2ea74e4035f0143ab66dfdd00f67eb3eda1adddbb6b572db1e0abd39ce00f9b3ccacb9f47973279ff306fe5401"
],
"sha3Uncles" => "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"signature" =>
"fcdb570511ec61edda93849bb7c6b3232af60feb2ea74e4035f0143ab66dfdd00f67eb3eda1adddbb6b572db1e0abd39ce00f9b3ccacb9f47973279ff306fe5401",
"size" => "0x2cf",
"stateRoot" => "0x2cd84079b0d0c267ed387e3895fd1c1dc21ff82717beb1132adac64276886e19",
"step" => "302674398",
"timestamp" => "0x5a343956",
"totalDifficulty" => "0x24ffffffffffffffffffffffffedf78dfd",
"transactions" => [
%{
"blockHash" => "0xf6b4b8c88df3ebd252ec476328334dc026cf66606a84fb769b3d3cbccc8471bd",
"blockNumber" => "0x25",
"chainId" => "0x4d",
"condition" => "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca",
"from" => "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca",
"to" => nil,
"gas" => "0x47b760",
"gasPrice" => "0x174876e800",
"hash" => to_string(transaction.hash),
"input" => "0x10855269000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef",
"nonce" => "0x4",
"publicKey" =>
"0xe5d196ad4ceada719d9e592f7166d0c75700f6eab2e3c3de34ba751ea786527cb3f6eb96ad9fdfdb9989ff572df50f1c42ef800af9c5207a38b929aff969b5c9",
"r" => "0xa7f8f45cce375bb7af8750416e1b03e0473f93c256da2285d1134fc97a700e01",
"raw" =>
"0xf88a0485174876e8008347b760948bf38d4764929064f2d4d3a56520a76ab3df415b80a410855269000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef81bea0a7f8f45cce375bb7af8750416e1b03e0473f93c256da2285d1134fc97a700e01a01f87a076f13824f4be8963e3dffd7300dae64d5f23c9a062af0c6ead347c135f",
"s" => "0x1f87a076f13824f4be8963e3dffd7300dae64d5f23c9a062af0c6ead347c135f",
"standardV" => "0x1",
"transactionIndex" => "0x0",
"v" => "0xbe",
"value" => "0x0"
}
],
"transactionsRoot" => "0x68e314a05495f390f9cd0c36267159522e5450d2adf254a74567b452e767bf34",
"uncles" => []
}
}
]}
end)
|> expect(:json_rpc, fn [
%{
id: id,
method: "eth_getTransactionReceipt",
params: _
}
],
_options ->
{:ok,
[
%{
id: id,
jsonrpc: "2.0",
result: %{
"blockHash" => "0xf6b4b8c88df3ebd252ec476328334dc026cf66606a84fb769b3d3cbccc8471bd",
"blockNumber" => "0x25",
"contractAddress" => nil,
"cumulativeGasUsed" => "0xc512",
"gasUsed" => "0xc512",
"logs" => [
%{
"address" => "0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca",
"blockHash" => "0xf6b4b8c88df3ebd252ec476328334dc026cf66606a84fb769b3d3cbccc8471bd",
"blockNumber" => "0x25",
"data" => "0x000000000000000000000000862d67cb0773ee3f8ce7ea89b328ffea861ab3ef",
"logIndex" => "0x0",
"topics" => ["0x600bcf04a13e752d1e3670a5a9f1c21177ca2a93c6f5391d4f1298d098097c22"],
"transactionHash" => to_string(transaction.hash),
"transactionIndex" => "0x0",
"transactionLogIndex" => "0x0",
"type" => "mined"
}
],
"logsBloom" =>
"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000200000000000000000000020000000000000000200000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"root" => nil,
"status" => "0x1",
"transactionHash" => to_string(transaction.hash),
"transactionIndex" => "0x0"
}
}
]}
end)
|> expect(:json_rpc, fn [%{id: id, method: "trace_block", params: _}], _options ->
{:ok, [%{id: id, result: []}]}
end)
|> expect(:json_rpc, fn [%{id: id, method: "trace_replayBlockTransactions", params: _}], _options ->
{:ok, [%{id: id, result: []}]}
end)
|> expect(:json_rpc, fn [
%{
id: 1,
jsonrpc: "2.0",
method: "eth_getBalance",
params: ["0xe8ddc5c7a2d2f0d7a9798459c0104fdf5e987aca", "0x25"]
}
],
_options ->
{:ok, [%{id: 1, jsonrpc: "2.0", result: "0x0"}]}
end)
end
[fetcher, [name: AddressesWithoutCodeTest1]]
|> Supervisor.child_spec()
|> ExUnit.Callbacks.start_supervised!()
Process.sleep(2_000)
updated_address =
from(a in Address, where: a.hash == ^address.hash, preload: :contracts_creation_transaction) |> Repo.one()
assert is_nil(updated_address.contracts_creation_transaction)
updated_transaction =
from(t in Transaction, where: t.hash == ^transaction.hash, preload: :created_contract_address) |> Repo.one()
assert is_nil(updated_transaction.created_contract_address)
assert is_nil(updated_address.contract_code)
end
end
end

@ -27,6 +27,7 @@ config :logger,
{LoggerFileBackend, :indexer}, {LoggerFileBackend, :indexer},
{LoggerFileBackend, :indexer_token_balances}, {LoggerFileBackend, :indexer_token_balances},
{LoggerFileBackend, :failed_contract_creations}, {LoggerFileBackend, :failed_contract_creations},
{LoggerFileBackend, :addresses_without_code},
{LoggerFileBackend, :reading_token_functions} {LoggerFileBackend, :reading_token_functions}
] ]

Loading…
Cancel
Save