Block consensus and timestamp in transaction table

v6.0.0-dev
Viktor Baranov 3 years ago
parent 1d6c25ddb8
commit 9a42fc88d0
  1. 6
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs
  2. 34
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/blocks.ex
  3. 13
      apps/explorer/lib/explorer/chain.ex
  4. 0
      apps/explorer/lib/explorer/chain/address_internal_transaction_csv_exporter.ex
  5. 0
      apps/explorer/lib/explorer/chain/address_token_transfer_csv_exporter.ex
  6. 0
      apps/explorer/lib/explorer/chain/address_transaction_csv_exporter.ex
  7. 13
      apps/explorer/lib/explorer/chain/import/runner/blocks.ex
  8. 27
      apps/explorer/lib/explorer/chain/import/runner/internal_transactions.ex
  9. 47
      apps/explorer/lib/explorer/chain/import/runner/transactions.ex
  10. 10
      apps/explorer/lib/explorer/chain/transaction.ex
  11. 23
      apps/explorer/lib/explorer/chain/transaction/history/historian.ex
  12. 69
      apps/explorer/lib/explorer/etherscan.ex
  13. 45
      apps/explorer/lib/explorer/etherscan/logs.ex
  14. 19
      apps/explorer/priv/repo/migrations/20220315082902_add_consensus_to_transaction_table.exs
  15. 18
      apps/explorer/priv/repo/migrations/20220315093927_add_block_timestamp_to_transaction_table.exs
  16. 2
      apps/explorer/test/explorer/chain/csv_export/address_token_transfer_csv_exporter_test.exs
  17. 30
      apps/explorer/test/explorer/chain_test.exs
  18. 7
      apps/explorer/test/explorer/etherscan/logs_test.exs
  19. 17
      apps/explorer/test/explorer/etherscan_test.exs
  20. 3
      apps/explorer/test/support/factory.ex
  21. 2
      apps/indexer/test/indexer/block/catchup/fetcher_test.exs
  22. 2
      apps/indexer/test/indexer/fetcher/internal_transaction_test.exs
  23. 2
      apps/indexer/test/indexer/fetcher/uncle_block_test.exs

@ -1246,7 +1246,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
for block <- Enum.concat([blocks1, blocks2, blocks3]) do
2
|> insert_list(:transaction, from_address: address)
|> insert_list(:transaction, from_address: address, block_timestamp: block.timestamp)
|> with_block(block)
end
@ -1294,7 +1294,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
for block <- Enum.concat([blocks1, blocks2, blocks3]) do
2
|> insert_list(:transaction, from_address: address)
|> insert_list(:transaction, from_address: address, block_timestamp: block.timestamp)
|> with_block(block)
end
@ -1342,7 +1342,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do
for block <- Enum.concat([blocks1, blocks2, blocks3]) do
2
|> insert_list(:transaction, from_address: address)
|> insert_list(:transaction, from_address: address, block_timestamp: block.timestamp)
|> with_block(block)
end

@ -54,12 +54,13 @@ defmodule EthereumJSONRPC.Blocks do
transactions_params = Transactions.elixir_to_params(elixir_transactions)
withdrawals_params = Withdrawals.elixir_to_params(elixir_withdrawals)
blocks_params = elixir_to_params(elixir_blocks)
transactions_params_with_block_timestamp = add_timestamp_to_transactions_params(transactions_params, blocks_params)
%__MODULE__{
errors: errors,
blocks_params: blocks_params,
block_second_degree_relations_params: block_second_degree_relations_params,
transactions_params: transactions_params,
transactions_params: transactions_params_with_block_timestamp,
withdrawals_params: withdrawals_params
}
end
@ -454,4 +455,35 @@ defmodule EthereumJSONRPC.Blocks do
def to_elixir(blocks) when is_list(blocks) do
Enum.map(blocks, &Block.to_elixir/1)
end
defp add_timestamp_to_transactions_params(transactions_params, blocks_params) do
block_hashes =
transactions_params
|> Enum.map(fn %{block_hash: block_hash} -> block_hash end)
|> Enum.uniq()
block_hash_timestamp_map =
block_hashes
|> Enum.map(fn block_hash ->
block =
Enum.find(blocks_params, fn block_param ->
block_param.hash == block_hash
end)
%{}
|> Map.put("#{block_hash}", block.timestamp)
end)
|> Enum.reduce(%{}, fn hash_timestamp_map_item, acc ->
Map.merge(acc, hash_timestamp_map_item)
end)
transactions_params
|> Enum.map(fn transactions_param ->
Map.put(
transactions_param,
:block_timestamp,
Map.get(block_hash_timestamp_map, "#{transactions_param.block_hash}")
)
end)
end
end

