Merge pull request #870 from poanetwork/745-uncle-support-ui

Uncle Support in the UI
pull/891/head
John Stamates 6 years ago committed by GitHub
commit 093a79523f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      apps/block_scout_web/assets/css/components/_animations.scss
  2. 5
      apps/block_scout_web/assets/css/components/_navbar.scss
  3. 24
      apps/block_scout_web/assets/css/components/_tile.scss
  4. 3
      apps/block_scout_web/lib/block_scout_web/channels/block_channel.ex
  5. 59
      apps/block_scout_web/lib/block_scout_web/controllers/block_controller.ex
  6. 6
      apps/block_scout_web/lib/block_scout_web/controllers/block_transaction_controller.ex
  7. 4
      apps/block_scout_web/lib/block_scout_web/router.ex
  8. 30
      apps/block_scout_web/lib/block_scout_web/templates/block/_tile.html.eex
  9. 6
      apps/block_scout_web/lib/block_scout_web/templates/block/index.html.eex
  10. 53
      apps/block_scout_web/lib/block_scout_web/templates/block/overview.html.eex
  11. 2
      apps/block_scout_web/lib/block_scout_web/templates/block_transaction/index.html.eex
  12. 17
      apps/block_scout_web/lib/block_scout_web/templates/layout/_topnav.html.eex
  13. 4
      apps/block_scout_web/lib/block_scout_web/views/block_view.ex
  14. 96
      apps/block_scout_web/priv/gettext/default.pot
  15. 96
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  16. 89
      apps/block_scout_web/test/block_scout_web/controllers/block_controller_test.exs
  17. 14
      apps/block_scout_web/test/block_scout_web/features/pages/block_list_page.ex
  18. 16
      apps/block_scout_web/test/block_scout_web/features/pages/block_page.ex
  19. 140
      apps/block_scout_web/test/block_scout_web/features/viewing_blocks_test.exs
  20. 21
      apps/block_scout_web/test/block_scout_web/views/block_view_test.exs
  21. 5
      apps/explorer/lib/explorer/chain.ex
  22. 14
      apps/explorer/lib/explorer/chain/block.ex

