Ensure min and max block number is for consensus blocks

Adding `WHERE consensus = true` both fixes the bug that
`min_block_number` and `max_block_number` were including non-consensus
blocks, but it also makes the query faster because there is an index
for consensus blocks.

Times from Eth Mainnet production:

```
ether=> SELECT min(b0."number") FROM "blocks" AS b0;
 min
-----
   0
(1 row)

Time: 414.218 ms
ether=> EXPLAIN SELECT min(b0."number") FROM "blocks" AS b0;
                                          QUERY PLAN
-----------------------------------------------------------------------------------------------
 Finalize Aggregate  (cost=223454.95..223454.96 rows=1 width=8)
   ->  Gather  (cost=223454.73..223454.95 rows=2 width=8)
         Workers Planned: 2
         ->  Partial Aggregate  (cost=222454.73..222454.74 rows=1 width=8)
               ->  Parallel Seq Scan on blocks b0  (cost=0.00..215051.99 rows=2961099 width=8)
(5 rows)

Time: 1.040 ms
ether=> EXPLAIN SELECT min(b0."number") FROM "blocks" AS b0 WHERE b0.consensus = true;
                                                         QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------
 Result  (cost=0.45..0.46 rows=1 width=8)
   InitPlan 1 (returns $0)
     ->  Limit  (cost=0.43..0.45 rows=1 width=8)
           ->  Index Only Scan using one_consensus_block_at_height on blocks b0  (cost=0.43..158050.90 rows=7045520 width=8)
                 Index Cond: (number IS NOT NULL)
(5 rows)

Time: 3.350 ms
ether=> SELECT min(b0."number") FROM "blocks" AS b0 WHERE b0.consensus = true;
 min
-----
   0
(1 row)

Time: 1.059 ms
```
pull/1332/head
Luke Imhoff 6 years ago
parent 44f50b161d
commit 083e5d3278
  1. 2
      apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/logs_controller.ex
  2. 2
      apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/transaction_controller.ex
  3. 2
      apps/block_scout_web/lib/block_scout_web/controllers/block_transaction_controller.ex
  4. 2
      apps/block_scout_web/lib/block_scout_web/controllers/transaction_internal_transaction_controller.ex
  5. 2
      apps/block_scout_web/lib/block_scout_web/controllers/transaction_log_controller.ex
  6. 2
      apps/block_scout_web/lib/block_scout_web/controllers/transaction_token_transfer_controller.ex
  7. 54
      apps/explorer/lib/explorer/chain.ex
  8. 2
      apps/explorer/lib/explorer/chain/supply/proof_of_authority.ex
  9. 4
      apps/explorer/lib/explorer/etherscan.ex
  10. 2
      apps/explorer/test/explorer/chain_test.exs
  11. 4
      apps/explorer/test/explorer/etherscan_test.exs
  12. 2
      apps/indexer/lib/indexer.ex

@ -183,7 +183,7 @@ defmodule BlockScoutWeb.API.RPC.LogsController do
defp to_block_number(params, param_key) do defp to_block_number(params, param_key) do
case params[param_key] do case params[param_key] do
"latest" -> "latest" ->
Chain.max_block_number() Chain.consensus_block_number(:max)
_ -> _ ->
to_integer(params, param_key) to_integer(params, param_key)

@ -83,7 +83,7 @@ defmodule BlockScoutWeb.API.RPC.TransactionController do
end end
defp max_block_number do defp max_block_number do
case Chain.max_block_number() do case Chain.consensus_block_number(:max) do
{:ok, number} -> number {:ok, number} -> number
{:error, :not_found} -> 0 {:error, :not_found} -> 0
end end

@ -82,7 +82,7 @@ defmodule BlockScoutWeb.BlockTransactionController do
defp block_above_tip?("0x" <> _), do: nil defp block_above_tip?("0x" <> _), do: nil
defp block_above_tip?(block_hash_or_number) when is_binary(block_hash_or_number) do defp block_above_tip?(block_hash_or_number) when is_binary(block_hash_or_number) do
with {:ok, max_block_number} <- Chain.max_block_number() do with {:ok, max_block_number} <- Chain.consensus_block_number(:max) do
{block_number, _} = Integer.parse(block_hash_or_number) {block_number, _} = Integer.parse(block_hash_or_number)
block_number > max_block_number block_number > max_block_number
else else