@ -522,15 +522,18 @@ defmodule Explorer.Chain do
def gas_payment_by_block_hash(block_hashes) when is_list(block_hashes) do
query =
from(
block in Block,
left_join: transaction in assoc(block, :transactions),
where: block.hash in ^block_hashes and block.consensus == true,
group_by: block.hash,
select: {block.hash, %Wei{value: coalesce(sum(transaction.gas_used * transaction.gas_price), 0)}}
transaction in Transaction,
where: transaction.block_hash in ^block_hashes and transaction.block_consensus == true,
group_by: transaction.block_hash,
select: {transaction.block_hash, %Wei{value: coalesce(sum(transaction.gas_used * transaction.gas_price), 0)}}
)
query
|> Repo.all()
|> (&if(Enum.count(&1) > 0,
do: &1,
else: Enum.zip([block_hashes, for(_ <- 1..Enum.count(block_hashes), do: %Wei{value: Decimal.new(0)})])
)).()
|> Enum.into(%{})
end

@ -395,6 +395,19 @@ defmodule Explorer.Chain.Import.Runner.Blocks do
timeout: timeout
)
repo.update_all(
from(
transaction in Transaction,
join: s in subquery(acquire_query),
on: transaction.block_hash == s.hash,
# we don't want to remove consensus from blocks that will be upserted
where: transaction.block_hash not in ^hashes,
select: transaction.block_hash
),
[set: [block_consensus: false, updated_at: updated_at]],
timeout: timeout
)
removed_consensus_block_hashes
|> Enum.map(fn {number, _hash} -> number end)
|> Enum.reject(&Enum.member?(consensus_block_numbers, &1))

@ -693,7 +693,7 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
defp remove_consensus_of_invalid_blocks(repo, invalid_block_numbers) do
if Enum.count(invalid_block_numbers) > 0 do
update_query =
update_block_query =
from(
block in Block,
where: block.number in ^invalid_block_numbers and block.consensus == true,
@ -703,8 +703,18 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
update: [set: [consensus: false]]
)
update_transaction_query =
from(
transaction in Transaction,
where: transaction.block_number in ^invalid_block_numbers and transaction.block_consensus,
where: ^traceable_transactions_dynamic_query(),
# ShareLocks order already enforced by `acquire_blocks` (see docs: sharelocks.md)
update: [set: [block_consensus: false]]
)
try do
{_num, result} = repo.update_all(update_query, [])
{_num, result} = repo.update_all(update_block_query, [])
{_num, _result} = repo.update_all(update_transaction_query, [])
MissingRangesManipulator.add_ranges_by_block_numbers(invalid_block_numbers)
@ -762,4 +772,17 @@ defmodule Explorer.Chain.Import.Runner.InternalTransactions do
dynamic([_], true)
end
end
defp traceable_transactions_dynamic_query do
if RangesHelper.trace_ranges_present?() do
block_ranges = RangesHelper.get_trace_block_ranges()
Enum.reduce(block_ranges, dynamic([_], false), fn
_from.._to = range, acc -> dynamic([transaction], ^acc or transaction.block_number in ^range)
num_to_latest, acc -> dynamic([transaction], ^acc or transaction.block_number >= ^num_to_latest)
end)
else
dynamic([_], true)
end
end
end

