test fix, comments and small refactorings

pull/2888/head
pasqu4le 5 years ago committed by Ayrat Badykov
parent 6a933d514f
commit 9f08c1c373
No known key found for this signature in database
GPG Key ID: B44668E265E9396F
  1. 6
      apps/block_scout_web/test/block_scout_web/controllers/address_read_contract_controller_test.exs
  2. 2
      apps/block_scout_web/test/block_scout_web/features/viewing_app_test.exs
  3. 44
      apps/explorer/lib/explorer/chain.ex
  4. 1
      apps/explorer/lib/explorer/chain/import/runner/blocks.ex
  5. 38
      apps/explorer/lib/explorer/chain/import/runner/internal_transactions.ex
  6. 6
      apps/explorer/lib/explorer/chain/import/runner/transactions.ex
  7. 5
      apps/explorer/lib/explorer/chain/import/stage/block_pending.ex
  8. 19
      apps/explorer/lib/explorer/chain/internal_transaction.ex
  9. 3
      apps/indexer/lib/indexer/fetcher/internal_transaction.ex

@ -44,13 +44,15 @@ defmodule BlockScoutWeb.AddressReadContractControllerTest do
test "returns not found for an unverified contract", %{conn: conn} do test "returns not found for an unverified contract", %{conn: conn} do
contract_address = insert(:contract_address) contract_address = insert(:contract_address)
transaction = insert(:transaction, from_address: contract_address) transaction = insert(:transaction, from_address: contract_address) |> with_block()
insert( insert(
:internal_transaction_create, :internal_transaction_create,
index: 0, index: 0,
transaction: transaction, transaction: transaction,
created_contract_address: contract_address created_contract_address: contract_address,
block_hash: transaction.block_hash,
block_index: 0
) )
conn = get(conn, address_read_contract_path(BlockScoutWeb.Endpoint, :index, contract_address.hash)) conn = get(conn, address_read_contract_path(BlockScoutWeb.Endpoint, :index, contract_address.hash))

@ -7,7 +7,7 @@ defmodule BlockScoutWeb.ViewingAppTest do
alias BlockScoutWeb.Counters.BlocksIndexedCounter alias BlockScoutWeb.Counters.BlocksIndexedCounter
alias Explorer.Counters.AddressesCounter alias Explorer.Counters.AddressesCounter
alias Explorer.{Repo} alias Explorer.{Repo}
alias Explorer.Chain.{PendingBlockOperation, Transaction} alias Explorer.Chain.PendingBlockOperation
setup do setup do
start_supervised!(AddressesCounter) start_supervised!(AddressesCounter)

@ -724,29 +724,20 @@ defmodule Explorer.Chain do
""" """
@spec finished_indexing?() :: boolean() @spec finished_indexing?() :: boolean()
def finished_indexing? do def finished_indexing? do
transaction_exists = with {:transactions_exist, true} <- {:transactions_exist, Repo.exists?(Transaction)},
Transaction min_block_number when not is_nil(min_block_number) <- Repo.aggregate(Transaction, :min, :block_number) do
|> limit(1) query =
|> Repo.one() from(
b in Block,
min_block_number_transaction = Repo.aggregate(Transaction, :min, :block_number) join: pending_ops in assoc(b, :pending_operations),
where: pending_ops.fetch_internal_transactions,
if transaction_exists do where: b.consensus and b.number == ^min_block_number
if min_block_number_transaction do )
query =
from(
b in Block,
join: pending_ops in assoc(b, :pending_operations),
where: pending_ops.fetch_internal_transactions,
where: b.consensus and b.number == ^min_block_number_transaction
)
!Repo.exists?(query) !Repo.exists?(query)
else
false
end
else else
true {:transactions_exist, false} -> true
nil -> false
end end
end end
@ -1338,10 +1329,7 @@ defmodule Explorer.Chain do
@doc """ @doc """
The number of `t:Explorer.Chain.InternalTransaction.t/0`. The number of `t:Explorer.Chain.InternalTransaction.t/0`.
iex> transaction = iex> transaction = :transaction |> insert() |> with_block()
...> :transaction |>
...> insert() |>
...> with_block()
iex> insert(:internal_transaction, index: 0, transaction: transaction, block_hash: transaction.block_hash, block_index: 0) iex> insert(:internal_transaction, index: 0, transaction: transaction, block_hash: transaction.block_hash, block_index: 0)
iex> Explorer.Chain.internal_transaction_count() iex> Explorer.Chain.internal_transaction_count()
1 1
@ -1353,8 +1341,7 @@ defmodule Explorer.Chain do
""" """
def internal_transaction_count do def internal_transaction_count do
InternalTransaction.where_nonpending_block() Repo.aggregate(InternalTransaction.where_nonpending_block(), :count, :transaction_hash)
|> Repo.aggregate(:count, :transaction_hash)
end end
@doc """ @doc """
@ -1630,7 +1617,8 @@ defmodule Explorer.Chain do
end end
@doc """ @doc """
Returns a stream of all blocks with unfetched internal transactions. Returns a stream of all blocks with unfetched internal transactions, using
the `pending_block_operation` table.
Only blocks with consensus are returned. Only blocks with consensus are returned.

