Add reward value of a block in the tile and overview section

pull/1249/head
Amanda Sposito 6 years ago
parent d8a894c66a
commit de3520667f
  1. 3
      apps/block_scout_web/lib/block_scout_web/controllers/address_validation_controller.ex
  2. 9
      apps/block_scout_web/lib/block_scout_web/controllers/block_controller.ex
  3. 3
      apps/block_scout_web/lib/block_scout_web/controllers/block_transaction_controller.ex
  4. 2
      apps/block_scout_web/lib/block_scout_web/controllers/chain_controller.ex
  5. 2
      apps/block_scout_web/lib/block_scout_web/notifier.ex
  6. 9
      apps/block_scout_web/lib/block_scout_web/templates/block/_tile.html.eex
  7. 58
      apps/block_scout_web/lib/block_scout_web/templates/block/overview.html.eex
  8. 8
      apps/block_scout_web/lib/block_scout_web/templates/chain/_block.html.eex
  9. 23
      apps/block_scout_web/lib/block_scout_web/views/block_view.ex
  10. 47
      apps/block_scout_web/test/block_scout_web/views/block_view_test.exs
  11. 20
      apps/explorer/lib/explorer/chain.ex
  12. 5
      apps/explorer/lib/explorer/chain/block.ex
  13. 22
      apps/explorer/lib/explorer/chain/block/reward.ex
  14. 38
      apps/explorer/test/explorer/chain_test.exs
  15. 11
      apps/explorer/test/support/factory.ex

@ -23,7 +23,8 @@ defmodule BlockScoutWeb.AddressValidationController do
necessity_by_association: %{ necessity_by_association: %{
miner: :required, miner: :required,
nephews: :optional, nephews: :optional,
transactions: :optional transactions: :optional,
rewards: :optional
} }
], ],
paging_options(params) paging_options(params)

@ -11,7 +11,8 @@ defmodule BlockScoutWeb.BlockController do
[ [
necessity_by_association: %{ necessity_by_association: %{
:transactions => :optional, :transactions => :optional,
[miner: :names] => :optional [miner: :names] => :optional,
:rewards => :optional
} }
] ]
|> handle_render(conn, params) |> handle_render(conn, params)
@ -25,7 +26,8 @@ defmodule BlockScoutWeb.BlockController do
[ [
necessity_by_association: %{ necessity_by_association: %{
:transactions => :optional, :transactions => :optional,
[miner: :names] => :optional [miner: :names] => :optional,
:rewards => :optional
}, },
block_type: "Reorg" block_type: "Reorg"
] ]
@ -37,7 +39,8 @@ defmodule BlockScoutWeb.BlockController do
necessity_by_association: %{ necessity_by_association: %{
:transactions => :optional, :transactions => :optional,
[miner: :names] => :optional, [miner: :names] => :optional,
:nephews => :required :nephews => :required,
:rewards => :optional
}, },
block_type: "Uncle" block_type: "Uncle"
] ]

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

@ -10,7 +10,7 @@ defmodule BlockScoutWeb.ChainController do
blocks = blocks =
[paging_options: %PagingOptions{page_size: 4}] [paging_options: %PagingOptions{page_size: 4}]
|> Chain.list_blocks() |> Chain.list_blocks()
|> Repo.preload([[miner: :names], :transactions]) |> Repo.preload([[miner: :names], :transactions, :rewards])
transaction_estimated_count = Chain.transaction_estimated_count() transaction_estimated_count = Chain.transaction_estimated_count()