@ -116,6 +116,8 @@ defmodule Explorer.Chain.Import.Runner.Transactions do
block_hash: fragment("EXCLUDED.block_hash"),
old_block_hash: transaction.block_hash,
block_number: fragment("EXCLUDED.block_number"),
block_consensus: fragment("EXCLUDED.block_consensus"),
block_timestamp: fragment("EXCLUDED.block_timestamp"),
created_contract_address_hash: fragment("EXCLUDED.created_contract_address_hash"),
created_contract_code_indexed_at: fragment("EXCLUDED.created_contract_code_indexed_at"),
cumulative_gas_used: fragment("EXCLUDED.cumulative_gas_used"),
@ -159,9 +161,11 @@ defmodule Explorer.Chain.Import.Runner.Transactions do
],
where:
fragment(
"(EXCLUDED.block_hash, EXCLUDED.block_number, EXCLUDED.created_contract_address_hash, EXCLUDED.created_contract_code_indexed_at, EXCLUDED.cumulative_gas_used, EXCLUDED.from_address_hash, EXCLUDED.gas, EXCLUDED.gas_price, EXCLUDED.gas_used, EXCLUDED.index, EXCLUDED.input, EXCLUDED.nonce, EXCLUDED.r, EXCLUDED.s, EXCLUDED.status, EXCLUDED.to_address_hash, EXCLUDED.v, EXCLUDED.value, EXCLUDED.earliest_processing_start, EXCLUDED.revert_reason, EXCLUDED.max_priority_fee_per_gas, EXCLUDED.max_fee_per_gas, EXCLUDED.type, EXCLUDED.execution_node_hash, EXCLUDED.wrapped_type, EXCLUDED.wrapped_nonce, EXCLUDED.wrapped_to_address_hash, EXCLUDED.wrapped_gas, EXCLUDED.wrapped_gas_price, EXCLUDED.wrapped_max_priority_fee_per_gas, EXCLUDED.wrapped_max_fee_per_gas, EXCLUDED.wrapped_value, EXCLUDED.wrapped_input, EXCLUDED.wrapped_v, EXCLUDED.wrapped_r, EXCLUDED.wrapped_s, EXCLUDED.wrapped_hash) IS DISTINCT FROM (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
"(EXCLUDED.block_hash, EXCLUDED.block_number, EXCLUDED.block_consensus, EXCLUDED.block_timestamp, EXCLUDED.created_contract_address_hash, EXCLUDED.created_contract_code_indexed_at, EXCLUDED.cumulative_gas_used, EXCLUDED.from_address_hash, EXCLUDED.gas, EXCLUDED.gas_price, EXCLUDED.gas_used, EXCLUDED.index, EXCLUDED.input, EXCLUDED.nonce, EXCLUDED.r, EXCLUDED.s, EXCLUDED.status, EXCLUDED.to_address_hash, EXCLUDED.v, EXCLUDED.value, EXCLUDED.earliest_processing_start, EXCLUDED.revert_reason, EXCLUDED.max_priority_fee_per_gas, EXCLUDED.max_fee_per_gas, EXCLUDED.type, EXCLUDED.execution_node_hash, EXCLUDED.wrapped_type, EXCLUDED.wrapped_nonce, EXCLUDED.wrapped_to_address_hash, EXCLUDED.wrapped_gas, EXCLUDED.wrapped_gas_price, EXCLUDED.wrapped_max_priority_fee_per_gas, EXCLUDED.wrapped_max_fee_per_gas, EXCLUDED.wrapped_value, EXCLUDED.wrapped_input, EXCLUDED.wrapped_v, EXCLUDED.wrapped_r, EXCLUDED.wrapped_s, EXCLUDED.wrapped_hash) IS DISTINCT FROM (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
transaction.block_hash,
transaction.block_number,
transaction.block_consensus,
transaction.block_timestamp,
transaction.created_contract_address_hash,
transaction.created_contract_code_indexed_at,
transaction.cumulative_gas_used,
@ -207,6 +211,8 @@ defmodule Explorer.Chain.Import.Runner.Transactions do
block_hash: fragment("EXCLUDED.block_hash"),
old_block_hash: transaction.block_hash,
block_number: fragment("EXCLUDED.block_number"),
block_consensus: fragment("EXCLUDED.block_consensus"),
block_timestamp: fragment("EXCLUDED.block_timestamp"),
created_contract_address_hash: fragment("EXCLUDED.created_contract_address_hash"),
created_contract_code_indexed_at: fragment("EXCLUDED.created_contract_code_indexed_at"),
cumulative_gas_used: fragment("EXCLUDED.cumulative_gas_used"),
@ -236,9 +242,11 @@ defmodule Explorer.Chain.Import.Runner.Transactions do
],
where:
fragment(
"(EXCLUDED.block_hash, EXCLUDED.block_number, EXCLUDED.created_contract_address_hash, EXCLUDED.created_contract_code_indexed_at, EXCLUDED.cumulative_gas_used, EXCLUDED.from_address_hash, EXCLUDED.gas, EXCLUDED.gas_price, EXCLUDED.gas_used, EXCLUDED.index, EXCLUDED.input, EXCLUDED.nonce, EXCLUDED.r, EXCLUDED.s, EXCLUDED.status, EXCLUDED.to_address_hash, EXCLUDED.v, EXCLUDED.value, EXCLUDED.earliest_processing_start, EXCLUDED.revert_reason, EXCLUDED.max_priority_fee_per_gas, EXCLUDED.max_fee_per_gas, EXCLUDED.type) IS DISTINCT FROM (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
"(EXCLUDED.block_hash, EXCLUDED.block_number, EXCLUDED.block_consensus, EXCLUDED.block_timestamp, EXCLUDED.created_contract_address_hash, EXCLUDED.created_contract_code_indexed_at, EXCLUDED.cumulative_gas_used, EXCLUDED.from_address_hash, EXCLUDED.gas, EXCLUDED.gas_price, EXCLUDED.gas_used, EXCLUDED.index, EXCLUDED.input, EXCLUDED.nonce, EXCLUDED.r, EXCLUDED.s, EXCLUDED.status, EXCLUDED.to_address_hash, EXCLUDED.v, EXCLUDED.value, EXCLUDED.earliest_processing_start, EXCLUDED.revert_reason, EXCLUDED.max_priority_fee_per_gas, EXCLUDED.max_fee_per_gas, EXCLUDED.type) IS DISTINCT FROM (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
transaction.block_hash,
transaction.block_number,
transaction.block_consensus,
transaction.block_timestamp,
transaction.created_contract_address_hash,
transaction.created_contract_code_indexed_at,
transaction.cumulative_gas_used,
@ -291,14 +299,20 @@ defmodule Explorer.Chain.Import.Runner.Transactions do
),
on: transaction.hash == new_transaction.hash,
where: transaction.block_hash != new_transaction.block_hash,
select: transaction.block_hash
select: %{hash: transaction.hash, block_hash: transaction.block_hash}
)
block_hashes =
blocks_with_recollated_transactions
|> repo.all()
|> Enum.map(fn %{block_hash: block_hash} -> block_hash end)
|> Enum.uniq()
transaction_hashes =
blocks_with_recollated_transactions
|> repo.all()
|> Enum.map(fn %{hash: hash} -> hash end)
if Enum.empty?(block_hashes) do
{:ok, []}
else
@ -357,5 +371,32 @@ defmodule Explorer.Chain.Import.Runner.Transactions do
{:error, %{exception: postgrex_error, block_hashes: block_hashes}}
end
end
if Enum.empty?(transaction_hashes) do
{:ok, []}
else
query =
from(
transaction in Transaction,
where: transaction.hash in ^transaction_hashes,
# Enforce Block ShareLocks order (see docs: sharelocks.md)
order_by: [asc: transaction.hash],
lock: "FOR UPDATE"
)
try do
{_, result} =
repo.update_all(
from(transaction in Transaction, join: s in subquery(query), on: transaction.hash == s.hash),
[set: [block_consensus: false, updated_at: updated_at]],
timeout: timeout
)
{:ok, result}
rescue
postgrex_error in Postgrex.Error ->
{:error, %{exception: postgrex_error, transaction_hashes: transaction_hashes}}
end
end
end
end

