<%= render BlockScoutWeb.CommonComponentsView, "_modal_close_button.html" %>
@@ -49,15 +49,15 @@
<%=
title =
if @show_snapshotted_data do
- gettext("Potential Reward Percent") <> "
(" <> gettext("Current Reward Percent") <> ")"
+ gettext("Potential Reward Share") <> "
(" <> gettext("Current Reward Share") <> ")"
else
- gettext("Potential Reward Percent")
+ gettext("Potential Reward Share")
end
tooltip =
- gettext("Reward distribution is based on stake amount. Validator receives a minimum of %{min}%.", min: @validator_min_reward_percent) <>
+ gettext("Reward distribution is based on stake amount. Validator receives at least %{min}% of the pool reward.", min: @validator_min_reward_percent) <>
if @show_snapshotted_data do
- " " <> gettext("Current Reward Percent is calculated based on the Working Stake Amount.")
+ " " <> gettext("Current Reward Share is calculated based on the Working Stake Amount.")
else
""
end
diff --git a/apps/explorer/lib/explorer/staking/contract_reader.ex b/apps/explorer/lib/explorer/staking/contract_reader.ex
index 3cf659d8d3..29e987930b 100644
--- a/apps/explorer/lib/explorer/staking/contract_reader.ex
+++ b/apps/explorer/lib/explorer/staking/contract_reader.ex
@@ -19,6 +19,8 @@ defmodule Explorer.Staking.ContractReader do
inactive_pools: {:staking, "df6f55f5", [], block_number},
# f0786096 = keccak256(MAX_CANDIDATES())
max_candidates: {:staking, "f0786096", [], block_number},
+ # 714897df = keccak256(MAX_VALIDATORS())
+ max_validators: {:validator_set, "714897df", [], block_number},
# 5fef7643 = keccak256(candidateMinStake())
min_candidate_stake: {:staking, "5fef7643", [], block_number},
# da7a9b6a = keccak256(delegatorMinStake())
diff --git a/apps/explorer/lib/explorer/staking/contract_state.ex b/apps/explorer/lib/explorer/staking/contract_state.ex
index 8313525fdc..5d697d5c17 100644
--- a/apps/explorer/lib/explorer/staking/contract_state.ex
+++ b/apps/explorer/lib/explorer/staking/contract_state.ex
@@ -27,6 +27,7 @@ defmodule Explorer.Staking.ContractState do
:max_candidates,
:min_candidate_stake,
:min_delegator_stake,
+ :seen_block,
:snapshotted_epoch_number,
:staking_allowed,
:staking_contract,
@@ -37,12 +38,15 @@ defmodule Explorer.Staking.ContractState do
:validator_set_contract
]
- # frequency in blocks
+ # token renewal frequency in blocks
@token_renew_frequency 10
+ # eth_subscribe max delay to switch to eth_blockNumber, in seconds
+ @eth_subscribe_max_delay 60
+
defstruct [
- :seen_block,
:snapshotting_finished,
+ :timer,
:contracts,
:abi
]
@@ -103,8 +107,8 @@ defmodule Explorer.Staking.ContractState do
})
state = %__MODULE__{
- seen_block: 0,
snapshotting_finished: false,
+ timer: nil,
contracts: %{
staking: staking_contract_address,
validator_set: validator_set_contract_address,
@@ -117,6 +121,7 @@ defmodule Explorer.Staking.ContractState do
block_reward_contract: %{abi: block_reward_abi, address: block_reward_contract_address},
is_snapshotting: false,
last_change_block: 0,
+ seen_block: 0,
snapshotted_epoch_number: -1,
staking_contract: %{abi: staking_abi, address: staking_contract_address},
token_contract: %{abi: token_abi, address: token_contract_address},
@@ -128,7 +133,10 @@ defmodule Explorer.Staking.ContractState do
end
def handle_continue(_, state) do
- {:noreply, state}
+ # if eth_subscribe doesn't work during the first @eth_subscribe_max_delay seconds
+ # after server start, use eth_blockNumber
+ timer = Process.send_after(self(), :eth_subscribe_stopped, @eth_subscribe_max_delay * 1000)
+ {:noreply, %{state | timer: timer}}
end
# handles an event about snapshotting finishing
@@ -136,104 +144,122 @@ defmodule Explorer.Staking.ContractState do
{:noreply, %{state | snapshotting_finished: true}}
end
- # handles new blocks and decides to fetch fresh chain info
+ # received when eth_subscribe is stopped
+ def handle_info(:eth_subscribe_stopped, state) do
+ state =
+ if Process.read_timer(state.timer) == false do
+ {microseconds, state} =
+ :timer.tc(
+ fn st -> fetch_state(st, get_current_block_number()) end,
+ [state]
+ )
+
+ # sleep up to 500 ms before the next eth_blockNumber request
+ Process.send_after(self(), :eth_subscribe_stopped, max(500 - round(microseconds / 1000), 0))
+
+ state
+ else
+ state
+ end
+
+ {:noreply, state}
+ end
+
+ # catches a new block number from eth_subscribe
def handle_info({:chain_event, :last_block_number, :realtime, block_number}, state) do
- if block_number > state.seen_block do
+ if state.timer != nil do
+ Process.cancel_timer(state.timer)
+ end
+
+ state = fetch_state(state, block_number)
+
+ timer = Process.send_after(self(), :eth_subscribe_stopped, @eth_subscribe_max_delay * 1000)
+ {:noreply, %{state | timer: timer}}
+ end
+
+ # handles new block and decides to fetch fresh chain info
+ # credo:disable-for-next-line
+ defp fetch_state(state, block_number) do
+ if block_number <= get(:seen_block) do
+ state
+ else
# read general info from the contracts (including pool list and validator list)
global_responses =
ContractReader.perform_requests(ContractReader.global_requests(block_number), state.contracts, state.abi)
epoch_very_beginning = global_responses.epoch_start_block == block_number + 1
- if global_responses.epoch_number > get(:epoch_number) and not epoch_very_beginning and state.seen_block > 0 do
- # if the previous staking epoch finished and we have blocks gap,
- # call fetch_state in a loop until the blocks gap is closed
- loop_block_start = state.seen_block + 1
- loop_block_end = block_number - 1
+ first_fetch = get(:epoch_end_block, 0) == 0
- if loop_block_end >= loop_block_start do
- for bn <- loop_block_start..loop_block_end do
- gr = ContractReader.perform_requests(ContractReader.global_requests(bn), state.contracts, state.abi)
- fetch_state(state, gr, bn, gr.epoch_start_block == bn + 1)
- end
- end
- end
+ validator_min_reward_percent =
+ get_validator_min_reward_percent(global_responses.epoch_number, block_number, state.contracts, state.abi)
- fetch_state(state, global_responses, block_number, epoch_very_beginning)
+ start_snapshotting =
+ global_responses.epoch_number > get(:snapshotted_epoch_number) && global_responses.epoch_number > 0 &&
+ not get(:is_snapshotting)
- state =
- if state.snapshotting_finished do
- %{state | snapshotting_finished: false}
- else
- state
- end
+ active_pools_length = Enum.count(global_responses.active_pools)
- {:noreply, %{state | seen_block: block_number}}
- else
- {:noreply, state}
- end
- end
+ # determine if something changed in contracts state since the previous seen block.
+ # if something changed or the `fetch_state` function is called for the first time
+ # or we are at the beginning of staking epoch or snapshotting recently finished
+ # then we should update database
+ last_change_block =
+ max(global_responses.staking_last_change_block, global_responses.validator_set_last_change_block)
- defp fetch_state(state, global_responses, block_number, epoch_very_beginning) do
- contracts = state.contracts
- abi = state.abi
- snapshotting_finished = state.snapshotting_finished
- first_fetch = get(:epoch_end_block, 0) == 0
-
- validator_min_reward_percent =
- get_validator_min_reward_percent(global_responses.epoch_number, block_number, contracts, abi)
-
- start_snapshotting =
- global_responses.epoch_number > get(:snapshotted_epoch_number) && global_responses.epoch_number > 0 &&
- not get(:is_snapshotting)
-
- active_pools_length = Enum.count(global_responses.active_pools)
-
- # determine if something changed in contracts state since the previous seen block.
- # if something changed or the `fetch_state` function is called for the first time
- # or we are at the beginning of staking epoch or snapshotting recently finished
- # then we should update database
- last_change_block =
- max(global_responses.staking_last_change_block, global_responses.validator_set_last_change_block)
-
- should_update_db =
- start_snapshotting or snapshotting_finished or first_fetch or last_change_block > get(:last_change_block)
-
- # save the general info to ETS (excluding pool list and validator list)
- settings =
- global_responses
- |> get_settings(validator_min_reward_percent, block_number)
- |> Enum.concat(active_pools_length: active_pools_length)
- |> Enum.concat(last_change_block: last_change_block)
-
- :ets.insert(@table_name, settings)
-
- if epoch_very_beginning or start_snapshotting do
- # if the block_number is the latest block of the finished staking epoch
- # or we are starting Blockscout server, the BlockRewardAuRa contract balance
- # could increase before (without Mint/Transfer events),
- # so we need to update its balance in database
- update_block_reward_balance(block_number)
- end
+ should_update_db =
+ start_snapshotting or state.snapshotting_finished or first_fetch or last_change_block > get(:last_change_block)
- # we should update database as something changed in contracts state
- if should_update_db do
- update_database(epoch_very_beginning, start_snapshotting, global_responses, contracts, abi, block_number)
- end
+ # save the general info to ETS (excluding pool list and validator list)
+ settings =
+ global_responses
+ |> get_settings(validator_min_reward_percent, block_number)
+ |> Enum.concat(active_pools_length: active_pools_length)
+ |> Enum.concat(last_change_block: last_change_block)
+ |> Enum.concat(seen_block: block_number)
- # notify the UI about a new block
- data = %{
- active_pools_length: active_pools_length,
- block_number: block_number,
- epoch_end_block: global_responses.epoch_end_block,
- epoch_number: global_responses.epoch_number,
- max_candidates: global_responses.max_candidates,
- staking_allowed: global_responses.staking_allowed,
- staking_token_defined: get(:token, nil) != nil,
- validator_set_apply_block: global_responses.validator_set_apply_block
- }
+ :ets.insert(@table_name, settings)
+
+ if epoch_very_beginning or start_snapshotting do
+ # if the block_number is the latest block of the finished staking epoch
+ # or we are starting Blockscout server, the BlockRewardAuRa contract balance
+ # could increase before (without Mint/Transfer events),
+ # so we need to update its balance in database
+ update_block_reward_balance(block_number)
+ end
+
+ # we should update database as something changed in contracts state
+ if should_update_db do
+ update_database(
+ epoch_very_beginning,
+ start_snapshotting,
+ global_responses,
+ state.contracts,
+ state.abi,
+ block_number
+ )
+ end
+
+ # notify the UI about a new block
+ data = %{
+ active_pools_length: active_pools_length,
+ block_number: block_number,
+ epoch_end_block: global_responses.epoch_end_block,
+ epoch_number: global_responses.epoch_number,
+ max_candidates: global_responses.max_candidates,
+ staking_allowed: global_responses.staking_allowed,
+ staking_token_defined: get(:token, nil) != nil,
+ validator_set_apply_block: global_responses.validator_set_apply_block
+ }
+
+ Publisher.broadcast([{:staking_update, data}], :realtime)
- Publisher.broadcast([{:staking_update, data}], :realtime)
+ if state.snapshotting_finished do
+ %{state | snapshotting_finished: false}
+ else
+ state
+ end
+ end
end
defp update_database(epoch_very_beginning, start_snapshotting, global_responses, contracts, abi, block_number) do
@@ -295,9 +321,6 @@ defmodule Explorer.Staking.ContractState do
block_number
)
- # calculate total amount staked into all active pools
- staked_total = Enum.sum(for {_, pool} <- pool_staking_responses, pool.is_active, do: pool.total_staked_amount)
-
# calculate likelihood of becoming a validator on the next epoch
[likelihood_values, total_likelihood] = global_responses.pools_likelihood
# array of pool addresses (staking addresses)
@@ -332,8 +355,7 @@ defmodule Explorer.Staking.ContractState do
global_responses: global_responses,
snapshotted_epoch_number: snapshotted_epoch_number,
likelihood: likelihood,
- total_likelihood: total_likelihood,
- staked_total: staked_total
+ total_likelihood: total_likelihood
})
# perform SQL queries
@@ -356,6 +378,25 @@ defmodule Explorer.Staking.ContractState do
end
end
+ defp get_current_block_number do
+ json_rpc_named_arguments = Application.get_env(:explorer, :json_rpc_named_arguments)
+
+ result =
+ %{id: 0, method: "eth_blockNumber", params: []}
+ |> EthereumJSONRPC.request()
+ |> EthereumJSONRPC.json_rpc(json_rpc_named_arguments)
+
+ case result do
+ {:ok, response} ->
+ response
+ |> String.replace_leading("0x", "")
+ |> String.to_integer(16)
+
+ _ ->
+ 0
+ end
+ end
+
defp get_settings(global_responses, validator_min_reward_percent, block_number) do
base_settings = get_base_settings(global_responses, validator_min_reward_percent)
@@ -600,9 +641,11 @@ defmodule Explorer.Staking.ContractState do
global_responses: global_responses,
snapshotted_epoch_number: snapshotted_epoch_number,
likelihood: likelihood,
- total_likelihood: total_likelihood,
- staked_total: staked_total
+ total_likelihood: total_likelihood
}) do
+ # total amount staked into all active pools
+ staked_total = Enum.sum(for {_, pool} <- pool_staking_responses, pool.is_active, do: pool.total_staked_amount)
+
Enum.map(pools, fn pool_staking_address ->
staking_resp = pool_staking_responses[pool_staking_address]
mining_resp = pool_mining_responses[pool_staking_address]
@@ -622,6 +665,15 @@ defmodule Explorer.Staking.ContractState do
0
end
+ is_unremovable = address_bytes_to_string(pool_staking_address) == global_responses.unremovable_validator
+
+ likelihood_value =
+ if get(:active_pools_length) > global_responses.max_validators and not is_unremovable do
+ ratio(likelihood[pool_staking_address] || 0, total_likelihood)
+ else
+ 100
+ end
+
%{
staking_address_hash: pool_staking_address,
delegators_count: delegators_count,
@@ -632,11 +684,11 @@ defmodule Explorer.Staking.ContractState do
0
end,
validator_reward_ratio: Float.floor(candidate_reward_resp.validator_share / 10_000, 2),
- likelihood: ratio(likelihood[pool_staking_address] || 0, total_likelihood),
+ likelihood: likelihood_value,
validator_reward_percent: staking_resp.validator_reward_percent / 10_000,
is_deleted: false,
is_validator: is_validator,
- is_unremovable: address_bytes_to_string(pool_staking_address) == global_responses.unremovable_validator,
+ is_unremovable: is_unremovable,
ban_reason: binary_to_string(mining_resp.ban_reason)
}
|> Map.merge(
diff --git a/apps/explorer/test/explorer/staking/contract_state_test.exs b/apps/explorer/test/explorer/staking/contract_state_test.exs
index 281cefe3b8..6a33bbe604 100644
--- a/apps/explorer/test/explorer/staking/contract_state_test.exs
+++ b/apps/explorer/test/explorer/staking/contract_state_test.exs
@@ -99,7 +99,7 @@ defmodule Explorer.Staking.ContractStateTest do
EthereumJSONRPC.Mox,
:json_rpc,
fn requests, _opts ->
- assert length(requests) == 17
+ assert length(requests) == 18
{:ok,
format_responses([
@@ -114,28 +114,30 @@ defmodule Explorer.Staking.ContractStateTest do
# 5 StakingAuRa.getPoolsInactive
"0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000",
# 6 StakingAuRa.MAX_CANDIDATES
- "0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000bb8",
- # 7 StakingAuRa.candidateMinStake
+ "0x0000000000000000000000000000000000000000000000000000000000000bb8",
+ # 7 StakingAuRa.MAX_VALIDATORS
+ "0x0000000000000000000000000000000000000000000000000000000000000013",
+ # 8 StakingAuRa.candidateMinStake
"0x0000000000000000000000000000000000000000000000000de0b6b3a7640000",
- # 8 StakingAuRa.delegatorMinStake
+ # 9 StakingAuRa.delegatorMinStake
"0x0000000000000000000000000000000000000000000000000de0b6b3a7640000",
- # 9 StakingAuRa.getPoolsLikelihood
+ # 10 StakingAuRa.getPoolsLikelihood
"0x0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000de0b6b3a7640000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000098a7d9b8314c000000000000000000000000000000000000000000000000000029a2241af62c0000",
- # 10 StakingAuRa.getPoolsToBeElected
+ # 11 StakingAuRa.getPoolsToBeElected
"0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000b916e7e1f4bcb13549602ed042d36746fd0d96c9000000000000000000000000db9cb2478d917719c53862008672166808258577000000000000000000000000b6695f5c2e3f5eff8036b5f5f3a9d83a5310e51e",
- # 11 StakingAuRa.areStakeAndWithdrawAllowed
+ # 12 StakingAuRa.areStakeAndWithdrawAllowed
"0x0000000000000000000000000000000000000000000000000000000000000000",
- # 12 StakingAuRa.lastChangeBlock
+ # 13 StakingAuRa.lastChangeBlock
"0x0000000000000000000000000000000000000000000000000000000000000000",
- # 13 StakingAuRa.erc677TokenContract
+ # 14 StakingAuRa.erc677TokenContract
"0x0000000000000000000000006f7a73c96bd56f8b0debc795511eda135e105ea3",
- # 14 ValidatorSetAuRa.unremovableValidator
+ # 15 ValidatorSetAuRa.unremovableValidator
"0x0000000000000000000000000b2f5e2f3cbd864eaa2c642e3769c1582361caf6",
- # 15 ValidatorSetAuRa.getValidators
+ # 16 ValidatorSetAuRa.getValidators
"0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000bbcaa8d48289bb1ffcf9808d9aa4b1d215054c7800000000000000000000000075df42383afe6bf5194aa8fa0e9b3d5f9e869441000000000000000000000000522df396ae70a058bd69778408630fdb023389b2",
- # 16 ValidatorSetAuRa.validatorSetApplyBlock
+ # 17 ValidatorSetAuRa.validatorSetApplyBlock
"0x0000000000000000000000000000000000000000000000000000000000000000",
- # 17 ValidatorSetAuRa.lastChangeBlock
+ # 18 ValidatorSetAuRa.lastChangeBlock
"0x0000000000000000000000000000000000000000000000000000000000000000"
])}
end
diff --git a/apps/indexer/lib/indexer/block/realtime/fetcher.ex b/apps/indexer/lib/indexer/block/realtime/fetcher.ex
index f6ad8a304a..ac064f4878 100644
--- a/apps/indexer/lib/indexer/block/realtime/fetcher.ex
+++ b/apps/indexer/lib/indexer/block/realtime/fetcher.ex
@@ -86,7 +86,11 @@ defmodule Indexer.Block.Realtime.Fetcher do
)
when is_binary(quantity) do
number = quantity_to_integer(quantity)
- Publisher.broadcast([{:last_block_number, number}], :realtime)
+
+ if number > 0 do
+ Publisher.broadcast([{:last_block_number, number}], :realtime)
+ end
+
# Subscriptions don't support getting all the blocks and transactions data,
# so we need to go back and get the full block
start_fetch_and_import(number, block_fetcher, previous_number, max_number_seen)
From aef62285fa496cca03d4b4ceade9c1498473d723 Mon Sep 17 00:00:00 2001
From: POA <33550681+poa@users.noreply.github.com>
Date: Fri, 22 Jan 2021 15:39:02 +0300
Subject: [PATCH 2/5] mix gettext.extract
---
apps/block_scout_web/priv/gettext/default.pot | 56 ++++++++++---------
.../priv/gettext/en/LC_MESSAGES/default.po | 56 ++++++++++---------
2 files changed, 60 insertions(+), 52 deletions(-)
diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot
index 35afcd3684..a597bd0776 100644
--- a/apps/block_scout_web/priv/gettext/default.pot
+++ b/apps/block_scout_web/priv/gettext/default.pot
@@ -2126,18 +2126,6 @@ msgstr ""
msgid "Claim the Amount"
msgstr ""
-#, elixir-format
-#:
-#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:52
-msgid "Current Reward Percent"
-msgstr ""
-
-#, elixir-format
-#:
-#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:60
-msgid "Current Reward Percent is calculated based on the Working Stake Amount."
-msgstr ""
-
#, elixir-format
#:
#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:32
@@ -2156,7 +2144,6 @@ msgid "DApp for Staking %{symbol} tokens"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:5
#: lib/block_scout_web/templates/stakes/_stakes_progress.html.eex:31
#: lib/block_scout_web/templates/stakes/_table.html.eex:38
msgid "Delegators"
@@ -2284,13 +2271,6 @@ msgstr ""
msgid "Pools searching is already in progress for this address"
msgstr ""
-#, elixir-format
-#:
-#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:52
-#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:54
-msgid "Potential Reward Percent"
-msgstr ""
-
#, elixir-format
#: lib/block_scout_web/templates/stakes/_stakes_modal_pool_info.html.eex:16
msgid "Reason for Ban: %{ban_reason}"
@@ -2317,12 +2297,6 @@ msgstr ""
msgid "Reward calculating is already in progress for this address"
msgstr ""
-#, elixir-format
-#:
-#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:58
-msgid "Reward distribution is based on stake amount. Validator receives a minimum of %{min}%."
-msgstr ""
-
#, elixir-format
#: lib/block_scout_web/templates/stakes/_stakes_modal_claim_reward.html.eex:9
msgid "Searching for pools you have ever staked into. Please, wait..."
@@ -2658,3 +2632,33 @@ msgstr ""
#: lib/block_scout_web/templates/layout/_topnav.html.eex:247
msgid "Press / and focus will be moved to the search field"
msgstr ""
+
+#, elixir-format
+#:
+#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:52
+msgid "Current Reward Share"
+msgstr ""
+
+#, elixir-format
+#:
+#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:60
+msgid "Current Reward Share is calculated based on the Working Stake Amount."
+msgstr ""
+
+#, elixir-format
+#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:5
+msgid "Delegators of "
+msgstr ""
+
+#, elixir-format
+#:
+#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:52
+#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:54
+msgid "Potential Reward Share"
+msgstr ""
+
+#, elixir-format
+#:
+#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:58
+msgid "Reward distribution is based on stake amount. Validator receives at least %{min}% of the pool reward."
+msgstr ""
diff --git a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
index 35afcd3684..7543fa4c8b 100644
--- a/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
+++ b/apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
@@ -2126,18 +2126,6 @@ msgstr ""
msgid "Claim the Amount"
msgstr ""
-#, elixir-format
-#:
-#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:52
-msgid "Current Reward Percent"
-msgstr ""
-
-#, elixir-format
-#:
-#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:60
-msgid "Current Reward Percent is calculated based on the Working Stake Amount."
-msgstr ""
-
#, elixir-format
#:
#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:32
@@ -2156,7 +2144,6 @@ msgid "DApp for Staking %{symbol} tokens"
msgstr ""
#, elixir-format
-#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:5
#: lib/block_scout_web/templates/stakes/_stakes_progress.html.eex:31
#: lib/block_scout_web/templates/stakes/_table.html.eex:38
msgid "Delegators"
@@ -2284,13 +2271,6 @@ msgstr ""
msgid "Pools searching is already in progress for this address"
msgstr ""
-#, elixir-format
-#:
-#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:52
-#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:54
-msgid "Potential Reward Percent"
-msgstr ""
-
#, elixir-format
#: lib/block_scout_web/templates/stakes/_stakes_modal_pool_info.html.eex:16
msgid "Reason for Ban: %{ban_reason}"
@@ -2317,12 +2297,6 @@ msgstr ""
msgid "Reward calculating is already in progress for this address"
msgstr ""
-#, elixir-format
-#:
-#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:58
-msgid "Reward distribution is based on stake amount. Validator receives a minimum of %{min}%."
-msgstr ""
-
#, elixir-format
#: lib/block_scout_web/templates/stakes/_stakes_modal_claim_reward.html.eex:9
msgid "Searching for pools you have ever staked into. Please, wait..."
@@ -2658,3 +2632,33 @@ msgstr ""
#: lib/block_scout_web/templates/layout/_topnav.html.eex:247
msgid "Press / and focus will be moved to the search field"
msgstr ""
+
+#, elixir-format, fuzzy
+#:
+#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:52
+msgid "Current Reward Share"
+msgstr ""
+
+#, elixir-format, fuzzy
+#:
+#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:60
+msgid "Current Reward Share is calculated based on the Working Stake Amount."
+msgstr ""
+
+#, elixir-format, fuzzy
+#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:5
+msgid "Delegators of "
+msgstr ""
+
+#, elixir-format, fuzzy
+#:
+#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:52
+#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:54
+msgid "Potential Reward Share"
+msgstr ""
+
+#, elixir-format, fuzzy
+#:
+#: lib/block_scout_web/templates/stakes/_stakes_modal_delegators_list.html.eex:58
+msgid "Reward distribution is based on stake amount. Validator receives at least %{min}% of the pool reward."
+msgstr ""
From 5fdaad576a0c9ff609e73ba6061afe02e6bc3e33 Mon Sep 17 00:00:00 2001
From: POA <33550681+poa@users.noreply.github.com>
Date: Fri, 22 Jan 2021 15:51:07 +0300
Subject: [PATCH 3/5] Update CHANGELOG
---
CHANGELOG.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c3ebb44d87..ea1254e01c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@
- [#3583](https://github.com/poanetwork/blockscout/pull/3583) - Reduce RPC requests and DB changes by Staking DApp
### Chore
+- [#3585](https://github.com/poanetwork/blockscout/pull/3585) - Add autoswitching from eth_subscribe to eth_blockNumber in Staking DApp
- [#3574](https://github.com/poanetwork/blockscout/pull/3574) - Correct UNI token price
- [#3567](https://github.com/poanetwork/blockscout/pull/3567) - Force to show filter at the page where filtered items list is empty
- [#3565](https://github.com/poanetwork/blockscout/pull/3565) - Staking dapp: unhealthy state alert message
From 00f394304eb8693015014b423b3d8a4bc1f8601f Mon Sep 17 00:00:00 2001
From: POA <33550681+poa@users.noreply.github.com>
Date: Tue, 26 Jan 2021 10:18:18 +0300
Subject: [PATCH 4/5] Staking DApp server side refactoring
---
apps/explorer/config/config.exs | 4 +-
.../lib/explorer/staking/contract_state.ex | 170 ++++++++++--------
.../explorer/staking/contract_state_test.exs | 4 +-
3 files changed, 101 insertions(+), 77 deletions(-)
diff --git a/apps/explorer/config/config.exs b/apps/explorer/config/config.exs
index a6290bdfb2..e9bca62003 100644
--- a/apps/explorer/config/config.exs
+++ b/apps/explorer/config/config.exs
@@ -224,7 +224,9 @@ config :explorer, Explorer.Chain.Block.Reward,
if System.get_env("POS_STAKING_CONTRACT") do
config :explorer, Explorer.Staking.ContractState,
enabled: true,
- staking_contract_address: System.get_env("POS_STAKING_CONTRACT")
+ staking_contract_address: System.get_env("POS_STAKING_CONTRACT"),
+ eth_subscribe_max_delay: System.get_env("POS_ETH_SUBSCRIBE_MAX_DELAY", "60"),
+ eth_blocknumber_pull_interval: System.get_env("POS_ETH_BLOCKNUMBER_PULL_INTERVAL", "500")
else
config :explorer, Explorer.Staking.ContractState, enabled: false
end
diff --git a/apps/explorer/lib/explorer/staking/contract_state.ex b/apps/explorer/lib/explorer/staking/contract_state.ex
index 5d697d5c17..9026411667 100644
--- a/apps/explorer/lib/explorer/staking/contract_state.ex
+++ b/apps/explorer/lib/explorer/staking/contract_state.ex
@@ -41,10 +41,9 @@ defmodule Explorer.Staking.ContractState do
# token renewal frequency in blocks
@token_renew_frequency 10
- # eth_subscribe max delay to switch to eth_blockNumber, in seconds
- @eth_subscribe_max_delay 60
-
defstruct [
+ :eth_blocknumber_pull_interval,
+ :eth_subscribe_max_delay,
:snapshotting_finished,
:timer,
:contracts,
@@ -82,7 +81,15 @@ defmodule Explorer.Staking.ContractState do
block_reward_abi = abi("BlockRewardAuRa")
token_abi = abi("Token")
- staking_contract_address = Application.get_env(:explorer, __MODULE__)[:staking_contract_address]
+ module_env = Application.get_env(:explorer, __MODULE__)
+
+ # eth_blockNumber pull interval, in milliseconds
+ eth_blocknumber_pull_interval = String.to_integer(module_env[:eth_blocknumber_pull_interval])
+
+ # eth_subscribe max delay to switch to eth_blockNumber, in seconds
+ eth_subscribe_max_delay = String.to_integer(module_env[:eth_subscribe_max_delay])
+
+ staking_contract_address = module_env[:staking_contract_address]
# 2d21d217 = keccak256(erc677TokenContract())
erc_677_token_contract_signature = "2d21d217"
@@ -107,6 +114,8 @@ defmodule Explorer.Staking.ContractState do
})
state = %__MODULE__{
+ eth_blocknumber_pull_interval: eth_blocknumber_pull_interval,
+ eth_subscribe_max_delay: eth_subscribe_max_delay,
snapshotting_finished: false,
timer: nil,
contracts: %{
@@ -133,9 +142,9 @@ defmodule Explorer.Staking.ContractState do
end
def handle_continue(_, state) do
- # if eth_subscribe doesn't work during the first @eth_subscribe_max_delay seconds
+ # if eth_subscribe doesn't work during the first eth_subscribe_max_delay seconds
# after server start, use eth_blockNumber
- timer = Process.send_after(self(), :eth_subscribe_stopped, @eth_subscribe_max_delay * 1000)
+ timer = Process.send_after(self(), :eth_subscribe_stopped, state.eth_subscribe_max_delay * 1000)
{:noreply, %{state | timer: timer}}
end
@@ -154,8 +163,12 @@ defmodule Explorer.Staking.ContractState do
[state]
)
- # sleep up to 500 ms before the next eth_blockNumber request
- Process.send_after(self(), :eth_subscribe_stopped, max(500 - round(microseconds / 1000), 0))
+ # sleep up to eth_blocknumber_pull_interval milliseconds before the next eth_blockNumber request
+ Process.send_after(
+ self(),
+ :eth_subscribe_stopped,
+ max(state.eth_blocknumber_pull_interval - round(microseconds / 1000), 0)
+ )
state
else
@@ -173,95 +186,89 @@ defmodule Explorer.Staking.ContractState do
state = fetch_state(state, block_number)
- timer = Process.send_after(self(), :eth_subscribe_stopped, @eth_subscribe_max_delay * 1000)
+ timer = Process.send_after(self(), :eth_subscribe_stopped, state.eth_subscribe_max_delay * 1000)
{:noreply, %{state | timer: timer}}
end
# handles new block and decides to fetch fresh chain info
- # credo:disable-for-next-line
defp fetch_state(state, block_number) do
if block_number <= get(:seen_block) do
state
else
- # read general info from the contracts (including pool list and validator list)
- global_responses =
- ContractReader.perform_requests(ContractReader.global_requests(block_number), state.contracts, state.abi)
-
- epoch_very_beginning = global_responses.epoch_start_block == block_number + 1
-
- first_fetch = get(:epoch_end_block, 0) == 0
+ fetch_state_internal(state, block_number)
+ end
+ end
- validator_min_reward_percent =
- get_validator_min_reward_percent(global_responses.epoch_number, block_number, state.contracts, state.abi)
+ defp fetch_state_internal(state, block_number) do
+ # read general info from the contracts (including pool list and validator list)
+ global_responses =
+ ContractReader.perform_requests(ContractReader.global_requests(block_number), state.contracts, state.abi)
- start_snapshotting =
- global_responses.epoch_number > get(:snapshotted_epoch_number) && global_responses.epoch_number > 0 &&
- not get(:is_snapshotting)
+ epoch_very_beginning = global_responses.epoch_start_block == block_number + 1
- active_pools_length = Enum.count(global_responses.active_pools)
+ start_snapshotting = start_snapshotting?(global_responses)
- # determine if something changed in contracts state since the previous seen block.
- # if something changed or the `fetch_state` function is called for the first time
- # or we are at the beginning of staking epoch or snapshotting recently finished
- # then we should update database
- last_change_block =
- max(global_responses.staking_last_change_block, global_responses.validator_set_last_change_block)
+ # determine if something changed in contracts state since the previous seen block.
+ # if something changed or the `fetch_state` function is called for the first time
+ # or we are at the beginning of staking epoch or snapshotting recently finished
+ # then we should update database
+ last_change_block =
+ max(global_responses.staking_last_change_block, global_responses.validator_set_last_change_block)
- should_update_db =
- start_snapshotting or state.snapshotting_finished or first_fetch or last_change_block > get(:last_change_block)
+ first_fetch = get(:epoch_end_block, 0) == 0
- # save the general info to ETS (excluding pool list and validator list)
- settings =
- global_responses
- |> get_settings(validator_min_reward_percent, block_number)
- |> Enum.concat(active_pools_length: active_pools_length)
- |> Enum.concat(last_change_block: last_change_block)
- |> Enum.concat(seen_block: block_number)
+ should_update_db =
+ start_snapshotting or state.snapshotting_finished or first_fetch or last_change_block > get(:last_change_block)
- :ets.insert(@table_name, settings)
+ # save the general info to ETS (excluding pool list and validator list)
+ set_settings(global_responses, state, block_number, last_change_block)
- if epoch_very_beginning or start_snapshotting do
- # if the block_number is the latest block of the finished staking epoch
- # or we are starting Blockscout server, the BlockRewardAuRa contract balance
- # could increase before (without Mint/Transfer events),
- # so we need to update its balance in database
- update_block_reward_balance(block_number)
- end
+ if epoch_very_beginning or start_snapshotting do
+ # if the block_number is the latest block of the finished staking epoch
+ # or we are starting Blockscout server, the BlockRewardAuRa contract balance
+ # could increase before (without Mint/Transfer events),
+ # so we need to update its balance in database
+ update_block_reward_balance(block_number)
+ end
- # we should update database as something changed in contracts state
- if should_update_db do
- update_database(
- epoch_very_beginning,
- start_snapshotting,
- global_responses,
- state.contracts,
- state.abi,
- block_number
- )
- end
+ # we should update database as something changed in contracts state
+ if should_update_db do
+ update_database(
+ epoch_very_beginning,
+ start_snapshotting,
+ global_responses,
+ state.contracts,
+ state.abi,
+ block_number
+ )
+ end
- # notify the UI about a new block
- data = %{
- active_pools_length: active_pools_length,
- block_number: block_number,
- epoch_end_block: global_responses.epoch_end_block,
- epoch_number: global_responses.epoch_number,
- max_candidates: global_responses.max_candidates,
- staking_allowed: global_responses.staking_allowed,
- staking_token_defined: get(:token, nil) != nil,
- validator_set_apply_block: global_responses.validator_set_apply_block
- }
+ # notify the UI about a new block
+ data = %{
+ active_pools_length: get(:active_pools_length),
+ block_number: block_number,
+ epoch_end_block: global_responses.epoch_end_block,
+ epoch_number: global_responses.epoch_number,
+ max_candidates: global_responses.max_candidates,
+ staking_allowed: global_responses.staking_allowed,
+ staking_token_defined: get(:token, nil) != nil,
+ validator_set_apply_block: global_responses.validator_set_apply_block
+ }
- Publisher.broadcast([{:staking_update, data}], :realtime)
+ Publisher.broadcast([{:staking_update, data}], :realtime)
- if state.snapshotting_finished do
- %{state | snapshotting_finished: false}
- else
- state
- end
+ if state.snapshotting_finished do
+ %{state | snapshotting_finished: false}
+ else
+ state
end
end
+ defp start_snapshotting?(global_responses) do
+ global_responses.epoch_number > get(:snapshotted_epoch_number) && global_responses.epoch_number > 0 &&
+ not get(:is_snapshotting)
+ end
+
defp update_database(epoch_very_beginning, start_snapshotting, global_responses, contracts, abi, block_number) do
is_validator = Enum.into(global_responses.validators, %{}, &{address_bytes_to_string(&1), true})
@@ -397,7 +404,10 @@ defmodule Explorer.Staking.ContractState do
end
end
- defp get_settings(global_responses, validator_min_reward_percent, block_number) do
+ defp get_settings(global_responses, state, block_number) do
+ validator_min_reward_percent =
+ get_validator_min_reward_percent(global_responses.epoch_number, block_number, state.contracts, state.abi)
+
base_settings = get_base_settings(global_responses, validator_min_reward_percent)
update_token =
@@ -412,6 +422,16 @@ defmodule Explorer.Staking.ContractState do
end
end
+ defp set_settings(global_responses, state, block_number, last_change_block) do
+ settings =
+ global_responses
+ |> get_settings(state, block_number)
+ |> Enum.concat(active_pools_length: Enum.count(global_responses.active_pools))
+ |> Enum.concat(last_change_block: last_change_block)
+ |> Enum.concat(seen_block: block_number)
+ :ets.insert(@table_name, settings)
+ end
+
defp get_mining_to_staking_address(validators, contracts, abi, block_number) do
validators.all
|> Enum.map(&ContractReader.staking_by_mining_request(&1, block_number))
diff --git a/apps/explorer/test/explorer/staking/contract_state_test.exs b/apps/explorer/test/explorer/staking/contract_state_test.exs
index 6a33bbe604..72aa2163a8 100644
--- a/apps/explorer/test/explorer/staking/contract_state_test.exs
+++ b/apps/explorer/test/explorer/staking/contract_state_test.exs
@@ -25,7 +25,9 @@ defmodule Explorer.Staking.ContractStateTest do
Application.put_env(:explorer, ContractState,
enabled: true,
- staking_contract_address: "0x1100000000000000000000000000000000000001"
+ staking_contract_address: "0x1100000000000000000000000000000000000001",
+ eth_blocknumber_pull_interval: "500",
+ eth_subscribe_max_delay: "60"
)
start_supervised!(ContractState)
From 820a586ee1c376efe1cd60e45bdcb8b7e4d546e5 Mon Sep 17 00:00:00 2001
From: POA <33550681+poa@users.noreply.github.com>
Date: Tue, 26 Jan 2021 10:35:37 +0300
Subject: [PATCH 5/5] mix format
---
apps/explorer/lib/explorer/staking/contract_state.ex | 1 +
1 file changed, 1 insertion(+)
diff --git a/apps/explorer/lib/explorer/staking/contract_state.ex b/apps/explorer/lib/explorer/staking/contract_state.ex
index 9026411667..83dfdddaef 100644
--- a/apps/explorer/lib/explorer/staking/contract_state.ex
+++ b/apps/explorer/lib/explorer/staking/contract_state.ex
@@ -429,6 +429,7 @@ defmodule Explorer.Staking.ContractState do
|> Enum.concat(active_pools_length: Enum.count(global_responses.active_pools))
|> Enum.concat(last_change_block: last_change_block)
|> Enum.concat(seen_block: block_number)
+
:ets.insert(@table_name, settings)
end