Setup reorg block detail and list pages

pull/870/head
Stamates 6 years ago committed by Ryan Arthur
parent 4f3dec146e
commit 15ea7e3247
  1. 14
      apps/block_scout_web/lib/block_scout_web/controllers/block_controller.ex
  2. 3
      apps/block_scout_web/lib/block_scout_web/controllers/block_transaction_controller.ex
  3. 2
      apps/block_scout_web/lib/block_scout_web/router.ex
  4. 12
      apps/block_scout_web/lib/block_scout_web/templates/block/overview.html.eex
  5. 6
      apps/block_scout_web/lib/block_scout_web/views/block_view.ex
  6. 29
      apps/block_scout_web/test/block_scout_web/controllers/block_controller_test.exs
  7. 4
      apps/block_scout_web/test/block_scout_web/features/pages/block_list_page.ex
  8. 11
      apps/block_scout_web/test/block_scout_web/features/viewing_blocks_test.exs
  9. 29
      apps/block_scout_web/test/block_scout_web/views/block_view_test.exs
  10. 4
      apps/explorer/lib/explorer/chain.ex
  11. 17
      apps/explorer/lib/explorer/chain/block.ex

@ -22,6 +22,20 @@ defmodule BlockScoutWeb.BlockController do
redirect(conn, to: block_transaction_path(conn, :index, hash_or_number)) redirect(conn, to: block_transaction_path(conn, :index, hash_or_number))
end end
def reorg(conn, params) do
Keyword.merge(
[
necessity_by_association: %{
:transactions => :optional,
[miner: :names] => :optional
},
block_type: "Reorg"
],
paging_options(params)
)
|> handle_render(conn, params)
end
def uncle(conn, params) do def uncle(conn, params) do
Keyword.merge( Keyword.merge(
[ [

@ -11,7 +11,8 @@ defmodule BlockScoutWeb.BlockTransactionController do
param_block_hash_or_number_to_block(formatted_block_hash_or_number, param_block_hash_or_number_to_block(formatted_block_hash_or_number,
necessity_by_association: %{ necessity_by_association: %{
[miner: :names] => :required, [miner: :names] => :required,
:uncles => :optional :uncles => :optional,
:nephews => :optional
} }
) do ) do
block_transaction_count = Chain.block_to_transaction_count(block) block_transaction_count = Chain.block_to_transaction_count(block)

@ -46,6 +46,8 @@ defmodule BlockScoutWeb.Router do
resources("/transactions", BlockTransactionController, only: [:index], as: :transaction) resources("/transactions", BlockTransactionController, only: [:index], as: :transaction)
end end
get("/reorgs", BlockController, :reorg, as: :reorg)
get("/uncles", BlockController, :uncle, as: :uncle) get("/uncles", BlockController, :uncle, as: :uncle)
resources("/pending_transactions", PendingTransactionController, only: [:index]) resources("/pending_transactions", PendingTransactionController, only: [:index])

@ -5,15 +5,15 @@
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<h1 class="card-title" data-test="detail_type"> <h1 class="card-title" data-test="detail_type">
<%= gettext("%{type} Details", type: (if uncle?(@block), do: "Uncle", else: "Block")) %> <%= gettext("%{block_type} Details", block_type: block_type(@block)) %>
</h1> </h1>
<!-- Block Height --> <!-- Block Height -->
<h3 data-test="block_detail_number"> <h3 data-test="block_detail_number">
<%= if uncle?(@block) do %> <%= if block_type(@block) == "Block" do %>
<%= gettext("Uncle Height:") %>
<%= link(@block, to: block_path(BlockScoutWeb.Endpoint, :show, @block.number)) %>
<% else %>
<%= gettext("Block Height: %{height}", height: @block.number) %> <%= gettext("Block Height: %{height}", height: @block.number) %>
<% else %>
<%= gettext("%{block_type} Height:", block_type: block_type(@block)) %>
<%= link(@block, to: block_path(BlockScoutWeb.Endpoint, :show, @block.number)) %>
<% end %> <% end %>
</h3> </h3>
<div class="d-flex flex-row justify-content-start text-muted"> <div class="d-flex flex-row justify-content-start text-muted">
@ -57,7 +57,7 @@
</dd> </dd>
</dl> </dl>
<%= if not uncle?(@block) do %> <%= if block_type(@block) == "Block" do %>
<!-- Total Difficulty --> <!-- Total Difficulty -->
<dl class="row"> <dl class="row">
<dt class="col-sm-3 text-muted"> <%= gettext "Total Difficulty" %> </dt> <dt class="col-sm-3 text-muted"> <%= gettext "Total Difficulty" %> </dt>

@ -20,6 +20,10 @@ defmodule BlockScoutWeb.BlockView do
"#{average} #{unit_text}" "#{average} #{unit_text}"
end end
def block_type(%Block{consensus: false, nephews: []}), do: "Reorg"
def block_type(%Block{consensus: false}), do: "Uncle"
def block_type(_block), do: "Block"
@doc """ @doc """
Work-around for spec issue in `Cldr.Unit.to_string!/1` Work-around for spec issue in `Cldr.Unit.to_string!/1`
""" """
@ -42,6 +46,4 @@ defmodule BlockScoutWeb.BlockView do
def formatted_timestamp(%Block{timestamp: timestamp}) do def formatted_timestamp(%Block{timestamp: timestamp}) do
Timex.format!(timestamp, "%b-%d-%Y %H:%M:%S %p %Z", :strftime) Timex.format!(timestamp, "%b-%d-%Y %H:%M:%S %p %Z", :strftime)
end end
def uncle?(%Block{consensus: consensus}), do: !consensus
end end

@ -109,6 +109,35 @@ defmodule BlockScoutWeb.BlockControllerTest do
end end
end end
describe "GET reorgs/2" do
test "returns all reorgs", %{conn: conn} do
reorg_hashes =
4
|> insert_list(:block, consensus: false)
|> Enum.map(& &1.hash)
conn = get(conn, reorg_path(conn, :reorg))
assert Enum.map(conn.assigns.blocks, & &1.hash) == Enum.reverse(reorg_hashes)
assert conn.assigns.block_type == "Reorg"
end
test "does not include blocks or uncles", %{conn: conn} do
reorg_hashes =
4
|> insert_list(:block, consensus: false)
|> Enum.map(& &1.hash)
insert(:block)
uncle = insert(:block, consensus: false)
insert(:block_second_degree_relation, uncle_hash: uncle.hash)
conn = get(conn, reorg_path(conn, :reorg))
assert Enum.map(conn.assigns.blocks, & &1.hash) == Enum.reverse(reorg_hashes)
assert conn.assigns.block_type == "Reorg"
end
end
describe "GET uncle/2" do describe "GET uncle/2" do
test "returns all uncles", %{conn: conn} do test "returns all uncles", %{conn: conn} do
uncle_hashes = uncle_hashes =

@ -11,6 +11,10 @@ defmodule BlockScoutWeb.BlockListPage do
visit(session, "/blocks") visit(session, "/blocks")
end end
def visit_reorgs_page(session) do
visit(session, "/reorgs")
end
def visit_uncles_page(session) do def visit_uncles_page(session) do
visit(session, "/uncles") visit(session, "/uncles")
end end

@ -150,6 +150,17 @@ defmodule BlockScoutWeb.ViewingBlocksTest do
end end
end end
describe "viewing reorg blocks list" do
test "lists uncle blocks", %{session: session} do
[reorg | _] = insert_list(10, :block, consensus: false)
session
|> BlockListPage.visit_reorgs_page()
|> assert_has(BlockListPage.block(reorg))
|> assert_has(BlockListPage.blocks(10))
end
end
describe "viewing uncle blocks list" do describe "viewing uncle blocks list" do
setup do setup do
uncles = uncles =

@ -18,26 +18,33 @@ defmodule BlockScoutWeb.BlockViewTest do
end end
end end
describe "formatted_timestamp/1" do describe "block_type/1" do
test "returns a formatted timestamp string for a block" do test "returns Block" do
block = insert(:block) block = insert(:block, nephews: [])
assert Timex.format!(block.timestamp, "%b-%d-%Y %H:%M:%S %p %Z", :strftime) == assert BlockView.block_type(block) == "Block"
BlockView.formatted_timestamp(block)
end end
end
describe "uncle?/1" do test "returns Reorg" do
test "returns true for an uncle block" do reorg = insert(:block, consensus: false, nephews: [])
assert BlockView.block_type(reorg) == "Reorg"
end
test "returns Uncle" do
uncle = insert(:block, consensus: false) uncle = insert(:block, consensus: false)
insert(:block_second_degree_relation, uncle_hash: uncle.hash)
assert BlockView.uncle?(uncle) assert BlockView.block_type(uncle) == "Uncle"
end end
end
test "returns false for a block" do describe "formatted_timestamp/1" do
test "returns a formatted timestamp string for a block" do
block = insert(:block) block = insert(:block)
refute BlockView.uncle?(block) assert Timex.format!(block.timestamp, "%b-%d-%Y %H:%M:%S %p %Z", :strftime) ==
BlockView.formatted_timestamp(block)
end end
end end
end end

@ -869,11 +869,11 @@ defmodule Explorer.Chain do
block_type = Keyword.get(options, :block_type, "Block") block_type = Keyword.get(options, :block_type, "Block")
Block Block
|> join_associations(necessity_by_association)
|> page_blocks(paging_options)
|> Block.block_type_filter(block_type) |> Block.block_type_filter(block_type)
|> page_blocks(paging_options)
|> limit(^paging_options.page_size) |> limit(^paging_options.page_size)
|> order_by(desc: :number) |> order_by(desc: :number)
|> join_associations(necessity_by_association)
|> Repo.all() |> Repo.all()
end end

@ -98,10 +98,15 @@ defmodule Explorer.Chain.Block do
end end
@doc """ @doc """
Adds to the given block's query a `where` with conditions to filter by the type of block; Adds to the given block's query a `where` with conditions to filter by the type of block;
`Uncle`, `Reorg`, or `Block`. `Uncle`'s are already filtered based on requiring nephews. `Uncle`, `Reorg`, or `Block`. `Uncle`'s are already filtered based on requiring nephews.
""" """
def block_type_filter(query, "Block"), do: where(query, [block], block.consensus == true) def block_type_filter(query, "Block"), do: where(query, [block], block.consensus == true)
def block_type_filter(query, "Uncle"), do: query def block_type_filter(query, "Reorg") do
query
|> join(:left, [block], uncles in assoc(block, :nephew_relations))
|> where([block, uncles], block.consensus == false and is_nil(uncles.uncle_hash))
end
def block_type_filter(query, "Uncle"), do: query
end end

Loading…
Cancel
Save