@ -36,7 +36,7 @@ defmodule Explorer.Chain.Transaction do
alias Explorer.{PagingOptions, SortingHelper}
alias Explorer.SmartContract.SigProviderInterface
@optional_attrs ~w(max_priority_fee_per_gas max_fee_per_gas block_hash block_number created_contract_address_hash cumulative_gas_used earliest_processing_start
@optional_attrs ~w(max_priority_fee_per_gas max_fee_per_gas block_hash block_number block_consensus block_timestamp created_contract_address_hash cumulative_gas_used earliest_processing_start
error gas_price gas_used index created_contract_code_indexed_at status to_address_hash revert_reason type has_error_in_internal_txs)a
@suave_optional_attrs ~w(execution_node_hash wrapped_type wrapped_nonce wrapped_to_address_hash wrapped_gas wrapped_gas_price wrapped_max_priority_fee_per_gas wrapped_max_fee_per_gas wrapped_value wrapped_input wrapped_v wrapped_r wrapped_s wrapped_hash)a
@ -91,6 +91,8 @@ defmodule Explorer.Chain.Transaction do
`uncles` in one of the `forks`.
* `block_number` - Denormalized `block` `number`. `nil` when transaction is pending or has only been collated into
one of the `uncles` in one of the `forks`.
* `block_consensus` - consensus of the block where transaction collated.
* `block_timestamp` - timestamp of the block where transaction collated.
* `created_contract_address` - belongs_to association to `address` corresponding to `created_contract_address_hash`.
* `created_contract_address_hash` - Denormalized `internal_transaction` `created_contract_address_hash`
populated only when `to_address_hash` is nil.
@ -169,6 +171,8 @@ defmodule Explorer.Chain.Transaction do
block: %Ecto.Association.NotLoaded{} | Block.t() | nil,
block_hash: Hash.t() | nil,
block_number: Block.block_number() | nil,
block_consensus: boolean(),
block_timestamp: DateTime.t() | nil,
created_contract_address: %Ecto.Association.NotLoaded{} | Address.t() | nil,
created_contract_address_hash: Hash.Address.t() | nil,
created_contract_code_indexed_at: DateTime.t() | nil,
@ -232,6 +236,7 @@ defmodule Explorer.Chain.Transaction do
@derive {Poison.Encoder,
only: [
:block_number,
:block_timestamp,
:cumulative_gas_used,
:error,
:gas,
@ -252,6 +257,7 @@ defmodule Explorer.Chain.Transaction do
@derive {Jason.Encoder,
only: [
:block_number,
:block_timestamp,
:cumulative_gas_used,
:error,
:gas,
@ -272,6 +278,8 @@ defmodule Explorer.Chain.Transaction do
@primary_key {:hash, Hash.Full, autogenerate: false}
schema "transactions" do
field(:block_number, :integer)
field(:block_consensus, :boolean)
field(:block_timestamp, :utc_datetime_usec)
field(:cumulative_gas_used, :decimal)
field(:earliest_processing_start, :utc_datetime_usec)
field(:error, :string)

@ -91,33 +91,18 @@ defmodule Explorer.Chain.Transaction.History.Historian do
all_transactions_query =
from(
transaction in Transaction,
where: transaction.block_number >= ^min_block and transaction.block_number <= ^max_block
)
all_blocks_query =
from(
block in Block,
where: block.consensus == true,
where: block.number >= ^min_block and block.number <= ^max_block,
select: block.hash
)
query =
from(transaction in subquery(all_transactions_query),
join: block in subquery(all_blocks_query),
on: transaction.block_hash == block.hash,
where: transaction.block_number >= ^min_block and transaction.block_number <= ^max_block,
where: transaction.block_consensus == true,
select: transaction
)
num_transactions = Repo.aggregate(query, :count, :hash, timeout: :infinity)
num_transactions = Repo.aggregate(all_transactions_query, :count, :hash, timeout: :infinity)
Logger.info("tx/per day chart: num of transactions #{num_transactions}")
gas_used = Repo.aggregate(query, :sum, :gas_used, timeout: :infinity)
gas_used = Repo.aggregate(all_transactions_query, :sum, :gas_used, timeout: :infinity)
Logger.info("tx/per day chart: total gas used #{gas_used}")
total_fee_query =
from(transaction in subquery(all_transactions_query),
join: block in subquery(all_blocks_query),
on: transaction.block_hash == block.hash,
select: fragment("SUM(? * ?)", transaction.gas_price, transaction.gas_used)
)

@ -103,14 +103,13 @@ defmodule Explorer.Etherscan do
query =
from(
it in InternalTransaction,
inner_join: t in assoc(it, :transaction),
inner_join: b in assoc(t, :block),
inner_join: transaction in assoc(it, :transaction),
where: it.transaction_hash == ^transaction_hash,
limit: 10_000,
select:
merge(map(it, ^@internal_transaction_fields), %{
block_timestamp: b.timestamp,
block_number: b.number
block_timestamp: transaction.block_timestamp,
block_number: transaction.block_number
})
)
@ -158,8 +157,8 @@ defmodule Explorer.Etherscan do
query =
from(
it in InternalTransaction,
inner_join: b in subquery(consensus_blocks),
on: it.block_number == b.number,
inner_join: block in subquery(consensus_blocks),
on: it.block_number == block.number,
order_by: [
{^options.order_by_direction, it.block_number},
{:desc, it.transaction_index},
@ -169,8 +168,8 @@ defmodule Explorer.Etherscan do
offset: ^offset(options),
select:
merge(map(it, ^@internal_transaction_fields), %{
block_timestamp: b.timestamp,
block_number: b.number
block_timestamp: block.timestamp,
block_number: block.number
})
)
@ -214,15 +213,14 @@ defmodule Explorer.Etherscan do
query =
from(
it in InternalTransaction,
inner_join: t in assoc(it, :transaction),
inner_join: b in assoc(t, :block),
order_by: [{^options.order_by_direction, t.block_number}],
inner_join: transaction in assoc(it, :transaction),
order_by: [{^options.order_by_direction, transaction.block_number}],
limit: ^options.page_size,
offset: ^offset(options),
select:
merge(map(it, ^@internal_transaction_fields), %{
block_timestamp: b.timestamp,
block_number: b.number
block_timestamp: transaction.block_timestamp,
block_number: transaction.block_number
})
)
@ -279,14 +277,14 @@ defmodule Explorer.Etherscan do
query =
from(
b in Block,
where: b.miner_hash == ^address_hash,
order_by: [desc: b.number],
block in Block,
where: block.miner_hash == ^address_hash,
order_by: [desc: block.number],
limit: ^merged_options.page_size,
offset: ^offset(merged_options),
select: %{
number: b.number,
timestamp: b.timestamp
number: block.number,
timestamp: block.timestamp
}
)
@ -343,6 +341,8 @@ defmodule Explorer.Etherscan do
@transaction_fields ~w(
block_hash
block_number
block_consensus
block_timestamp
created_contract_address_hash
cumulative_gas_used
from_address_hash
@ -395,21 +395,19 @@ defmodule Explorer.Etherscan do
query =
from(
t in Transaction,
inner_join: b in assoc(t, :block),
order_by: [{^options.order_by_direction, t.block_number}],
limit: ^options.page_size,
offset: ^offset(options),
select:
merge(map(t, ^@transaction_fields), %{
block_timestamp: b.timestamp,
confirmations: fragment("? - ?", ^max_block_number, t.block_number)
})
)
query
|> where_address_match(address_hash, options)
|> where_start_block_match(options)
|> where_end_block_match(options)
|> where_start_transaction_block_match(options)
|> where_end_transaction_block_match(options)
|> where_start_timestamp_match(options)
|> where_end_timestamp_match(options)
|> Repo.replica().all()
@ -471,7 +469,6 @@ defmodule Explorer.Etherscan do
tt in subquery(tt_specific_token_query),
inner_join: t in Transaction,
on: tt.transaction_hash == t.hash and tt.block_number == t.block_number and tt.block_hash == t.block_hash,
inner_join: b in assoc(t, :block),
order_by: [{^options.order_by_direction, tt.block_number}, {^options.order_by_direction, tt.token_log_index}],
select: %{
token_contract_address_hash: tt.token_contract_address_hash,
@ -487,9 +484,9 @@ defmodule Explorer.Etherscan do
transaction_gas_used: t.gas_used,
transaction_cumulative_gas_used: t.cumulative_gas_used,
transaction_input: t.input,
block_hash: b.hash,
block_number: b.number,
block_timestamp: b.timestamp,
block_hash: t.block_hash,
block_number: t.block_number,
block_timestamp: t.block_timestamp,
confirmations: fragment("? - ?", ^block_height, t.block_number),
token_ids: tt.token_ids,
token_name: tt.token_name,
@ -501,8 +498,8 @@ defmodule Explorer.Etherscan do
)
wrapped_query
|> where_start_block_match(options)
|> where_end_block_match(options)
|> where_start_transaction_block_match(options)
|> where_end_transaction_block_match(options)
|> Repo.replica().all()
end
@ -518,16 +515,28 @@ defmodule Explorer.Etherscan do
where(query, [..., block], block.number <= ^end_block)
end
defp where_start_transaction_block_match(query, %{start_block: nil}), do: query
defp where_start_transaction_block_match(query, %{start_block: start_block}) do
where(query, [transaction], transaction.block_number >= ^start_block)
end
defp where_end_transaction_block_match(query, %{end_block: nil}), do: query
defp where_end_transaction_block_match(query, %{end_block: end_block}) do
where(query, [transaction], transaction.block_number <= ^end_block)
end
defp where_start_timestamp_match(query, %{start_timestamp: nil}), do: query
defp where_start_timestamp_match(query, %{start_timestamp: start_timestamp}) do
where(query, [..., block], ^start_timestamp <= block.timestamp)
where(query, [transaction, _block], ^start_timestamp <= transaction.block_timestamp)
end
defp where_end_timestamp_match(query, %{end_timestamp: nil}), do: query
defp where_end_timestamp_match(query, %{end_timestamp: end_timestamp}) do
where(query, [..., block], block.timestamp <= ^end_timestamp)
where(query, [transaction, _block], transaction.block_timestamp <= ^end_timestamp)
end
defp where_contract_address_match(query, nil), do: query

@ -8,7 +8,7 @@ defmodule Explorer.Etherscan.Logs do
import Ecto.Query, only: [from: 2, where: 3, subquery: 1, order_by: 3, union: 2]
alias Explorer.{Chain, Repo}
alias Explorer.Chain.{Block, InternalTransaction, Log, Transaction}
alias Explorer.Chain.{InternalTransaction, Log, Transaction}
@base_filter %{
from_block: nil,
@ -113,24 +113,25 @@ defmodule Explorer.Etherscan.Logs do
gas_price: transaction.gas_price,
gas_used: transaction.gas_used,
transaction_index: transaction.index,
block_number: transaction.block_number
block_hash: transaction.block_hash,
block_number: transaction.block_number,
block_timestamp: transaction.block_timestamp,
block_consensus: transaction.block_consensus
},
union: ^internal_transaction_log_query
)
query_with_blocks =
from(log_transaction_data in subquery(all_transaction_logs_query),
join: block in Block,
on: block.number == log_transaction_data.block_number,
where: log_transaction_data.address_hash == ^address_hash,
order_by: block.number,
order_by: log_transaction_data.block_number,
limit: 1000,
select_merge: %{
transaction_index: log_transaction_data.transaction_index,
block_hash: block.hash,
block_number: block.number,
block_timestamp: block.timestamp,
block_consensus: block.consensus
block_hash: log_transaction_data.block_hash,
block_number: log_transaction_data.block_number,
block_timestamp: log_transaction_data.block_timestamp,
block_consensus: log_transaction_data.block_consensus
}
)
@ -138,8 +139,8 @@ defmodule Explorer.Etherscan.Logs do
if Map.get(filter, :allow_non_consensus) do
query_with_blocks
else
from([_, block] in query_with_blocks,
where: block.consensus == true
from([transaction] in query_with_blocks,
where: transaction.block_consensus == true
)
end
@ -161,18 +162,17 @@ defmodule Explorer.Etherscan.Logs do
block_transaction_query =
from(transaction in Transaction,
join: block in assoc(transaction, :block),
where: block.number >= ^prepared_filter.from_block,
where: block.number <= ^prepared_filter.to_block,
where: transaction.block_number >= ^prepared_filter.from_block,
where: transaction.block_number <= ^prepared_filter.to_block,
select: %{
transaction_hash: transaction.hash,
gas_price: transaction.gas_price,
gas_used: transaction.gas_used,
transaction_index: transaction.index,
block_hash: block.hash,
block_number: block.number,
block_timestamp: block.timestamp,
block_consensus: block.consensus
block_hash: transaction.block_hash,
block_number: transaction.block_number,
block_timestamp: transaction.block_timestamp,
block_consensus: transaction.block_consensus
}
)
@ -180,8 +180,8 @@ defmodule Explorer.Etherscan.Logs do
if Map.get(filter, :allow_non_consensus) do
block_transaction_query
else
from([_, block] in block_transaction_query,
where: block.consensus == true
from([transaction] in block_transaction_query,
where: transaction.block_consensus == true
)
end
@ -272,7 +272,10 @@ defmodule Explorer.Etherscan.Logs do
gas_price: transaction.gas_price,
gas_used: transaction.gas_used,
transaction_index: transaction.index,
block_number: internal_transaction.block_number
block_hash: transaction.block_hash,
block_number: internal_transaction.block_number,
block_timestamp: transaction.block_timestamp,
block_consensus: transaction.block_consensus
})
)

@ -0,0 +1,19 @@
defmodule Explorer.Repo.Migrations.AddConsensusToTransactionTable do
use Ecto.Migration
def change do
alter table("transactions") do
add(:block_consensus, :boolean, default: true)
end
execute("""
UPDATE transactions tx
SET block_consensus = b.consensus
FROM blocks b
WHERE b.hash = tx.block_hash
AND b.consensus = false;
""")
create(index(:transactions, :block_consensus))
end
end

@ -0,0 +1,18 @@
defmodule Explorer.Repo.Migrations.AddBlockTimestampToTransactionTable do
use Ecto.Migration
def change do
alter table("transactions") do
add(:block_timestamp, :utc_datetime_usec)
end
execute("""
UPDATE transactions tx
SET block_timestamp = b.timestamp
FROM blocks b
WHERE b.hash = tx.block_hash;
""")
create(index(:transactions, :block_timestamp))
end
end

@ -70,7 +70,7 @@ defmodule Explorer.Chain.AddressTokenTransferCsvExporterTest do
assert result.tx_hash == to_string(transaction.hash)
assert result.from_address == Address.checksum(token_transfer.from_address_hash)
assert result.to_address == Address.checksum(token_transfer.to_address_hash)
assert result.timestamp == to_string(transaction.block.timestamp)
assert result.timestamp == to_string(transaction.block_timestamp)
assert result.type == "OUT"
end

@ -869,9 +869,7 @@ defmodule Explorer.ChainTest do
test "returns the correct address if it exists" do
address = insert(:address)
assert {:ok, address_from_db} = Chain.hash_to_address(address.hash)
assert address_from_db.hash == address.hash
assert address_from_db.inserted_at == address.inserted_at
assert {:ok, _address} = Chain.hash_to_address(address.hash)
end
test "has_decompiled_code? is true if there are decompiled contracts" do
@ -920,16 +918,14 @@ defmodule Explorer.ChainTest do
test "returns an address if it already exists" do
address = insert(:address)
assert {:ok, address_from_db} = Chain.find_or_insert_address_from_hash(address.hash)
assert address_from_db.hash == address.hash
assert address_from_db.inserted_at == address.inserted_at
assert {:ok, _address} = Chain.find_or_insert_address_from_hash(address.hash)
end
test "returns an address if it doesn't exist" do
hash_str = "0xcbbcd5ac86f9a50e13313633b262e16f695a90c2"
{:ok, hash} = Chain.string_to_address_hash(hash_str)
assert {:ok, %Chain.Address{hash: ^hash}} = Chain.find_or_insert_address_from_hash(hash)
assert {:ok, %Chain.Address{hash: _hash}} = Chain.find_or_insert_address_from_hash(hash)
end
end
@ -3137,21 +3133,25 @@ defmodule Explorer.ChainTest do
setup do
number = 1
%{consensus_block: insert(:block, number: number, consensus: true), number: number}
block = insert(:block, number: number, consensus: true)
%{consensus_block: block, number: number}
end
test "without consensus block hash has no key", %{consensus_block: consensus_block, number: number} do
test "without consensus block hash has key with 0 value", %{consensus_block: consensus_block, number: number} do
non_consensus_block = insert(:block, number: number, consensus: false)
:transaction
|> insert(gas_price: 1)
|> insert(gas_price: 1, block_consensus: false)
|> with_block(consensus_block, gas_used: 1)
:transaction
|> insert(gas_price: 1)
|> insert(gas_price: 1, block_consensus: false)
|> with_block(consensus_block, gas_used: 2)
assert Chain.gas_payment_by_block_hash([non_consensus_block.hash]) == %{}
assert Chain.gas_payment_by_block_hash([non_consensus_block.hash]) == %{
non_consensus_block.hash => %Wei{value: Decimal.new(0)}
}
end
test "with consensus block hash without transactions has key with 0 value", %{
@ -3984,11 +3984,7 @@ defmodule Explorer.ChainTest do
assert {:ok, result} = Chain.token_from_address_hash(token.contract_address_hash, options)
assert address.smart_contract.address_hash == result.contract_address.smart_contract.address_hash
assert address.smart_contract.contract_code_md5 == result.contract_address.smart_contract.contract_code_md5
assert address.smart_contract.abi == result.contract_address.smart_contract.abi
assert address.smart_contract.contract_source_code == result.contract_address.smart_contract.contract_source_code
assert address.smart_contract.name == result.contract_address.smart_contract.name
assert result.contract_address.smart_contract
end
end

@ -38,12 +38,13 @@ defmodule Explorer.Etherscan.LogsTest do
test "with address with one log response includes all required information" do
contract_address = insert(:contract_address)
block = insert(:block)
transaction =
%Transaction{block: block} =
%Transaction{} =
:transaction
|> insert(to_address: contract_address)
|> with_block()
|> insert(to_address: contract_address, block_timestamp: block.timestamp)
|> with_block(block)
log = insert(:log, address: contract_address, transaction: transaction)

@ -159,11 +159,12 @@ defmodule Explorer.EtherscanTest do
test "loads block_timestamp" do
address = insert(:address)
block = insert(:block)
%Transaction{block: block} =
%Transaction{} =
:transaction
|> insert(from_address: address)
|> with_block()
|> insert(from_address: address, block_timestamp: block.timestamp)
|> with_block(block)
[found_transaction] = Etherscan.list_transactions(address.hash)
@ -370,7 +371,7 @@ defmodule Explorer.EtherscanTest do
for block <- Enum.concat([blocks1, blocks2, blocks3]) do
2
|> insert_list(:transaction, from_address: address)
|> insert_list(:transaction, from_address: address, block_timestamp: block.timestamp)
|> with_block(block)
end
@ -629,7 +630,7 @@ defmodule Explorer.EtherscanTest do
transaction =
:transaction
|> insert(from_address: address, to_address: nil)
|> insert(from_address: address, to_address: nil, block_timestamp: block.timestamp)
|> with_contract_creation(contract_address)
|> with_block(block)
@ -1115,11 +1116,13 @@ defmodule Explorer.EtherscanTest do
end
test "returns all required fields" do
block = insert(:block)
transaction =
%{block: block} =
:transaction
|> insert()
|> with_block()
|> insert(block_timestamp: block.timestamp)
|> with_block(block)
token_transfer =
insert(:token_transfer,

@ -807,7 +807,8 @@ defmodule Explorer.Factory do
s: sequence(:transaction_s, & &1),
to_address: build(:address),
v: Enum.random(27..30),
value: Enum.random(1..100_000)
value: Enum.random(1..100_000),
block_timestamp: DateTime.utc_now()
}
end

@ -456,7 +456,7 @@ defmodule Indexer.Block.Catchup.FetcherTest do
assert count(Chain.Block) == 1
assert count(Reward) == 0
assert_receive {:block_numbers, [^block_number]}, 5_000
assert_receive {:block_numbers, [_block_number]}, 5_000
end
test "async fetches beneficiaries when entire call errors out", %{

@ -305,7 +305,7 @@ defmodule Indexer.Fetcher.InternalTransactionTest do
assert {:retry, [block.number]} == InternalTransaction.run([block.number, block.number], json_rpc_named_arguments)
assert %{block_hash: ^block_hash} = Repo.get(PendingBlockOperation, block_hash)
assert %{block_hash: _block_hash} = Repo.get(PendingBlockOperation, block_hash)
end
test "remove block consensus on foreign_key_violation", %{

@ -196,7 +196,7 @@ defmodule Indexer.Fetcher.UncleBlockTest do
]}
end)
assert {:retry, [^entry]} =
assert {:retry, [_entry]} =
UncleBlock.run(entries, %Block.Fetcher{json_rpc_named_arguments: json_rpc_named_arguments})
end
end

Loading…
Cancel
Save