Fetch unremovable staking and disallow staking to empty pool (#2608)

is_unremovable field is added to staking pools and filled using contract
getter unremovableValidator.
If such validator hasn't staked to itself yet, Stake button is disabled.
Also, Remove My Pool button is disabled for such validator.

Also, fetching is_validator field is optimized, and is now performed using
one getValidators call instead of one isValidator call per pool.
staking
Paul Tsupikoff 5 years ago committed by Victor Baranov
parent 31b90102de
commit 2344222930
  1. 10
      apps/block_scout_web/lib/block_scout_web/controllers/stakes_controller.ex
  2. 4
      apps/block_scout_web/lib/block_scout_web/templates/stakes/_stakes_modal_move.html.eex
  3. 4
      apps/block_scout_web/lib/block_scout_web/templates/stakes/_stakes_top.html.eex
  4. 1
      apps/explorer/lib/explorer/chain/import/runner/staking_pools.ex
  5. 5
      apps/explorer/lib/explorer/chain/staking_pool.ex
  6. 4
      apps/explorer/lib/explorer/staking/contract_reader.ex
  7. 12
      apps/explorer/lib/explorer/staking/contract_state.ex
  8. 9
      apps/explorer/priv/repo/migrations/20190820212841_add_unremovable_to_staking_pools.exs
  9. 11
      apps/explorer/test/explorer/staking/contract_state_test.exs

@ -92,7 +92,7 @@ defmodule BlockScoutWeb.StakesController do
average_block_time: average_block_time,
pools_type: filter,
buttons: %{
stake: true,
stake: stake_allowed?(pool, delegator),
move: move_allowed?(delegator),
withdraw: withdraw_allowed?(delegator, epoch_number)
}
@ -130,6 +130,14 @@ defmodule BlockScoutWeb.StakesController do
inactive_pools_path(conn, :index, params)
end
defp stake_allowed?(pool, nil) do
Decimal.positive?(pool.self_staked_amount)
end
defp stake_allowed?(pool, delegator) do
Decimal.positive?(pool.self_staked_amount) or delegator.delegator_address_hash == pool.staking_address_hash
end
defp move_allowed?(nil), do: false
defp move_allowed?(delegator) do

@ -26,7 +26,9 @@
<div class="input-group form-group">
<select pool-select class="form-control">
<option disabled <%= unless @pool_to do "selected" end %>><%= gettext("Choose Pool") %></option>
<%= for %{pool: pool} <- @pools, pool.staking_address_hash != @pool_from.staking_address_hash do %>
<%= for %{pool: pool} <- @pools,
pool.staking_address_hash != @pool_from.staking_address_hash,
Decimal.positive?(pool.self_staked_amount) or pool.staking_address_hash == @delegator.delegator_address_hash do %>
<option
value="<%= pool.staking_address_hash %>"
<%= if @pool_to && pool.staking_address_hash == @pool_to.staking_address_hash do "selected" end %>

@ -8,7 +8,9 @@
<!-- Buttons -->
<div class="stakes-top-buttons">
<%= if @account[:pool] && @account.pool.is_active do %>
<%= render BlockScoutWeb.StakesView, "_stakes_btn_remove_pool.html", text: gettext("Remove My Pool"), extra_class: "js-remove-pool" %>
<%= unless @account.pool.is_unremovable do %>
<%= render BlockScoutWeb.StakesView, "_stakes_btn_remove_pool.html", text: gettext("Remove My Pool"), extra_class: "js-remove-pool" %>
<% end %>
<% else %>
<%= render BlockScoutWeb.CommonComponentsView, "_btn_add_full.html", text: gettext("Become a Candidate"), extra_class: "js-become-candidate" %>
<% end %>

@ -127,6 +127,7 @@ defmodule Explorer.Chain.Import.Runner.StakingPools do
is_active: fragment("EXCLUDED.is_active"),
is_banned: fragment("EXCLUDED.is_banned"),
is_validator: fragment("EXCLUDED.is_validator"),
is_unremovable: fragment("EXCLUDED.is_unremovable"),
likelihood: fragment("EXCLUDED.likelihood"),
block_reward_ratio: fragment("EXCLUDED.block_reward_ratio"),
staked_ratio: fragment("EXCLUDED.staked_ratio"),

@ -20,6 +20,7 @@ defmodule Explorer.Chain.StakingPool do
is_active: boolean,
is_banned: boolean,
is_validator: boolean,
is_unremovable: boolean,
likelihood: Decimal.t(),
block_reward_ratio: Decimal.t(),
staked_ratio: Decimal.t(),
@ -34,11 +35,12 @@ defmodule Explorer.Chain.StakingPool do
is_active delegators_count staked_amount self_staked_amount is_validator
was_validator_count is_banned was_banned_count banned_until likelihood
staked_ratio staking_address_hash mining_address_hash block_reward_ratio
is_unremovable
)a
@req_attrs ~w(
is_active delegators_count staked_amount self_staked_amount is_validator
was_validator_count is_banned was_banned_count banned_until
staking_address_hash mining_address_hash
staking_address_hash mining_address_hash is_unremovable
)a
schema "staking_pools" do
@ -47,6 +49,7 @@ defmodule Explorer.Chain.StakingPool do
field(:is_active, :boolean, default: false)
field(:is_banned, :boolean, default: false)
field(:is_validator, :boolean, default: false)
field(:is_unremovable, :boolean, default: false)
field(:likelihood, :decimal)
field(:block_reward_ratio, :decimal)
field(:staked_ratio, :decimal)