@ -108,7 +108,7 @@ defmodule BlockScoutWeb.Notifier do
end end
defp broadcast_block(block) do defp broadcast_block(block) do
preloaded_block = Repo.preload(block, [[miner: :names], :transactions]) preloaded_block = Repo.preload(block, [[miner: :names], :transactions, :rewards])
average_block_time = Chain.average_block_time() average_block_time = Chain.average_block_time()
Endpoint.broadcast("blocks:new_block", "new_block", %{ Endpoint.broadcast("blocks:new_block", "new_block", %{

@ -42,6 +42,15 @@
contract: false %> contract: false %>
</span> </span>
</div> </div>
<%= if show_reward?(@block.rewards) do %>
<div class="text-nowrap text-truncate mt-3 mt-md-0">
<!-- validator reward -->
<%= gettext "Reward" %>
<span class="ml-2">
<%= combined_rewards_value(@block) %>
</span>
</div>
<% end %>
</div> </div>
<div class="col-md-4 col-lg-3 text-md-right d-flex flex-column align-items-md-end justify-content-md-end mt-3 mt-md-0"> <div class="col-md-4 col-lg-3 text-md-right d-flex flex-column align-items-md-end justify-content-md-end mt-3 mt-md-0">
<!-- Gas Limit --> <!-- Gas Limit -->

@ -65,14 +65,14 @@
</dl> </dl>
<!-- Nonce --> <!-- Nonce -->
<dl class="row mb-0"> <dl class="row">
<dt class="col-sm-3 text-muted"> <%= gettext "Nonce" %> </dt> <dt class="col-sm-3 text-muted"> <%= gettext "Nonce" %> </dt>
<dd class="col-sm-9"> <%= to_string(@block.nonce) %> </dd> <dd class="col-sm-9"> <%= to_string(@block.nonce) %> </dd>
</dl> </dl>
<%= if length(@block.uncle_relations) > 0 do %> <%= if length(@block.uncle_relations) > 0 do %>
<!-- Uncles --> <!-- Uncles -->
<dl class="row mt-3 mb-0"> <dl class="row mt-3">
<dt class="col-sm-3 text-muted"> <%= gettext "Uncles" %> </dt> <dt class="col-sm-3 text-muted"> <%= gettext "Uncles" %> </dt>
<dd class="col-sm-9"> <dd class="col-sm-9">
<%= for {relation, index} <- Enum.with_index(@block.uncle_relations) do %> <%= for {relation, index} <- Enum.with_index(@block.uncle_relations) do %>
@ -87,6 +87,24 @@
</dd> </dd>
</dl> </dl>
<% end %> <% end %>
<!-- Otherwise it will be displayed in its own block -->
<%= if show_reward?(@block.rewards) do %>
<dl class="row">
<dt class="col-sm-3 text-muted"><%= gettext "Gas Used" %></dt>
<dd class="col-sm-9">
<%= @block.gas_used |> Cldr.Number.to_string! %>
<span class="text-muted"> (<%= (Decimal.to_integer(@block.gas_used) / Decimal.to_integer(@block.gas_limit)) |> Cldr.Number.to_string!(format: "#.#%") %>) </span>
</dt>
</dl>
<dl class="row mb-0">
<dt class="col-sm-3 text-muted"><%= gettext "Gas Limit" %></dt>
<dd class="col-sm-9">
<%= Cldr.Number.to_string!(@block.gas_limit) %>
</dd>
</dl>
<% end %>
<% end %> <% end %>
</div> </div>
</div> </div>
@ -112,19 +130,33 @@
</div> </div>
</div> </div>
<!-- Gas --> <!-- Validator Reward or Gas-->
<div class="card flex-grow-1 ml-0 ml-md-5 ml-lg-0"> <div class="card flex-grow-1 ml-0 ml-md-5 ml-lg-0">
<div class="card-body"> <div class="card-body">
<h2 class="card-title"> <%= gettext "Gas Used" %> </h2> <%= if show_reward?(@block.rewards) do %>
<div class="text-right"> <h2 class="card-title"> <%= gettext "Block Rewards" %> </h2>
<!-- Gas Used -->
<h3> <%= for block_reward <- @block.rewards do %>
<%= @block.gas_used |> Cldr.Number.to_string! %> <dl class="row">
<span class="text-muted"> (<%= (Decimal.to_integer(@block.gas_used) / Decimal.to_integer(@block.gas_limit)) |> Cldr.Number.to_string!(format: "#.#%") %>) </span> <dt class="col-sm-5 text-muted"><%= block_reward_text(block_reward) %></dt>
</h3> <dd class="col-sm-7">
<!-- Gas Limit --> <%= format_wei_value(block_reward.reward, :ether) %></dt>
<span class="text-muted"> <%= @block.gas_limit |> Cldr.Number.to_string! %> <%= gettext "Gas Limit" %> </span> </dd>
</div> </dl>
<% end %>
<% else %>
<h2 class="card-title"> <%= gettext "Gas Used" %> </h2>
<div class="text-right">
<!-- Gas Used -->
<h3>
<%= @block.gas_used |> Cldr.Number.to_string! %>
<span class="text-muted"> (<%= (Decimal.to_integer(@block.gas_used) / Decimal.to_integer(@block.gas_limit)) |> Cldr.Number.to_string!(format: "#.#%") %>) </span>
</h3>
<!-- Gas Limit -->
<span class="text-muted"> <%= @block.gas_limit |> Cldr.Number.to_string! %> <%= gettext "Gas Limit" %> </span>
</div>
<% end %>
</div> </div>
</div> </div>
</div> </div>

@ -18,5 +18,13 @@
address: @block.miner, address: @block.miner,
contract: false %> contract: false %>
</span> </span>
<%= if BlockScoutWeb.BlockView.show_reward?(@block.rewards) do %>
<span class="text-truncate">
<%= gettext "Reward" %>
<%= BlockScoutWeb.BlockView.combined_rewards_value(@block) %>
</span>
<% end %>
</div> </div>
</div> </div>

@ -3,7 +3,9 @@ defmodule BlockScoutWeb.BlockView do
import Math.Enum, only: [mean: 1] import Math.Enum, only: [mean: 1]
alias Explorer.Chain
alias Explorer.Chain.{Block, Wei} alias Explorer.Chain.{Block, Wei}
alias Explorer.Chain.Block.Reward
@dialyzer :no_match @dialyzer :no_match
@ -46,4 +48,25 @@ 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 show_reward?([]), do: false
def show_reward?(_), do: true
def block_reward_text(%Reward{address_type: :validator}) do
gettext("Miner Reward")
end
def block_reward_text(%Reward{address_type: :emission_funds}) do
gettext("Emission Reward")
end
def block_reward_text(%Reward{address_type: :uncle}) do
gettext("Uncle Reward")
end
def combined_rewards_value(block) do
block
|> Chain.block_combined_rewards()
|> format_wei_value(:ether)
end
end end

@ -47,4 +47,51 @@ defmodule BlockScoutWeb.BlockViewTest do
BlockView.formatted_timestamp(block) BlockView.formatted_timestamp(block)
end end
end end
describe "show_reward?/1" do
test "returns false when list of rewards is empty" do
assert BlockView.show_reward?([]) == false
end
test "returns true when list of rewards is not empty" do
block = insert(:block)
validator = insert(:reward, address_hash: block.miner_hash, block_hash: block.hash, address_type: :validator)
assert BlockView.show_reward?([validator]) == true
end
end
describe "combined_rewards_value/1" do
test "returns all the reward values summed up and formatted into a String" do
block = insert(:block)
insert(
:reward,
address_hash: block.miner_hash,
block_hash: block.hash,
address_type: :validator,
reward: Decimal.new(1_000_000_000_000_000_000)
)
insert(
:reward,
address_hash: block.miner_hash,
block_hash: block.hash,
address_type: :emission_funds,
reward: Decimal.new(1_000_000_000_000_000_000)
)
insert(
:reward,
address_hash: block.miner_hash,
block_hash: block.hash,
address_type: :uncle,
reward: Decimal.new(1_000_042_000_000_000_000)
)
block = Repo.preload(block, :rewards)
assert BlockView.combined_rewards_value(block) == "3.000042 POA"
end
end
end end

@ -2156,4 +2156,24 @@ defmodule Explorer.Chain do
Repo.all(query, timeout: :infinity) Repo.all(query, timeout: :infinity)
end end
@doc """
Combined block reward from all the fees.
"""
@spec block_combined_rewards(Block.t()) :: Wei.t()
def block_combined_rewards(block) do
{:ok, value} =
block.rewards
|> Enum.reduce(
0,
fn block_reward, acc ->
{:ok, decimal} = Wei.dump(block_reward.reward)
Decimal.add(decimal, acc)
end
)
|> Wei.cast()
value
end
end end

@ -7,7 +7,8 @@ defmodule Explorer.Chain.Block do
use Explorer.Schema use Explorer.Schema
alias Explorer.Chain.{Address, Block.SecondDegreeRelation, Gas, Hash, Transaction} alias Explorer.Chain.{Address, Gas, Hash, Transaction}
alias Explorer.Chain.Block.{Reward, SecondDegreeRelation}
@required_attrs ~w(consensus difficulty gas_limit gas_used hash miner_hash nonce number parent_hash size timestamp @required_attrs ~w(consensus difficulty gas_limit gas_used hash miner_hash nonce number parent_hash size timestamp
total_difficulty)a total_difficulty)a
@ -87,6 +88,8 @@ defmodule Explorer.Chain.Block do
has_many(:uncles, through: [:uncle_relations, :uncle]) has_many(:uncles, through: [:uncle_relations, :uncle])
has_many(:transactions, Transaction) has_many(:transactions, Transaction)
has_many(:rewards, Reward, foreign_key: :block_hash)
end end
def changeset(%__MODULE__{} = block, attrs) do def changeset(%__MODULE__{} = block, attrs) do

@ -6,7 +6,7 @@ defmodule Explorer.Chain.Block.Reward do
use Explorer.Schema use Explorer.Schema
alias Explorer.Chain.Block.Reward.AddressType alias Explorer.Chain.Block.Reward.AddressType
alias Explorer.Chain.{Hash, Wei} alias Explorer.Chain.{Address, Block, Hash, Wei}
@required_attrs ~w(address_hash address_type block_hash reward)a @required_attrs ~w(address_hash address_type block_hash reward)a
@ -19,19 +19,35 @@ defmodule Explorer.Chain.Block.Reward do
* `:reward` - Total block reward * `:reward` - Total block reward
""" """
@type t :: %__MODULE__{ @type t :: %__MODULE__{
address: %Ecto.Association.NotLoaded{} | Address.t() | nil,
address_hash: Hash.Address.t(), address_hash: Hash.Address.t(),
address_type: AddressType.t(), address_type: AddressType.t(),
block: %Ecto.Association.NotLoaded{} | Block.t() | nil,
block_hash: Hash.Full.t(), block_hash: Hash.Full.t(),
reward: Wei.t() reward: Wei.t()
} }
@primary_key false @primary_key false
schema "block_rewards" do schema "block_rewards" do
field(:address_hash, Hash.Address)
field(:address_type, AddressType) field(:address_type, AddressType)
field(:block_hash, Hash.Full)
field(:reward, Wei) field(:reward, Wei)
belongs_to(
:address,
Address,
foreign_key: :address_hash,
references: :hash,
type: Hash.Address
)
belongs_to(
:block,
Block,
foreign_key: :block_hash,
references: :hash,
type: Hash.Full
)
timestamps() timestamps()
end end

@ -22,6 +22,8 @@ defmodule Explorer.ChainTest do
Wei Wei
} }
alias Explorer.Chain.Block.Reward
alias Explorer.Chain.Supply.ProofOfAuthority alias Explorer.Chain.Supply.ProofOfAuthority
alias Explorer.Counters.{AddressesWithBalanceCounter, TokenHoldersCounter} alias Explorer.Counters.{AddressesWithBalanceCounter, TokenHoldersCounter}
@ -3177,4 +3179,40 @@ defmodule Explorer.ChainTest do
[block2_with_invalid_consensus.number, block8_with_invalid_consensus.number] [block2_with_invalid_consensus.number, block8_with_invalid_consensus.number]
end end
end end
describe "block_combined_rewards/1" do
test "sums the block_rewards values" do
block = insert(:block)
insert(
:reward,
address_hash: block.miner_hash,
block_hash: block.hash,
address_type: :validator,
reward: Decimal.new(1_000_000_000_000_000_000)
)
insert(
:reward,
address_hash: block.miner_hash,
block_hash: block.hash,
address_type: :emission_funds,
reward: Decimal.new(1_000_000_000_000_000_000)
)
insert(
:reward,
address_hash: block.miner_hash,
block_hash: block.hash,
address_type: :uncle,
reward: Decimal.new(1_000_000_000_000_000_000)
)
block = Repo.preload(block, :rewards)
{:ok, expected_value} = Wei.cast(3_000_000_000_000_000_000)
assert Chain.block_combined_rewards(block) == expected_value
end
end
end end

@ -9,7 +9,7 @@ defmodule Explorer.Factory do
alias Comeonin.Bcrypt alias Comeonin.Bcrypt
alias Explorer.Accounts.{User, UserContact} alias Explorer.Accounts.{User, UserContact}
alias Explorer.Admin.Administrator alias Explorer.Admin.Administrator
alias Explorer.Chain.Block.{EmissionReward, Range} alias Explorer.Chain.Block.{EmissionReward, Range, Reward}
alias Explorer.Chain.{ alias Explorer.Chain.{
Address, Address,
@ -425,6 +425,15 @@ defmodule Explorer.Factory do
} }
end end
def reward_factory do
%Reward{
address_hash: build(:address).hash,
address_type: :validator,
block_hash: build(:block).hash,
reward: Decimal.new(3)
}
end
def transaction_factory do def transaction_factory do
%Transaction{ %Transaction{
from_address: build(:address), from_address: build(:address),

Loading…
Cancel
Save