@ -317,7 +317,6 @@ defmodule Explorer.Chain.Import.Runner.Blocks do
nonconsensus_hashes nonconsensus_hashes
|> MapSet.new() |> MapSet.new()
|> MapSet.union(MapSet.new(hashes)) |> MapSet.union(MapSet.new(hashes))
|> MapSet.to_list()
|> Enum.sort() |> Enum.sort()
|> Enum.map(fn hash -> |> Enum.map(fn hash ->
%{block_hash: hash, fetch_internal_transactions: true} %{block_hash: hash, fetch_internal_transactions: true}

@ -18,10 +18,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
# milliseconds # milliseconds
@timeout 60_000 @timeout 60_000
@type imported :: [ @type imported :: [InternalTransaction.t()]
%{required(:index) => non_neg_integer(), required(:transaction_hash) => Hash.Full.t()}
| %{required(:empty_block_number) => non_neg_integer()}
]
@impl Runner @impl Runner
def ecto_schema_module, do: InternalTransaction def ecto_schema_module, do: InternalTransaction
@ -50,7 +47,8 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
update_transactions_options = %{timeout: transactions_timeout, timestamps: timestamps} update_transactions_options = %{timeout: transactions_timeout, timestamps: timestamps}
internal_transactions_changes = Enum.filter(changes_list, &Map.has_key?(&1, :type)) # filter out params with just `block_number` (indicating blocks without internal transactions)
internal_transactions_params = Enum.filter(changes_list, &Map.has_key?(&1, :type))
# Enforce ShareLocks tables order (see docs: sharelocks.md) # Enforce ShareLocks tables order (see docs: sharelocks.md)
multi multi
@ -64,14 +62,14 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
acquire_transactions(repo, pending_block_hashes) acquire_transactions(repo, pending_block_hashes)
end) end)
|> Multi.run(:invalid_block_numbers, fn _, %{acquire_transactions: transactions} -> |> Multi.run(:invalid_block_numbers, fn _, %{acquire_transactions: transactions} ->
invalid_block_numbers(transactions, internal_transactions_changes) invalid_block_numbers(transactions, internal_transactions_params)
end) end)
|> Multi.run(:valid_internal_transactions, fn _, |> Multi.run(:valid_internal_transactions, fn _,
%{ %{
acquire_transactions: transactions, acquire_transactions: transactions,
invalid_block_numbers: invalid_block_numbers invalid_block_numbers: invalid_block_numbers
} -> } ->
valid_internal_transactions(transactions, internal_transactions_changes, invalid_block_numbers) valid_internal_transactions(transactions, internal_transactions_params, invalid_block_numbers)
end) end)
|> Multi.run(:internal_transactions, fn repo, %{valid_internal_transactions: valid_internal_transactions} -> |> Multi.run(:internal_transactions, fn repo, %{valid_internal_transactions: valid_internal_transactions} ->
insert(repo, valid_internal_transactions, insert_options) insert(repo, valid_internal_transactions, insert_options)
@ -201,6 +199,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
from( from(
pending_ops in PendingBlockOperation, pending_ops in PendingBlockOperation,
where: pending_ops.block_hash in ^block_hashes, where: pending_ops.block_hash in ^block_hashes,
where: pending_ops.fetch_internal_transactions,
select: pending_ops.block_hash, select: pending_ops.block_hash,
# Enforce PendingBlockOperation ShareLocks order (see docs: sharelocks.md) # Enforce PendingBlockOperation ShareLocks order (see docs: sharelocks.md)
order_by: [asc: pending_ops.block_hash], order_by: [asc: pending_ops.block_hash],
@ -224,10 +223,17 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
{:ok, repo.all(query)} {:ok, repo.all(query)}
end end
defp invalid_block_numbers(transactions, internal_transactions_changes) do defp invalid_block_numbers(transactions, internal_transactions_params) do
# Finds all mistmatches between transactions and internal transactions
# for a block number:
# - there are no internal txs for some transactions
# - there are no transactions for some internal transactions
# - there are internal txs with a different block number than their transactions
# Returns block numbers where any of these issues is found
required_tuples = MapSet.new(transactions, &{&1.hash, &1.block_number}) required_tuples = MapSet.new(transactions, &{&1.hash, &1.block_number})
candidate_tuples = MapSet.new(internal_transactions_changes, &{&1.transaction_hash, &1.block_number}) candidate_tuples = MapSet.new(internal_transactions_params, &{&1.transaction_hash, &1.block_number})
all_tuples = MapSet.union(required_tuples, candidate_tuples) all_tuples = MapSet.union(required_tuples, candidate_tuples)
@ -242,11 +248,11 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
{:ok, invalid_numbers} {:ok, invalid_numbers}
end end
defp valid_internal_transactions(transactions, internal_transactions_changes, invalid_block_numbers) do defp valid_internal_transactions(transactions, internal_transactions_params, invalid_block_numbers) do
blocks_map = Map.new(transactions, &{&1.block_number, &1.block_hash}) blocks_map = Map.new(transactions, &{&1.block_number, &1.block_hash})
valid_internal_txs = valid_internal_txs =
internal_transactions_changes internal_transactions_params
|> Enum.group_by(& &1.block_number) |> Enum.group_by(& &1.block_number)
|> Map.drop(invalid_block_numbers) |> Map.drop(invalid_block_numbers)
|> Enum.flat_map(fn {block_number, entries} -> |> Enum.flat_map(fn {block_number, entries} ->
@ -266,13 +272,17 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
end end
def defer_internal_transactions_primary_key(repo) do def defer_internal_transactions_primary_key(repo) do
# allows internal_transactions primary key to skips being checked during the # Allows internal_transactions primary key to not be checked during the
# DB transactions and instead be checked at the end of it. # DB transactions and instead be checked only at the end of it.
# This allows us to use a more efficient upserting logic # This allows us to use a more efficient upserting logic, while keeping the
# uniqueness valid.
SQL.query(repo, "SET CONSTRAINTS internal_transactions_pkey DEFERRED") SQL.query(repo, "SET CONSTRAINTS internal_transactions_pkey DEFERRED")
end end
def remove_left_over_internal_transactions(repo, valid_internal_transactions) do def remove_left_over_internal_transactions(repo, valid_internal_transactions) do
# Removes internal transactions that were part of a block before a refetch
# and have not been upserted with new ones (if any exist).
case valid_internal_transactions do case valid_internal_transactions do
[] -> [] ->
{:ok, []} {:ok, []}

@ -78,10 +78,8 @@ defmodule Explorer.Chain.Import.Runner.Transactions do
when is_list(changes_list) do when is_list(changes_list) do
on_conflict = Map.get_lazy(options, :on_conflict, &default_on_conflict/0) on_conflict = Map.get_lazy(options, :on_conflict, &default_on_conflict/0)
ordered_changes_list = # Enforce Transaction ShareLocks order (see docs: sharelocks.md)
changes_list ordered_changes_list = Enum.sort_by(changes_list, & &1.hash)
# Enforce Transaction ShareLocks order (see docs: sharelocks.md)
|> Enum.sort_by(& &1.hash)
Import.insert_changes_list( Import.insert_changes_list(
repo, repo,

@ -1,7 +1,8 @@
defmodule Explorer.Chain.Import.Stage.BlockPending do defmodule Explorer.Chain.Import.Stage.BlockPending do
@moduledoc """ @moduledoc """
Imports any tables that follows and cannot be imported at the same time as Imports any tables that uses `Explorer.Chain.PendingBlockOperation` to track
those imported by `Explorer.Chain.Import.Stage.Addresses`, progress and cannot be imported at the same time as those imported by
`Explorer.Chain.Import.Stage.Addresses`,
`Explorer.Chain.Import.Stage.AddressReferencing` and `Explorer.Chain.Import.Stage.AddressReferencing` and
`Explorer.Chain.Import.Stage.BlockReferencing` `Explorer.Chain.Import.Stage.BlockReferencing`
""" """

@ -22,11 +22,15 @@ defmodule Explorer.Chain.InternalTransaction do
* `to_address` - the sink of the `value` * `to_address` - the sink of the `value`
* `to_address_hash` - hash of the sink of the `value` * `to_address_hash` - hash of the sink of the `value`
* `trace_address` - list of traces * `trace_address` - list of traces
* `transaction` - transaction in which this transaction occurred * `transaction` - transaction in which this internal transaction occurred
* `transaction_hash` - foreign key for `transaction` * `transaction_hash` - foreign key for `transaction`
* `transaction_index` - the `t:Explorer.Chain.Transaction.t/0` `index` of `transaction` in `block_number`. * `transaction_index` - the `t:Explorer.Chain.Transaction.t/0` `index` of `transaction` in `block_number`.
* `type` - type of internal transaction * `type` - type of internal transaction
* `value` - value of transferred from `from_address` to `to_address` * `value` - value of transferred from `from_address` to `to_address`
* `block` - block in which this internal transaction occurred
* `block_hash` - foreign key for `block`
* `block_index` - the index of this internal transaction inside the `block`
* `pending_block` - `nil` if `block` has all its internal transactions fetched
""" """
@type t :: %__MODULE__{ @type t :: %__MODULE__{
block_number: Explorer.Chain.Block.block_number() | nil, block_number: Explorer.Chain.Block.block_number() | nil,
@ -409,17 +413,12 @@ defmodule Explorer.Chain.InternalTransaction do
on its own or use empty types to know that a block has no internal transactions. on its own or use empty types to know that a block has no internal transactions.
""" """
def blockless_changeset(%__MODULE__{} = internal_transaction, attrs \\ %{}) do def blockless_changeset(%__MODULE__{} = internal_transaction, attrs \\ %{}) do
changeset = changeset = cast(internal_transaction, attrs, ~w(type block_number)a)
internal_transaction
|> cast(attrs, ~w(type block_number)a)
if is_nil(get_field(changeset, :type)) do if validate_required(changeset, ~w(type)a).valid? do
changeset type_changeset(changeset, attrs)
|> validate_required(~w(block_number)a)
else else
changeset validate_required(changeset, ~w(block_number)a)
|> validate_required(~w(type)a)
|> type_changeset(attrs)
end end
end end

@ -145,7 +145,6 @@ defmodule Indexer.Fetcher.InternalTransaction do
end end
defp import_internal_transaction(internal_transactions_params, unique_numbers) do defp import_internal_transaction(internal_transactions_params, unique_numbers) do
unique_numbers_count = Enum.count(unique_numbers)
internal_transactions_params_without_failed_creations = remove_failed_creations(internal_transactions_params) internal_transactions_params_without_failed_creations = remove_failed_creations(internal_transactions_params)
addresses_params = addresses_params =
@ -192,7 +191,7 @@ defmodule Indexer.Fetcher.InternalTransaction do
] ]
end, end,
step: step, step: step,
error_count: unique_numbers_count error_count: Enum.count(unique_numbers)
) )
# re-queue the de-duped entries # re-queue the de-duped entries

Loading…
Cancel
Save