@ -17,7 +17,8 @@ defmodule Explorer.Staking.ContractReader do
inactive_pools: {:staking, "getPoolsInactive", []},
pools_likely: {:staking, "getPoolsToBeElected", []},
pools_likelihood: {:staking, "getPoolsLikelihood", []},
validators: {:validator_set, "getValidators", []}
validators: {:validator_set, "getValidators", []},
unremovable_validator: {:validator_set, "unremovableValidator", []}
]
end
@ -37,7 +38,6 @@ defmodule Explorer.Staking.ContractReader do
def pool_mining_requests(mining_address) do
[
is_validator: {:validator_set, "isValidator", [mining_address]},
was_validator_count: {:validator_set, "validatorCounter", [mining_address]},
is_banned: {:validator_set, "isValidatorBanned", [mining_address]},
banned_until: {:validator_set, "bannedUntil", [mining_address]},

@ -128,6 +128,8 @@ defmodule Explorer.Staking.ContractState do
:ets.insert(@table_name, settings)
pools = global_responses.active_pools ++ global_responses.inactive_pools
is_validator = Enum.into(global_responses.validators, %{}, &{hash_to_string(&1), true})
unremovable_validator = global_responses.unremovable_validator
pool_staking_responses =
pools
@ -179,7 +181,9 @@ defmodule Explorer.Staking.ContractState do
end,
likelihood: ratio(likelihood[staking_address] || 0, total_likelihood),
block_reward_ratio: staking_response.block_reward / 10_000,
is_deleted: false
is_deleted: false,
is_validator: is_validator[staking_response.mining_address_hash] || false,
is_unremovable: hash_to_string(staking_address) == unremovable_validator
}
|> Map.merge(
Map.take(staking_response, [
@ -191,7 +195,6 @@ defmodule Explorer.Staking.ContractState do
)
|> Map.merge(
Map.take(mining_response, [
:is_validator,
:was_validator_count,
:is_banned,
:banned_until,
@ -203,10 +206,9 @@ defmodule Explorer.Staking.ContractState do
delegator_entries =
Enum.map(delegator_responses, fn {{pool_address, delegator_address, is_active}, response} ->
staking_response = pool_staking_responses[pool_address]
mining_response = pool_mining_responses[pool_address]
reward_ratio =
if mining_response.is_validator do
if is_validator[staking_response.mining_address_hash] do
reward_ratio = delegator_rewards[pool_address][delegator_address]
if reward_ratio do
@ -304,6 +306,8 @@ defmodule Explorer.Staking.ContractState do
defp ratio(_numerator, 0), do: 0
defp ratio(numerator, denominator), do: numerator / denominator * 100
defp hash_to_string(hash), do: "0x" <> Base.encode16(hash, case: :lower)
# sobelow_skip ["Traversal"]
defp abi(file_name) do
:explorer

@ -0,0 +1,9 @@
defmodule Explorer.Repo.Migrations.AddUnremovableToStakingPools do
use Ecto.Migration
def change do
alter table(:staking_pools) do
add(:is_unremovable, :boolean, default: false, null: false)
end
end
end

@ -76,7 +76,7 @@ defmodule Explorer.Staking.ContractStateTest do
EthereumJSONRPC.Mox,
:json_rpc,
fn requests, _opts ->
assert length(requests) == 11
assert length(requests) == 12
{:ok,
format_responses([
@ -90,7 +90,8 @@ defmodule Explorer.Staking.ContractStateTest do
"0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000b6695f5c2e3f5eff8036b5f5f3a9d83a5310e51e",
"0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000b916e7e1f4bcb13549602ed042d36746fd0d96c9000000000000000000000000db9cb2478d917719c53862008672166808258577",
"0x00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000514000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000044c",
"0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000bbcaa8d48289bb1ffcf9808d9aa4b1d215054c78000000000000000000000000f67cc5231c5858ad6cc87b105217426e17b824bb000000000000000000000000be69eb0968226a1808975e1a1f2127667f2bffb3"
"0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000bbcaa8d48289bb1ffcf9808d9aa4b1d215054c78000000000000000000000000f67cc5231c5858ad6cc87b105217426e17b824bb000000000000000000000000be69eb0968226a1808975e1a1f2127667f2bffb3",
"0x0000000000000000000000000b2f5e2f3cbd864eaa2c642e3769c1582361caf6"
])}
end
)
@ -147,26 +148,22 @@ defmodule Explorer.Staking.ContractStateTest do
EthereumJSONRPC.Mox,
:json_rpc,
fn requests, _opts ->
assert length(requests) == 20
assert length(requests) == 16
{:ok,
format_responses([
"0x0000000000000000000000000000000000000000000000000000000000000001",
"0x000000000000000000000000000000000000000000000000000000000000004b",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000002",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000001",
"0x000000000000000000000000000000000000000000000000000000000000004a",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000001",
"0x000000000000000000000000000000000000000000000000000000000000004a",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",

Loading…
Cancel
Save