@ -65,7 +65,7 @@ defmodule BlockScoutWeb.TransactionInternalTransactionController do
end end
defp max_block_number do defp max_block_number do
case Chain.max_block_number() do case Chain.consensus_block_number(:max) do
{:ok, number} -> number {:ok, number} -> number
{:error, :not_found} -> 0 {:error, :not_found} -> 0
end end

@ -61,7 +61,7 @@ defmodule BlockScoutWeb.TransactionLogController do
end end
defp max_block_number do defp max_block_number do
case Chain.max_block_number() do case Chain.consensus_block_number(:max) do
{:ok, number} -> number {:ok, number} -> number
{:error, :not_found} -> 0 {:error, :not_found} -> 0
end end

@ -65,7 +65,7 @@ defmodule BlockScoutWeb.TransactionTokenTransferController do
end end
defp max_block_number do defp max_block_number do
case Chain.max_block_number() do case Chain.consensus_block_number(:max) do
{:ok, number} -> number {:ok, number} -> number
{:error, :not_found} -> 0 {:error, :not_found} -> 0
end end

@ -845,8 +845,8 @@ defmodule Explorer.Chain do
""" """
@spec indexed_ratio() :: float() @spec indexed_ratio() :: float()
def indexed_ratio do def indexed_ratio do
with {:ok, min_block_number} <- min_block_number(), with {:ok, min_block_number} <- consensus_block_number(:min),
{:ok, max_block_number} <- max_block_number() do {:ok, max_block_number} <- consensus_block_number(:max) do
indexed_blocks = max_block_number - min_block_number + 1 indexed_blocks = max_block_number - min_block_number + 1
indexed_blocks / (max_block_number + 1) indexed_blocks / (max_block_number + 1)
else else
@ -1156,48 +1156,40 @@ defmodule Explorer.Chain do
end end
@doc """ @doc """
The maximum `t:Explorer.Chain.Block.t/0` `number` Aggregate of consensus block numbers.
If blocks are skipped and inserted out of number order, the max number is still returned If blocks are skipped and inserted out of number order, the `:max` and `:min` numbers are still returned
iex> insert(:block, number: 2) iex> insert(:block, number: 2)
iex> insert(:block, number: 1) iex> insert(:block, number: 1)
iex> Explorer.Chain.max_block_number() iex> Explorer.Chain.consensus_block_number(:min)
{:ok, 1}
iex> Explorer.Chain.consensus_block_number(:max)
{:ok, 2} {:ok, 2}
If there are no blocks, `{:error, :not_found}` is returned Non-consensus blocks are ignored
iex> Explorer.Chain.max_block_number()
{:error, :not_found}
"""
@spec max_block_number() :: {:ok, Block.block_number()} | {:error, :not_found}
def max_block_number do
case Repo.aggregate(Block, :max, :number) do
nil -> {:error, :not_found}
number -> {:ok, number}
end
end
@doc """
The minimum `t:Explorer.Chain.Block.t/0` `number` (used to show loading status while indexing)
If blocks are skipped and inserted out of number order, the min number is still returned
iex> insert(:block, number: 2) iex> insert(:block, number: 3, consensus: false)
iex> insert(:block, number: 1) iex> insert(:block, number: 2, consensus: true)
iex> Explorer.Chain.min_block_number() iex> insert(:block, number: 1, consensus: false)
{:ok, 1} iex> Explorer.Chain.consensus_block_number(:min)
{:ok, 2}
iex> Explorer.Chain.consensus_block_number(:max)
{:ok, 2}
If there are no blocks, `{:error, :not_found}` is returned If there are no blocks, `{:error, :not_found}` is returned
iex> Explorer.Chain.min_block_number() iex> Explorer.Chain.consensus_block_number(:min)
{:error, :not_found}
iex> Explorer.Chain.consensus_block_number(:max)
{:error, :not_found} {:error, :not_found}
""" """
@spec min_block_number() :: {:ok, Block.block_number()} | {:error, :not_found} def consensus_block_number(aggregate) do
def min_block_number do Block
case Repo.aggregate(Block, :min, :number) do |> where(consensus: true)
|> Repo.aggregate(aggregate, :number)
|> case do
nil -> {:error, :not_found} nil -> {:error, :not_found}
number -> {:ok, number} number -> {:ok, number}
end end