@ -66,6 +66,11 @@
.fade-up {
will-change: transform, opacity, height;
animation: fade-up 0.6s cubic-bezier(0.455, 0.03, 0.515, 0.955);
@media (max-width: 767px) {
max-height: 234px;
animation: fade-up--mobile 0.6s cubic-bezier(0.455, 0.03, 0.515, 0.955);
}
}
.shrink-out {

@ -147,16 +147,13 @@
}
}
.nav-link.dropdown-toggle {
padding-right: 0 !important;
}
.dropdown-menu {
padding: 0px;
}
.dropdown-item {
padding: 10px 20px;
font-size: 12px;
&:hover {
background-color: $tertiary;

@ -17,8 +17,28 @@
&-type {
&-block {
border-left: 4px solid $primary;
&-Block {
border-left: 4px solid $indigo;
.tile-label {
color: $indigo;
}
}
&-Uncle {
border-left: 4px solid $cyan;
.tile-label {
color: $cyan;
}
}
&-Reorg {
border-left: 4px solid $purple;
.tile-label {
color: $purple;
}
}
&-transaction {

@ -24,7 +24,8 @@ defmodule BlockScoutWeb.BlockChannel do
View.render_to_string(
BlockView,
"_tile.html",
block: block
block: block,
block_type: BlockView.block_type(block)
)
rendered_chain_block =

@ -6,25 +6,54 @@ defmodule BlockScoutWeb.BlockController do
alias Explorer.Chain
def index(conn, params) do
full_options =
Keyword.merge(
[
necessity_by_association: %{
:transactions => :optional,
[miner: :names] => :optional
}
],
paging_options(params)
)
[
necessity_by_association: %{
:transactions => :optional,
[miner: :names] => :optional
}
]
|> Keyword.merge(paging_options(params))
|> handle_render(conn, params)
end
blocks_plus_one = Chain.list_blocks(full_options)
def show(conn, %{"hash_or_number" => hash_or_number}) do
redirect(conn, to: block_transaction_path(conn, :index, hash_or_number))
end
{blocks, next_page} = split_list_by_page(blocks_plus_one)
def reorg(conn, params) do
[
necessity_by_association: %{
:transactions => :optional,
[miner: :names] => :optional
},
block_type: "Reorg"
]
|> Keyword.merge(paging_options(params))
|> handle_render(conn, params)
end
render(conn, "index.html", blocks: blocks, next_page_params: next_page_params(next_page, blocks, params))
def uncle(conn, params) do
[
necessity_by_association: %{
:transactions => :optional,
[miner: :names] => :optional,
:nephews => :required
},
block_type: "Uncle"
]
|> Keyword.merge(paging_options(params))
|> handle_render(conn, params)
end
def show(conn, %{"hash_or_number" => hash_or_number}) do
redirect(conn, to: block_transaction_path(conn, :index, hash_or_number))
defp handle_render(full_options, conn, params) do
blocks_plus_one = Chain.list_blocks(full_options)
{blocks, next_page} = split_list_by_page(blocks_plus_one)
render(conn, "index.html",
blocks: blocks,
next_page_params: next_page_params(next_page, blocks, params),
block_type: Keyword.get(full_options, :block_type, "Block")
)
end
end

@ -9,7 +9,11 @@ defmodule BlockScoutWeb.BlockTransactionController do
def index(conn, %{"block_hash_or_number" => formatted_block_hash_or_number} = params) do
with {:ok, block} <-
param_block_hash_or_number_to_block(formatted_block_hash_or_number,
necessity_by_association: %{[miner: :names] => :required}
necessity_by_association: %{
[miner: :names] => :required,
:uncles => :optional,
:nephews => :optional
}
) do
block_transaction_count = Chain.block_to_transaction_count(block)

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

@ -1,13 +1,27 @@
<div class="tile tile-type-block fade-up" data-block-number="<%= to_string(@block.number) %>">
<div class="tile tile-type-<%= String.downcase(@block_type) %> fade-up" data-selector="block-tile" data-block-number="<%= to_string(@block.number) %>">
<div class="row">
<div class="col-md-8 col-lg-9">
<div class="col-md-2 d-flex flex-row flex-md-column align-items-left justify-content-start justify-content-lg-center mb-1 mb-md-0 pl-md-4">
<span class="tile-label" data-test="transaction_type">
<%= @block_type %>
</span>
</div>
<div class="col-md-6 col-lg-7">
<!-- block height -->
<%= link(
@block,
class: "tile-title",
to: block_path(BlockScoutWeb.Endpoint, :show, @block),
"data-selector": "block-number"
) %>
<%= if @block_type == "Block" do %>
<%= link(
@block,
class: "tile-title",
to: block_path(BlockScoutWeb.Endpoint, :show, @block),
"data-selector": "block-number"
) %>
<% else %>
<%= link(
@block,
class: "tile-title",
to: block_path(BlockScoutWeb.Endpoint, :show, @block.hash),
"data-selector": "block-number"
) %>
<% end %>
<div>
<!-- transactions -->
<span class="mr-2">

@ -1,4 +1,4 @@
<section class="container" data-page="block-list">
<section class="container" data-page="<%= String.downcase(@block_type) %>-list">
<div class="card">
<div class="card-body">
<div data-selector="channel-disconnected-message" style="display:none;">
@ -7,11 +7,11 @@
</div>
</div>
<h1><%= gettext "Blocks" %></h1>
<h1><%= gettext("%{block_type}s", block_type: @block_type) %></h1>
<span data-selector="blocks-list">
<%= for block <- @blocks do %>
<%= render BlockScoutWeb.BlockView, "_tile.html", block: block %>
<%= render BlockScoutWeb.BlockView, "_tile.html", block: block, block_type: @block_type %>
<% end %>
</span>

@ -4,9 +4,18 @@
<!-- Block Details -->
<div class="card">
<div class="card-body">
<h1 class="card-title"> <%= gettext("Block Details") %> </h1>
<h1 class="card-title" data-test="detail_type">
<%= gettext("%{block_type} Details", block_type: block_type(@block)) %>
</h1>
<!-- Block Height -->
<h3 data-test="block_detail_number"> <%= gettext "Block Height #%{height}", height: @block.number %> </h3>
<h3 data-test="block_detail_number">
<%= if block_type(@block) == "Block" do %>
<%= 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 %>
</h3>
<div class="d-flex flex-row justify-content-start text-muted">
<!-- # of Transactions -->
<span class="mr-4"> <%= gettext "%{count} Transactions", count: @block_transaction_count %> </span>
@ -48,17 +57,37 @@
</dd>
</dl>
<!-- Total Difficulty -->
<dl class="row">
<dt class="col-sm-3 text-muted"> <%= gettext "Total Difficulty" %> </dt>
<dd class="col-sm-9"> <%= @block.total_difficulty |> Cldr.Number.to_string! %> </dd>
</dl>
<%= if block_type(@block) == "Block" do %>
<!-- Total Difficulty -->
<dl class="row">
<dt class="col-sm-3 text-muted"> <%= gettext "Total Difficulty" %> </dt>
<dd class="col-sm-9"> <%= @block.total_difficulty |> Cldr.Number.to_string! %> </dd>
</dl>
<!-- Nonce -->
<dl class="row mb-0">
<dt class="col-sm-3 text-muted"> <%= gettext "Nonce" %> </dt>
<dd class="col-sm-9"> <%= to_string(@block.nonce) %> </dd>
</dl>
<!-- Nonce -->
<dl class="row mb-0">
<dt class="col-sm-3 text-muted"> <%= gettext "Nonce" %> </dt>
<dd class="col-sm-9"> <%= to_string(@block.nonce) %> </dd>
</dl>
<%= if length(@block.uncle_relations) > 0 do %>
<!-- Uncles -->
<dl class="row mt-3 mb-0">
<dt class="col-sm-3 text-muted"> <%= gettext "Uncles" %> </dt>
<dd class="col-sm-9">
<%= for {relation, index} <- Enum.with_index(@block.uncle_relations) do %>
<%= link(
gettext("Position %{index}", index: index),
class: "block__link",
"data-test": "uncle_link",
"data-uncle-hash": to_string(relation.uncle_hash),
to: block_path(@conn, :show, relation.uncle_hash)
) %><%= if index < length(@block.uncle_relations) - 1 do %>,<% end %>
<% end %>
</dd>
</dl>
<% end %>
<% end %>
</div>
</div>
</div>

@ -41,7 +41,7 @@
</span>
<% else %>
<div class="tile tile-muted text-center">
<span data-selector="empty-transactions-list"><%= gettext "There are no transactions for this address." %></span>
<span data-selector="empty-transactions-list"><%= gettext "There are no transactions for this block." %></span>
</div>
<% end %>

@ -8,13 +8,24 @@
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<%= link to: block_path(@conn, :index), class: "nav-link topnav-nav-link" do %>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarBlocksDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="nav-link-icon">
<%= render BlockScoutWeb.IconsView, "_block_icon.html" %>
</span>
<%= gettext("Blocks") %>
<% end %>
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<%= link to: block_path(@conn, :index), class: "dropdown-item" do %>
<%= gettext("Blocks") %>
<% end %>
<%= link to: uncle_path(@conn, :uncle), class: "dropdown-item" do %>
<%= gettext("Uncles") %>
<% end %>
<%= link to: reorg_path(@conn, :reorg), class: "dropdown-item" do %>
<%= gettext("Forked Blocks (Reorgs)") %>
<% end %>
</div>
</li>
<li class="nav-item">
<%= link to: transaction_path(@conn, :index), class: "nav-link topnav-nav-link" do %>

@ -20,6 +20,10 @@ defmodule BlockScoutWeb.BlockView do
"#{average} #{unit_text}"
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 """
Work-around for spec issue in `Cldr.Unit.to_string!/1`
"""

@ -6,7 +6,7 @@ msgstr[0] ""
msgstr[1] ""
#, elixir-format
#: lib/block_scout_web/templates/block/_tile.html.eex:14
#: lib/block_scout_web/templates/block/_tile.html.eex:28
msgid "%{count} transaction"
msgid_plural "%{count} transactions"
msgstr[0] ""
@ -20,7 +20,7 @@ msgstr[0] ""
msgstr[1] ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:12
#: lib/block_scout_web/templates/block/overview.html.eex:21
#: lib/block_scout_web/templates/chain/_block.html.eex:9
msgid "%{count} Transactions"
msgstr ""
@ -49,12 +49,12 @@ msgid "A string with the name of the module to be invoked."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:40
#: lib/block_scout_web/templates/layout/_topnav.html.eex:51
msgid "API"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:32
#: lib/block_scout_web/templates/layout/_topnav.html.eex:43
msgid "Accounts"
msgstr ""
@ -109,25 +109,15 @@ msgstr ""
msgid "Block Confirmations"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:7
msgid "Block Details"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:9
msgid "Block Height #%{height}"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:45
msgid "Block Number"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/index.html.eex:10
#: lib/block_scout_web/templates/chain/show.html.eex:57
#: lib/block_scout_web/templates/layout/_topnav.html.eex:16
#: lib/block_scout_web/templates/layout/_topnav.html.eex:20
msgid "Blocks"
msgstr ""
@ -309,7 +299,7 @@ msgid "Details"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:45
#: lib/block_scout_web/templates/block/overview.html.eex:54
msgid "Difficulty"
msgstr ""
@ -381,14 +371,14 @@ msgid "Gas"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/_tile.html.eex:34
#: lib/block_scout_web/templates/block/overview.html.eex:97
#: lib/block_scout_web/templates/block/_tile.html.eex:48
#: lib/block_scout_web/templates/block/overview.html.eex:126
msgid "Gas Limit"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/_tile.html.eex:39
#: lib/block_scout_web/templates/block/overview.html.eex:89
#: lib/block_scout_web/templates/block/_tile.html.eex:53
#: lib/block_scout_web/templates/block/overview.html.eex:118
msgid "Gas Used"
msgstr ""
@ -404,7 +394,7 @@ msgid "Gwei"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:27
#: lib/block_scout_web/templates/block/overview.html.eex:36
msgid "Hash"
msgstr ""
@ -477,8 +467,8 @@ msgid "Max of"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/_tile.html.eex:23
#: lib/block_scout_web/templates/block/overview.html.eex:70
#: lib/block_scout_web/templates/block/_tile.html.eex:37
#: lib/block_scout_web/templates/block/overview.html.eex:99
#: lib/block_scout_web/templates/chain/_block.html.eex:13
msgid "Miner"
msgstr ""
@ -547,7 +537,7 @@ msgid "No"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:59
#: lib/block_scout_web/templates/block/overview.html.eex:69
#: lib/block_scout_web/templates/transaction/overview.html.eex:66
msgid "Nonce"
msgstr ""
@ -582,12 +572,12 @@ msgid "Owner Address"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:71
#: lib/block_scout_web/templates/layout/_topnav.html.eex:82
msgid "POA Core"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:70
#: lib/block_scout_web/templates/layout/_topnav.html.eex:81
msgid "POA Sokol"
msgstr ""
@ -602,7 +592,7 @@ msgid "Parameters"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:33
#: lib/block_scout_web/templates/block/overview.html.eex:42
msgid "Parent Hash"
msgstr ""
@ -676,13 +666,13 @@ msgid "Responses"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:47
#: lib/block_scout_web/templates/layout/_topnav.html.eex:54
#: lib/block_scout_web/templates/layout/_topnav.html.eex:58
#: lib/block_scout_web/templates/layout/_topnav.html.eex:65
msgid "Search"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:47
#: lib/block_scout_web/templates/layout/_topnav.html.eex:58
msgid "Search by address, transaction hash, or block number"
msgstr ""
@ -773,7 +763,6 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_transaction/index.html.eex:77
#: lib/block_scout_web/templates/block_transaction/index.html.eex:44
msgid "There are no transactions for this address."
msgstr ""
@ -860,7 +849,7 @@ msgid "Topics"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:53
#: lib/block_scout_web/templates/block/overview.html.eex:63
msgid "Total Difficulty"
msgstr ""
@ -897,7 +886,7 @@ msgstr ""
#: lib/block_scout_web/templates/block_transaction/index.html.eex:26
#: lib/block_scout_web/templates/block_transaction/index.html.eex:35
#: lib/block_scout_web/templates/chain/show.html.eex:76
#: lib/block_scout_web/templates/layout/_topnav.html.eex:24
#: lib/block_scout_web/templates/layout/_topnav.html.eex:35
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:56
#: lib/block_scout_web/templates/transaction/index.html.eex:56
#: lib/block_scout_web/views/address_view.ex:221
@ -1131,3 +1120,44 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_metatags.html.eex:10
msgid "View transaction %{transaction} on %{subnetwork}"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:8
msgid "%{block_type} Details"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:15
msgid "%{block_type} Height:"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/index.html.eex:10
msgid "%{block_type}s"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:13
msgid "Block Height: %{height}"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:26
msgid "Forked Blocks (Reorgs)"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:80
msgid "Position %{index}"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block_transaction/index.html.eex:44
msgid "There are no transactions for this block."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:76
#: lib/block_scout_web/templates/layout/_topnav.html.eex:23
msgid "Uncles"
msgstr ""

@ -6,7 +6,7 @@ msgstr[0] ""
msgstr[1] ""
#, elixir-format
#: lib/block_scout_web/templates/block/_tile.html.eex:14
#: lib/block_scout_web/templates/block/_tile.html.eex:28
msgid "%{count} transaction"
msgid_plural "%{count} transactions"
msgstr[0] ""
@ -20,7 +20,7 @@ msgstr[0] ""
msgstr[1] ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:12
#: lib/block_scout_web/templates/block/overview.html.eex:21
#: lib/block_scout_web/templates/chain/_block.html.eex:9
msgid "%{count} Transactions"
msgstr ""
@ -49,12 +49,12 @@ msgid "A string with the name of the module to be invoked."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:40
#: lib/block_scout_web/templates/layout/_topnav.html.eex:51
msgid "API"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:32
#: lib/block_scout_web/templates/layout/_topnav.html.eex:43
msgid "Accounts"
msgstr ""
@ -109,25 +109,15 @@ msgstr ""
msgid "Block Confirmations"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:7
msgid "Block Details"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:9
msgid "Block Height #%{height}"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/transaction/overview.html.eex:45
msgid "Block Number"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/index.html.eex:10
#: lib/block_scout_web/templates/chain/show.html.eex:57
#: lib/block_scout_web/templates/layout/_topnav.html.eex:16
#: lib/block_scout_web/templates/layout/_topnav.html.eex:20
msgid "Blocks"
msgstr ""
@ -309,7 +299,7 @@ msgid "Details"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:45
#: lib/block_scout_web/templates/block/overview.html.eex:54
msgid "Difficulty"
msgstr ""
@ -381,14 +371,14 @@ msgid "Gas"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/_tile.html.eex:34
#: lib/block_scout_web/templates/block/overview.html.eex:97
#: lib/block_scout_web/templates/block/_tile.html.eex:48
#: lib/block_scout_web/templates/block/overview.html.eex:126
msgid "Gas Limit"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/_tile.html.eex:39
#: lib/block_scout_web/templates/block/overview.html.eex:89
#: lib/block_scout_web/templates/block/_tile.html.eex:53
#: lib/block_scout_web/templates/block/overview.html.eex:118
msgid "Gas Used"
msgstr ""
@ -404,7 +394,7 @@ msgid "Gwei"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:27
#: lib/block_scout_web/templates/block/overview.html.eex:36
msgid "Hash"
msgstr ""
@ -477,8 +467,8 @@ msgid "Max of"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/_tile.html.eex:23
#: lib/block_scout_web/templates/block/overview.html.eex:70
#: lib/block_scout_web/templates/block/_tile.html.eex:37
#: lib/block_scout_web/templates/block/overview.html.eex:99
#: lib/block_scout_web/templates/chain/_block.html.eex:13
msgid "Miner"
msgstr ""
@ -547,7 +537,7 @@ msgid "No"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:59
#: lib/block_scout_web/templates/block/overview.html.eex:69
#: lib/block_scout_web/templates/transaction/overview.html.eex:66
msgid "Nonce"
msgstr ""
@ -582,12 +572,12 @@ msgid "Owner Address"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:71
#: lib/block_scout_web/templates/layout/_topnav.html.eex:82
msgid "POA Core"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:70
#: lib/block_scout_web/templates/layout/_topnav.html.eex:81
msgid "POA Sokol"
msgstr ""
@ -602,7 +592,7 @@ msgid "Parameters"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:33
#: lib/block_scout_web/templates/block/overview.html.eex:42
msgid "Parent Hash"
msgstr ""
@ -676,13 +666,13 @@ msgid "Responses"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:47
#: lib/block_scout_web/templates/layout/_topnav.html.eex:54
#: lib/block_scout_web/templates/layout/_topnav.html.eex:58
#: lib/block_scout_web/templates/layout/_topnav.html.eex:65
msgid "Search"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:47
#: lib/block_scout_web/templates/layout/_topnav.html.eex:58
msgid "Search by address, transaction hash, or block number"
msgstr ""
@ -773,7 +763,6 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_transaction/index.html.eex:77
#: lib/block_scout_web/templates/block_transaction/index.html.eex:44
msgid "There are no transactions for this address."
msgstr ""
@ -860,7 +849,7 @@ msgid "Topics"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:53
#: lib/block_scout_web/templates/block/overview.html.eex:63
msgid "Total Difficulty"
msgstr ""
@ -897,7 +886,7 @@ msgstr ""
#: lib/block_scout_web/templates/block_transaction/index.html.eex:26
#: lib/block_scout_web/templates/block_transaction/index.html.eex:35
#: lib/block_scout_web/templates/chain/show.html.eex:76
#: lib/block_scout_web/templates/layout/_topnav.html.eex:24
#: lib/block_scout_web/templates/layout/_topnav.html.eex:35
#: lib/block_scout_web/templates/pending_transaction/index.html.eex:56
#: lib/block_scout_web/templates/transaction/index.html.eex:56
#: lib/block_scout_web/views/address_view.ex:221
@ -1131,3 +1120,44 @@ msgstr ""
#: lib/block_scout_web/templates/transaction/_metatags.html.eex:10
msgid "View transaction %{transaction} on %{subnetwork}"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:8
msgid "%{block_type} Details"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:15
msgid "%{block_type} Height:"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/index.html.eex:10
msgid "%{block_type}s"
msgstr ""
#, elixir-format, fuzzy
#: lib/block_scout_web/templates/block/overview.html.eex:13
msgid "Block Height: %{height}"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/layout/_topnav.html.eex:26
msgid "Forked Blocks (Reorgs)"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:80
msgid "Position %{index}"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block_transaction/index.html.eex:44
msgid "There are no transactions for this block."
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/block/overview.html.eex:76
#: lib/block_scout_web/templates/layout/_topnav.html.eex:23
msgid "Uncles"
msgstr ""

@ -8,6 +8,13 @@ defmodule BlockScoutWeb.BlockControllerTest do
conn = get(conn, "/blocks/3")
assert redirected_to(conn) =~ "/blocks/3/transactions"
end
test "with uncle block redirects to block_hash route", %{conn: conn} do
uncle = insert(:block, consensus: false)
conn = get(conn, block_path(conn, :show, uncle))
assert redirected_to(conn) =~ "/blocks/#{to_string(uncle.hash)}/transactions"
end
end
describe "GET index/2" do
@ -15,12 +22,28 @@ defmodule BlockScoutWeb.BlockControllerTest do
block_ids =
4
|> insert_list(:block)
|> Stream.map(fn block -> block.number end)
|> Stream.map(& &1.number)
|> Enum.reverse()
conn = get(conn, block_path(conn, :index))
assert Enum.map(conn.assigns.blocks, & &1.number) == block_ids
end
test "does not include uncles", %{conn: conn} do
blocks =
4
|> insert_list(:block)
|> Enum.reverse()
for index <- 0..3 do
uncle = insert(:block, consensus: false)
insert(:block_second_degree_relation, uncle_hash: uncle.hash, nephew: Enum.at(blocks, index))
end
conn = get(conn, block_path(conn, :index))
assert conn.assigns.blocks |> Enum.map(fn block -> block.number end) == block_ids
assert Enum.map(conn.assigns.blocks, & &1.number) == Enum.map(blocks, & &1.number)
end
test "returns a block with two transactions", %{conn: conn} do
@ -85,4 +108,66 @@ defmodule BlockScoutWeb.BlockControllerTest do
assert html_response(conn, 200) =~ miner_name
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
test "returns all uncles", %{conn: conn} do
uncle_hashes =
for _index <- 1..4 do
uncle = insert(:block, consensus: false)
insert(:block_second_degree_relation, uncle_hash: uncle.hash)
uncle.hash
end
conn = get(conn, uncle_path(conn, :uncle))
assert Enum.map(conn.assigns.blocks, & &1.hash) == Enum.reverse(uncle_hashes)
assert conn.assigns.block_type == "Uncle"
end
test "does not include blocks or reorgs", %{conn: conn} do
uncle_hashes =
for _index <- 1..4 do
uncle = insert(:block, consensus: false)
insert(:block_second_degree_relation, uncle_hash: uncle.hash)
uncle.hash
end
insert(:block)
insert(:block, consensus: false)
conn = get(conn, uncle_path(conn, :uncle))
assert Enum.map(conn.assigns.blocks, & &1.hash) == Enum.reverse(uncle_hashes)
end
end
end

@ -11,11 +11,23 @@ defmodule BlockScoutWeb.BlockListPage do
visit(session, "/blocks")
end
def visit_reorgs_page(session) do
visit(session, "/reorgs")
end
def visit_uncles_page(session) do
visit(session, "/uncles")
end
def block(%Block{number: block_number}) do
css("[data-block-number='#{block_number}']")
css("[data-selector='block-tile'][data-block-number='#{block_number}']")
end
def place_holder_blocks(count) do
css("[data-selector='place-holder']", count: count)
end
def blocks(count) do
css("[data-selector='block-tile']", count: count)
end
end

@ -15,6 +15,10 @@ defmodule BlockScoutWeb.BlockPage do
css("[data-test='block_detail_number']", text: to_string(block_number))
end
def page_type(type) do
css("[data-test='detail_type']", text: type)
end
def token_transfers(%Transaction{hash: transaction_hash}, count: count) do
css("[data-transaction-hash='#{transaction_hash}'] [data-test='token_transfer']", count: count)
end
@ -31,7 +35,15 @@ defmodule BlockScoutWeb.BlockPage do
css("[data-transaction-hash='#{transaction_hash}'] [data-test='transaction_status']")
end
def visit_page(session, %Block{number: block_number}) do
visit(session, "/blocks/#{block_number}/transactions")
def uncle_link(%Block{hash: hash}) do
css("[data-test='uncle_link'][data-uncle-hash='#{hash}']")
end
def visit_page(session, %Block{number: block_number, consensus: true}) do
visit(session, "/blocks/#{block_number}")
end
def visit_page(session, %Block{hash: hash}) do
visit(session, "/blocks/#{hash}")
end
end

@ -20,12 +20,6 @@ defmodule BlockScoutWeb.ViewingBlocksTest do
{:ok, first_shown_block: newest_block, last_shown_block: oldest_block}
end
test "viewing the blocks index page", %{first_shown_block: block, session: session} do
session
|> BlockListPage.visit_page()
|> assert_has(BlockListPage.block(block))
end
describe "block details page" do
test "show block detail page", %{session: session} do
block = insert(:block, number: 42)
@ -33,35 +27,7 @@ defmodule BlockScoutWeb.ViewingBlocksTest do
session
|> BlockPage.visit_page(block)
|> assert_has(BlockPage.detail_number(block))
end
test "inserts place holder blocks if out of order block received", %{session: session} do
BlockListPage.visit_page(session)
block = insert(:block, number: 315)
Notifier.handle_event({:chain_event, :blocks, :realtime, [block]})
session
|> assert_has(BlockListPage.block(block))
|> assert_has(BlockListPage.place_holder_blocks(3))
end
test "replaces place holder block if skipped block received", %{session: session} do
BlockListPage.visit_page(session)
block = insert(:block, number: 315)
Notifier.handle_event({:chain_event, :blocks, :realtime, [block]})
session
|> assert_has(BlockListPage.block(block))
|> assert_has(BlockListPage.place_holder_blocks(3))
skipped_block = insert(:block, number: 314)
Notifier.handle_event({:chain_event, :blocks, :realtime, [skipped_block]})
session
|> assert_has(BlockListPage.block(skipped_block))
|> assert_has(BlockListPage.place_holder_blocks(2))
|> assert_has(BlockPage.page_type("Block Details"))
end
test "block detail page has transactions", %{session: session} do
@ -125,5 +91,109 @@ defmodule BlockScoutWeb.ViewingBlocksTest do
|> click(BlockPage.token_transfers_expansion(transaction))
|> assert_has(BlockPage.token_transfers(transaction, count: 3))
end
test "show reorg detail page", %{session: session} do
reorg = insert(:block, consensus: false)
session
|> BlockPage.visit_page(reorg)
|> assert_has(BlockPage.detail_number(reorg))
|> assert_has(BlockPage.page_type("Reorg Details"))
end
test "show uncle detail page", %{session: session} do
uncle = insert(:block, consensus: false)
insert(:block_second_degree_relation, uncle_hash: uncle.hash)
session
|> BlockPage.visit_page(uncle)
|> assert_has(BlockPage.detail_number(uncle))
|> assert_has(BlockPage.page_type("Uncle Details"))
end
test "show link to uncle on block detail page", %{session: session} do
block = insert(:block)
uncle = insert(:block, consensus: false)
insert(:block_second_degree_relation, uncle_hash: uncle.hash, nephew: block)
session
|> BlockPage.visit_page(block)
|> assert_has(BlockPage.detail_number(block))
|> assert_has(BlockPage.page_type("Block Details"))
|> assert_has(BlockPage.uncle_link(uncle))
end
end
describe "viewing blocks list" do
test "viewing the blocks index page", %{first_shown_block: block, session: session} do
session
|> BlockListPage.visit_page()
|> assert_has(BlockListPage.block(block))
end
test "inserts place holder blocks if out of order block received", %{session: session} do
BlockListPage.visit_page(session)
block = insert(:block, number: 315)
Notifier.handle_event({:chain_event, :blocks, :realtime, [block]})
session
|> assert_has(BlockListPage.block(block))
|> assert_has(BlockListPage.place_holder_blocks(3))
end
test "replaces place holder block if skipped block received", %{session: session} do
BlockListPage.visit_page(session)
block = insert(:block, number: 315)
Notifier.handle_event({:chain_event, :blocks, :realtime, [block]})
session
|> assert_has(BlockListPage.block(block))
|> assert_has(BlockListPage.place_holder_blocks(3))
skipped_block = insert(:block, number: 314)
Notifier.handle_event({:chain_event, :blocks, :realtime, [skipped_block]})
session
|> assert_has(BlockListPage.block(skipped_block))
|> assert_has(BlockListPage.place_holder_blocks(2))
end
end
describe "viewing uncle blocks list" do
setup do
uncles =
for _index <- 1..10 do
uncle = insert(:block, consensus: false)
insert(:block_second_degree_relation, uncle_hash: uncle.hash)
:transaction
|> insert()
|> with_block(uncle)
uncle
end
{:ok, %{uncles: uncles}}
end
test "lists uncle blocks", %{session: session, uncles: [uncle | _]} do
session
|> BlockListPage.visit_uncles_page()
|> assert_has(BlockListPage.block(uncle))
|> assert_has(BlockListPage.blocks(10))
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
end

@ -18,6 +18,27 @@ defmodule BlockScoutWeb.BlockViewTest do
end
end
describe "block_type/1" do
test "returns Block" do
block = insert(:block, nephews: [])
assert BlockView.block_type(block) == "Block"
end
test "returns Reorg" do
reorg = insert(:block, consensus: false, nephews: [])
assert BlockView.block_type(reorg) == "Reorg"
end
test "returns Uncle" do
uncle = insert(:block, consensus: false)
insert(:block_second_degree_relation, uncle_hash: uncle.hash)
assert BlockView.block_type(uncle) == "Uncle"
end
end
describe "formatted_timestamp/1" do
test "returns a formatted timestamp string for a block" do
block = insert(:block)

@ -858,18 +858,21 @@ defmodule Explorer.Chain do
* `:paging_options` - a `t:Explorer.PagingOptions.t/0` used to specify the `:page_size` and
`:key` (a tuple of the lowest/oldest `{block_number}`). Results will be the internal
transactions older than the `block_number` that are passed.
* ':block_type' - use to filter by type of block; Uncle`, `Reorg`, or `Block` (default).
"""
@spec list_blocks([paging_options | necessity_by_association_option]) :: [Block.t()]
def list_blocks(options \\ []) when is_list(options) do
necessity_by_association = Keyword.get(options, :necessity_by_association, %{})
paging_options = Keyword.get(options, :paging_options, @default_paging_options)
block_type = Keyword.get(options, :block_type, "Block")
Block
|> join_associations(necessity_by_association)
|> Block.block_type_filter(block_type)
|> page_blocks(paging_options)
|> limit(^paging_options.page_size)
|> order_by(desc: :number)
|> join_associations(necessity_by_association)
|> Repo.all()
end

@ -96,4 +96,18 @@ defmodule Explorer.Chain.Block do
|> foreign_key_constraint(:parent_hash)
|> unique_constraint(:hash, name: :blocks_pkey)
end
@doc """
Adds to the given block's query a `where` with conditions to filter by the type of block;
`Uncle`, `Reorg`, or `Block`.
"""
def block_type_filter(query, "Block"), do: where(query, [block], block.consensus == true)
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: where(query, [block], block.consensus == false)
end

Loading…
Cancel
Save