@ -41,7 +41,7 @@ defmodule Explorer.Chain.Supply.ProofOfAuthority do
end end
defp block_height do defp block_height do
case Chain.max_block_number() do case Chain.consensus_block_number(:max) do
{:ok, height} -> height {:ok, height} -> height
_ -> 0 _ -> 0
end end

@ -43,7 +43,7 @@ defmodule Explorer.Etherscan do
%Hash{byte_count: unquote(Hash.Address.byte_count())} = address_hash, %Hash{byte_count: unquote(Hash.Address.byte_count())} = address_hash,
options \\ @default_options options \\ @default_options
) do ) do
case Chain.max_block_number() do case Chain.consensus_block_number(:max) do
{:ok, max_block_number} -> {:ok, max_block_number} ->
merged_options = Map.merge(@default_options, options) merged_options = Map.merge(@default_options, options)
list_transactions(address_hash, max_block_number, merged_options) list_transactions(address_hash, max_block_number, merged_options)
@ -152,7 +152,7 @@ defmodule Explorer.Etherscan do
contract_address_hash, contract_address_hash,
options \\ @default_options options \\ @default_options
) do ) do
case Chain.max_block_number() do case Chain.consensus_block_number(:max) do
{:ok, max_block_number} -> {:ok, max_block_number} ->
merged_options = Map.merge(@default_options, options) merged_options = Map.merge(@default_options, options)
list_token_transfers(address_hash, contract_address_hash, max_block_number, merged_options) list_token_transfers(address_hash, contract_address_hash, max_block_number, merged_options)

@ -594,7 +594,7 @@ defmodule Explorer.ChainTest do
describe "confirmations/1" do describe "confirmations/1" do
test "with block.number == max_block_number " do test "with block.number == max_block_number " do
block = insert(:block) block = insert(:block)
{:ok, max_block_number} = Chain.max_block_number() {:ok, max_block_number} = Chain.consensus_block_number(:max)
assert block.number == max_block_number assert block.number == max_block_number
assert Chain.confirmations(block, max_block_number: max_block_number) == 0 assert Chain.confirmations(block, max_block_number: max_block_number) == 0

@ -122,7 +122,7 @@ defmodule Explorer.EtherscanTest do
[found_transaction] = Etherscan.list_transactions(address.hash) [found_transaction] = Etherscan.list_transactions(address.hash)
{:ok, max_block_number} = Chain.max_block_number() {:ok, max_block_number} = Chain.consensus_block_number(:max)
expected_confirmations = max_block_number - transaction.block_number expected_confirmations = max_block_number - transaction.block_number
assert found_transaction.confirmations == expected_confirmations assert found_transaction.confirmations == expected_confirmations
@ -888,7 +888,7 @@ defmodule Explorer.EtherscanTest do
[found_token_transfer] = Etherscan.list_token_transfers(token_transfer.from_address_hash, nil) [found_token_transfer] = Etherscan.list_token_transfers(token_transfer.from_address_hash, nil)
{:ok, max_block_number} = Chain.max_block_number() {:ok, max_block_number} = Chain.consensus_block_number(:max)
expected_confirmations = max_block_number - transaction.block_number expected_confirmations = max_block_number - transaction.block_number
assert found_token_transfer.confirmations == expected_confirmations assert found_token_transfer.confirmations == expected_confirmations

@ -22,7 +22,7 @@ defmodule Indexer do
""" """
def max_block_number do def max_block_number do
case Chain.max_block_number() do case Chain.consensus_block_number(:max) do
{:ok, number} -> number {:ok, number} -> number
{:error, :not_found} -> 0 {:error, :not_found} -> 0
end end

Loading…
Cancel
Save