From 813b0d80ae9920df2d9927e22711d74ce3ad955a Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 16 May 2019 15:09:06 +0300 Subject: [PATCH 01/73] ALLOWED_EVM_VERIONS ENV VAR --- apps/explorer/config/config.exs | 3 ++- .../smart_contract/solidity/code_compiler.ex | 24 +++++++++++++++---- .../solidity/code_compiler_test.exs | 23 ++++++++++++++++++ 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/apps/explorer/config/config.exs b/apps/explorer/config/config.exs index e6eb5c4813..c095619178 100644 --- a/apps/explorer/config/config.exs +++ b/apps/explorer/config/config.exs @@ -9,7 +9,8 @@ use Mix.Config config :explorer, ecto_repos: [Explorer.Repo], coin: System.get_env("COIN") || "POA", - token_functions_reader_max_retries: 3 + token_functions_reader_max_retries: 3, + allowed_evm_versions: System.get_env("ALLOWED_EVM_VERSIONS") config :explorer, Explorer.Counters.AverageBlockTime, enabled: true diff --git a/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex b/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex index 656f1b9734..722e45709a 100644 --- a/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex +++ b/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex @@ -4,11 +4,19 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do """ alias Explorer.SmartContract.SolcDownloader + alias Poison.Parser require Logger @new_contract_name "New.sol" - @allowed_evm_versions ["homestead", "tangerineWhistle", "spuriousDragon", "byzantium", "constantinople", "petersburg"] + @default_allowed_evm_versions [ + "homestead", + "tangerineWhistle", + "spuriousDragon", + "byzantium", + "constantinople", + "petersburg" + ] @doc """ Compiles a code in the solidity command line. @@ -72,13 +80,13 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do code = Keyword.fetch!(params, :code) optimize = Keyword.fetch!(params, :optimize) optimization_runs = params |> Keyword.get(:optimization_runs, 200) |> Integer.to_string() - evm_version = Keyword.get(params, :evm_version, List.last(@allowed_evm_versions)) + evm_version = Keyword.get(params, :evm_version, List.last(allowed_evm_versions())) external_libs = Keyword.get(params, :external_libs, %{}) external_libs_string = Jason.encode!(external_libs) checked_evm_version = - if evm_version in @allowed_evm_versions do + if evm_version in allowed_evm_versions() do evm_version else "byzantium" @@ -125,7 +133,15 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do end end - def allowed_evm_versions, do: @allowed_evm_versions + def allowed_evm_versions() do + if Application.get_env(:explorer, :allowed_evm_versions) do + :explorer + |> Application.get_env(:allowed_evm_versions) + |> Parser.parse!() + else + @default_allowed_evm_versions + end + end def get_contract_info(contracts, _) when contracts == %{}, do: {:error, :compilation} diff --git a/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs b/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs index 57f15577ac..fa6f5b9124 100644 --- a/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs +++ b/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs @@ -270,6 +270,29 @@ defmodule Explorer.SmartContract.Solidity.CodeCompilerTest do end end + describe "allowed_evm_versions/0" do + + @allowed_evm_versions_pattern '[ + "homestead", + "tangerineWhistle", + "spuriousDragon", + "byzantium", + "constantinople", + "petersburg2" + ]' + + test "returns default_allowed_evm_versions" do + response = CodeCompiler.allowed_evm_versions() + assert response = ["homestead","tangerineWhistle","spuriousDragon","byzantium","constantinople","petersburg"] + end + + test "returns allowed evm versions defined by ALLOWED_EVM_VERSIONS env var" do + Application.put_env(:explorer, :allowed_evm_versions, @allowed_evm_versions_pattern) + response = CodeCompiler.allowed_evm_versions() + assert response = ["homestead","tangerineWhistle","spuriousDragon","byzantium","constantinople","petersburg2"] + end + end + describe "get_contract_info/1" do test "return name error when the Contract name doesn't match" do name = "Name" From 568a74f586d81b0ff3acdb41405f86259617b140 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 16 May 2019 16:00:32 +0300 Subject: [PATCH 02/73] Add CHANGELOG entry and fix some tests --- CHANGELOG.md | 1 + .../smart_contract/solidity/code_compiler.ex | 2 +- .../smart_contract/solidity/code_compiler_test.exs | 13 ++++++++++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ed6b6014d..c628131526 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ - [#1900](https://github.com/poanetwork/blockscout/pull/1900) - SUPPORTED_CHAINS ENV var - [#1892](https://github.com/poanetwork/blockscout/pull/1892) - Remove temporary worker modules - [#1958](https://github.com/poanetwork/blockscout/pull/1958) - Default value for release link env var +- [#1964](https://github.com/poanetwork/blockscout/pull/1964) - ALLOWED_EVM_VERSIONS env var ## 1.3.10-beta diff --git a/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex b/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex index 722e45709a..656da83c59 100644 --- a/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex +++ b/apps/explorer/lib/explorer/smart_contract/solidity/code_compiler.ex @@ -133,7 +133,7 @@ defmodule Explorer.SmartContract.Solidity.CodeCompiler do end end - def allowed_evm_versions() do + def allowed_evm_versions do if Application.get_env(:explorer, :allowed_evm_versions) do :explorer |> Application.get_env(:allowed_evm_versions) diff --git a/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs b/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs index fa6f5b9124..4b03d9c6bd 100644 --- a/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs +++ b/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs @@ -271,7 +271,6 @@ defmodule Explorer.SmartContract.Solidity.CodeCompilerTest do end describe "allowed_evm_versions/0" do - @allowed_evm_versions_pattern '[ "homestead", "tangerineWhistle", @@ -283,13 +282,21 @@ defmodule Explorer.SmartContract.Solidity.CodeCompilerTest do test "returns default_allowed_evm_versions" do response = CodeCompiler.allowed_evm_versions() - assert response = ["homestead","tangerineWhistle","spuriousDragon","byzantium","constantinople","petersburg"] + assert response = ["homestead", "tangerineWhistle", "spuriousDragon", "byzantium", "constantinople", "petersburg"] end test "returns allowed evm versions defined by ALLOWED_EVM_VERSIONS env var" do Application.put_env(:explorer, :allowed_evm_versions, @allowed_evm_versions_pattern) response = CodeCompiler.allowed_evm_versions() - assert response = ["homestead","tangerineWhistle","spuriousDragon","byzantium","constantinople","petersburg2"] + + assert response = [ + "homestead", + "tangerineWhistle", + "spuriousDragon", + "byzantium", + "constantinople", + "petersburg2" + ] end end From 3b9169fb23d0547381d2068e6499e586d0560b6d Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 16 May 2019 16:36:12 +0300 Subject: [PATCH 03/73] change the allowed evm versions values for tests --- .../solidity/code_compiler_test.exs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs b/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs index 4b03d9c6bd..b35a041b5b 100644 --- a/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs +++ b/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs @@ -272,12 +272,9 @@ defmodule Explorer.SmartContract.Solidity.CodeCompilerTest do describe "allowed_evm_versions/0" do @allowed_evm_versions_pattern '[ - "homestead", - "tangerineWhistle", - "spuriousDragon", - "byzantium", - "constantinople", - "petersburg2" + "CustomEVM1", + "CustomEVM2", + "CustomEVM3" ]' test "returns default_allowed_evm_versions" do @@ -290,12 +287,9 @@ defmodule Explorer.SmartContract.Solidity.CodeCompilerTest do response = CodeCompiler.allowed_evm_versions() assert response = [ - "homestead", - "tangerineWhistle", - "spuriousDragon", - "byzantium", - "constantinople", - "petersburg2" + "CustomEVM1", + "CustomEVM2", + "CustomEVM3" ] end end From d92d41f2c2953475fd91011eb8c1ecdba2d7bd51 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 16 May 2019 17:29:03 +0300 Subject: [PATCH 04/73] Set to nil allowed_evm_versions var after test --- .../test/explorer/smart_contract/solidity/code_compiler_test.exs | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs b/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs index b35a041b5b..166aece22c 100644 --- a/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs +++ b/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs @@ -291,6 +291,7 @@ defmodule Explorer.SmartContract.Solidity.CodeCompilerTest do "CustomEVM2", "CustomEVM3" ] + Application.put_env(:explorer, :allowed_evm_versions, nil) end end From 8fafdf00273a328bc84e878e68b87dfb2cdc1aa7 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 16 May 2019 17:35:41 +0300 Subject: [PATCH 05/73] Fix mix format --- .../test/explorer/smart_contract/solidity/code_compiler_test.exs | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs b/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs index 166aece22c..5705ce9bde 100644 --- a/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs +++ b/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs @@ -291,6 +291,7 @@ defmodule Explorer.SmartContract.Solidity.CodeCompilerTest do "CustomEVM2", "CustomEVM3" ] + Application.put_env(:explorer, :allowed_evm_versions, nil) end end From e837178e648b38e9891a04b7b4d7b49e9abdb752 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Fri, 17 May 2019 16:03:34 +0300 Subject: [PATCH 06/73] create module with common pagination module --- .../controllers/address_controller.ex | 14 ++------ .../address_transaction_controller.ex | 32 +++++++++++++++++-- .../controllers/pagination_helpers.ex | 16 ++++++++++ .../address_transaction/index.html.eex | 2 +- .../_pagination_container.html.eex | 2 +- .../views/common_components_view..ex | 3 -- .../views/common_components_view.ex | 7 ++++ 7 files changed, 57 insertions(+), 19 deletions(-) create mode 100644 apps/block_scout_web/lib/block_scout_web/controllers/pagination_helpers.ex delete mode 100644 apps/block_scout_web/lib/block_scout_web/views/common_components_view..ex create mode 100644 apps/block_scout_web/lib/block_scout_web/views/common_components_view.ex diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex index 3d43ed7054..1231278bfe 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex @@ -2,6 +2,7 @@ defmodule BlockScoutWeb.AddressController do use BlockScoutWeb, :controller import BlockScoutWeb.Chain, only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1] + import BlockScoutWeb.PaginationHelpers alias Explorer.{Chain, Market} alias Explorer.Chain.Address @@ -15,12 +16,7 @@ defmodule BlockScoutWeb.AddressController do {addresses_page, next_page} = split_list_by_page(addresses) - cur_page_number = - cond do - !params["prev_page_number"] -> 1 - params["next_page"] -> String.to_integer(params["prev_page_number"]) + 1 - params["prev_page"] -> String.to_integer(params["prev_page_number"]) - 1 - end + cur_page_number = current_page_number(params) next_page_path = case next_page_params(next_page, addresses_page, params) do @@ -28,11 +24,7 @@ defmodule BlockScoutWeb.AddressController do nil next_page_params -> - next_params = - next_page_params - |> Map.put("prev_page_path", cur_page_path(conn, params)) - |> Map.put("next_page", true) - |> Map.put("prev_page_number", cur_page_number) + next_params = add_navigation_params(next_page_params, cur_page_path(conn, params), cur_page_number) address_path( conn, diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex index da22b8c042..2cc76cc5f6 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_transaction_controller.ex @@ -7,6 +7,7 @@ defmodule BlockScoutWeb.AddressTransactionController do import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1] import BlockScoutWeb.Chain, only: [current_filter: 1, paging_options: 1, next_page_params: 3, split_list_by_page: 1] + import BlockScoutWeb.PaginationHelpers alias BlockScoutWeb.TransactionView alias Explorer.{Chain, Market} @@ -37,6 +38,7 @@ defmodule BlockScoutWeb.AddressTransactionController do results_plus_one = Chain.address_to_transactions_with_rewards(address, options) {results, next_page} = split_list_by_page(results_plus_one) + cur_page_number = current_page_number(params) next_page_url = case next_page_params(next_page, results, params) do @@ -44,11 +46,13 @@ defmodule BlockScoutWeb.AddressTransactionController do nil next_page_params -> + next_params = add_navigation_params(next_page_params, cur_page_path(conn, address, params), cur_page_number) + address_transaction_path( conn, :index, address, - Map.delete(next_page_params, "type") + Map.delete(next_params, "type") ) end @@ -74,7 +78,12 @@ defmodule BlockScoutWeb.AddressTransactionController do end end) - json(conn, %{items: items_json, next_page_path: next_page_url}) + json(conn, %{ + items: items_json, + next_page_path: next_page_url, + prev_page_path: params["prev_page_path"], + cur_page_number: cur_page_number + }) else :error -> unprocessable_entity(conn) @@ -87,6 +96,8 @@ defmodule BlockScoutWeb.AddressTransactionController do def index(conn, %{"address_id" => address_hash_string} = params) do with {:ok, address_hash} <- Chain.string_to_address_hash(address_hash_string), {:ok, address} <- Chain.hash_to_address(address_hash) do + cur_page_number = current_page_number(params) + render( conn, "index.html", @@ -96,7 +107,9 @@ defmodule BlockScoutWeb.AddressTransactionController do filter: params["filter"], transaction_count: transaction_count(address), validation_count: validation_count(address), - current_path: current_path(conn) + current_path: current_path(conn), + prev_page_path: params["prev_page_path"], + cur_page_number: cur_page_number ) else :error -> @@ -106,4 +119,17 @@ defmodule BlockScoutWeb.AddressTransactionController do not_found(conn) end end + + defp cur_page_path(conn, address, %{"block_number" => _, "index" => _} = params) do + new_params = Map.put(params, "next_page", false) + + address_transaction_path( + conn, + :index, + address, + new_params + ) + end + + defp cur_page_path(conn, address, params), do: address_transaction_path(conn, :index, address, params) end diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/pagination_helpers.ex b/apps/block_scout_web/lib/block_scout_web/controllers/pagination_helpers.ex new file mode 100644 index 0000000000..0c3ccc0c6f --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/controllers/pagination_helpers.ex @@ -0,0 +1,16 @@ +defmodule BlockScoutWeb.PaginationHelpers do + def current_page_number(params) do + cond do + !params["prev_page_number"] -> 1 + params["next_page"] -> String.to_integer(params["prev_page_number"]) + 1 + params["prev_page"] -> String.to_integer(params["prev_page_number"]) - 1 + end + end + + def add_navigation_params(params, current_page_path, current_page_number) do + params + |> Map.put("prev_page_path", current_page_path) + |> Map.put("next_page", true) + |> Map.put("prev_page_number", current_page_number) + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex index 9932a8422d..aefe6c5606 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex @@ -51,7 +51,7 @@ - <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true %> + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", show_pagination_limit: true, data_next_page_button: true %> - -
-
<%= @address.contract_code %>
-
+ <%= case contract_creation_code do %> + <% {:selfdestructed, transaction_init} -> %> +
+

<%= gettext "Contract Creation Code" %>

+ +
+
+

<%= gettext "Contracts that self destruct in their constructors have no contract code published and cannot be verified." %>

+

<%= gettext "Displaying the init data provided of the creating transaction." %>

+
+
+
<%= transaction_init %>
+
+ <% {:ok, contract_code} -> %> +
+

<%= gettext "Contract Byte Code" %>

+ +
+
+
<%= contract_code %>
+
+ <% end %> diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_logs/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_logs/index.html.eex new file mode 100644 index 0000000000..22f446924f --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_logs/index.html.eex @@ -0,0 +1,82 @@ +
+ <%= render BlockScoutWeb.AddressView, "overview.html", assigns %> +
+ <%= render BlockScoutWeb.AddressView, "_tabs.html", assigns %> + +
+ +

<%= gettext "Logs" %>

+ + <%= if @next_page_url do %> + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", cur_page_number: "1", show_pagination_limit: true, next_page_path: @next_page_url %> + <% end %> + + <%= if !@next_page_url do %> + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", cur_page_number: "1", show_pagination_limit: true %> + <% end %> + + <%= if Enum.count(@logs) > 0 do %> + <%= for log <- @logs do %> +
+
+
<%= gettext "Transaction" %>
+
+

+ <%= link( + log.transaction, + to: transaction_path(@conn, :show, log.transaction), + "data-test": "log_address_link", + "data-address-hash": log.transaction + ) %> +

+
+
<%= gettext "Topics" %>
+
+
+ <%= unless is_nil(log.first_topic) do %> +
+ [0] + <%= log.first_topic %> +
+ <% end %> + <%= unless is_nil(log.second_topic) do %> +
+ [1] + <%= log.second_topic %> +
+ <% end %> + <%= unless is_nil(log.third_topic) do %> +
+ [2] + <%= log.third_topic %> +
+ <% end %> + <%= unless is_nil(log.fourth_topic) do %> +
+ [3] + <%= log.fourth_topic %> +
+ <% end %> +
+
+
+ <%= gettext "Data" %> +
+
+ <%= unless is_nil(log.data) do %> +
+ <%= log.data %> +
+ <% end %> +
+
+
+ <% end %> + <% else %> +
+ <%= gettext "There are no logs for this address." %> +
+ <% end %> +
+
+
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex index 6f1dc762fd..de0215727c 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_token_balance/_token_balances.html.eex @@ -1,6 +1,6 @@ <%= if Enum.any?(@token_balances) do %> - >}, + contracts_creation_internal_transaction: %InternalTransaction{init: init} + }) do + {:selfdestructed, init} + end + + def contract_creation_code(%Address{contract_code: contract_code}) do + {:ok, contract_code} + end end diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_decompiled_contract_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_decompiled_contract_view.ex index f80a0ded99..b92d356479 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_decompiled_contract_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_decompiled_contract_view.ex @@ -165,7 +165,10 @@ defmodule BlockScoutWeb.AddressDecompiledContractView do |> String.replace("\e[1m", "") |> String.replace("»", "»") |> String.replace("\e[0m", "") - |> String.split(~r/\|\<\/span\>/, include_captures: true, trim: true) + |> String.split(~r/\|\|\<\/span\>/, + include_captures: true, + trim: true + ) |> add_styles_to_every_line() result diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_logs_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_logs_view.ex new file mode 100644 index 0000000000..7155e65206 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/address_logs_view.ex @@ -0,0 +1,3 @@ +defmodule BlockScoutWeb.AddressLogsView do + use BlockScoutWeb, :view +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_view.ex index 6ce72b90ae..7a6005d526 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_view.ex @@ -300,6 +300,7 @@ defmodule BlockScoutWeb.AddressView do defp tab_name(["read_contract"]), do: gettext("Read Contract") defp tab_name(["coin_balances"]), do: gettext("Coin Balance History") defp tab_name(["validations"]), do: gettext("Blocks Validated") + defp tab_name(["logs"]), do: gettext("Logs") def short_hash(%Address{hash: hash}) do << diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/block_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/block_view.ex index 941a064804..5c98794b2a 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/block_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/block_view.ex @@ -1,7 +1,7 @@ defmodule BlockScoutWeb.API.RPC.BlockView do use BlockScoutWeb, :view - alias BlockScoutWeb.API.RPC.RPCView + alias BlockScoutWeb.API.RPC.{EthRPCView, RPCView} alias Explorer.Chain.{Hash, Wei} def render("block_reward.json", %{block: block, reward: reward}) do @@ -22,7 +22,33 @@ defmodule BlockScoutWeb.API.RPC.BlockView do RPCView.render("show.json", data: data) end + def render("eth_block_number.json", %{number: number, id: id}) do + result = encode_quantity(number) + + EthRPCView.render("show.json", %{result: result, id: id}) + end + def render("error.json", %{error: error}) do RPCView.render("error.json", error: error) end + + defp encode_quantity(binary) when is_binary(binary) do + hex_binary = Base.encode16(binary, case: :lower) + + result = String.replace_leading(hex_binary, "0", "") + + final_result = if result == "", do: "0", else: result + + "0x#{final_result}" + end + + defp encode_quantity(value) when is_integer(value) do + value + |> :binary.encode_unsigned() + |> encode_quantity() + end + + defp encode_quantity(value) when is_nil(value) do + nil + end end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/eth_rpc_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/eth_rpc_view.ex new file mode 100644 index 0000000000..39eb5ae9d1 --- /dev/null +++ b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/eth_rpc_view.ex @@ -0,0 +1,33 @@ +defmodule BlockScoutWeb.API.RPC.EthRPCView do + use BlockScoutWeb, :view + + defstruct [:result, :id, :error] + + def render("show.json", %{result: result, id: id}) do + %__MODULE__{ + result: result, + id: id + } + end + + def render("error.json", %{error: message, id: id}) do + %__MODULE__{ + error: message, + id: id + } + end + + defimpl Poison.Encoder, for: BlockScoutWeb.API.RPC.EthRPCView do + def encode(%BlockScoutWeb.API.RPC.EthRPCView{result: result, id: id, error: error}, _options) when is_nil(error) do + """ + {"jsonrpc":"2.0","result":"#{result}","id":#{id}} + """ + end + + def encode(%BlockScoutWeb.API.RPC.EthRPCView{id: id, error: error}, _options) do + """ + {"jsonrpc":"2.0","error": #{error},"id": #{id}} + """ + end + end +end diff --git a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/transaction_view.ex b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/transaction_view.ex index 459272df07..e792be8f40 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/api/rpc/transaction_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/api/rpc/transaction_view.ex @@ -77,7 +77,8 @@ defmodule BlockScoutWeb.API.RPC.TransactionView do %{ "address" => "#{log.address_hash}", "topics" => get_topics(log), - "data" => "#{log.data}" + "data" => "#{log.data}", + "index" => "#{log.index}" } end diff --git a/apps/block_scout_web/lib/block_scout_web/views/layout_view.ex b/apps/block_scout_web/lib/block_scout_web/views/layout_view.ex index 7a454a0e72..12df6d3f0a 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/layout_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/layout_view.ex @@ -142,10 +142,22 @@ defmodule BlockScoutWeb.LayoutView do end def release_link(version) do - release_link = Application.get_env(:block_scout_web, :release_link) + release_link_env_var = Application.get_env(:block_scout_web, :release_link) - if release_link == "" || release_link == nil do - version + release_link = + cond do + version == "" || version == nil -> + nil + + release_link_env_var == "" || release_link_env_var == nil -> + "https://github.com/poanetwork/blockscout/releases/tag/" <> version + + true -> + release_link_env_var + end + + if release_link == nil do + "" else html_escape({:safe, "#{version}"}) end diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot index 65a9fd7564..12add2c649 100644 --- a/apps/block_scout_web/priv/gettext/default.pot +++ b/apps/block_scout_web/priv/gettext/default.pot @@ -187,7 +187,7 @@ msgid "Blocks Indexed" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address/_tabs.html.eex:27 +#: lib/block_scout_web/templates/address/_tabs.html.eex:32 #: lib/block_scout_web/templates/address/overview.html.eex:95 #: lib/block_scout_web/templates/address_validation/index.html.eex:13 #: lib/block_scout_web/views/address_view.ex:302 @@ -215,7 +215,7 @@ msgid "Close" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address/_tabs.html.eex:37 +#: lib/block_scout_web/templates/address/_tabs.html.eex:42 #: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:165 #: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:187 #: lib/block_scout_web/views/address_view.ex:298 @@ -228,7 +228,7 @@ msgid "Compiler" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:23 +#: lib/block_scout_web/templates/address_contract/index.html.eex:30 msgid "Compiler version" msgstr "" @@ -256,7 +256,7 @@ msgid "Connection Lost, click to load newer validations" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:46 +#: lib/block_scout_web/templates/address_contract/index.html.eex:53 msgid "Contract ABI" msgstr "" @@ -289,17 +289,12 @@ msgid "Contract Name" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:60 -msgid "Contract creation code" -msgstr "" - -#, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:19 +#: lib/block_scout_web/templates/address_contract/index.html.eex:26 msgid "Contract name:" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:34 +#: lib/block_scout_web/templates/address_contract/index.html.eex:41 msgid "Contract source code" msgstr "" @@ -332,6 +327,7 @@ msgid "Curl" msgstr "" #, elixir-format +#: lib/block_scout_web/templates/address_logs/index.html.eex:63 #: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:18 #: lib/block_scout_web/templates/transaction_log/index.html.eex:67 #: lib/block_scout_web/templates/transaction_log/index.html.eex:133 @@ -503,8 +499,11 @@ msgid "Limit" msgstr "" #, elixir-format +#: lib/block_scout_web/templates/address/_tabs.html.eex:26 +#: lib/block_scout_web/templates/address_logs/index.html.eex:8 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:17 #: lib/block_scout_web/templates/transaction_log/index.html.eex:8 +#: lib/block_scout_web/views/address_view.ex:303 #: lib/block_scout_web/views/transaction_view.ex:340 msgid "Logs" msgstr "" @@ -619,7 +618,7 @@ msgid "Older" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:27 +#: lib/block_scout_web/templates/address_contract/index.html.eex:34 msgid "Optimization enabled" msgstr "" @@ -680,7 +679,7 @@ msgid "Query" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address/_tabs.html.eex:53 +#: lib/block_scout_web/templates/address/_tabs.html.eex:58 #: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:25 #: lib/block_scout_web/views/address_view.ex:300 #: lib/block_scout_web/views/tokens/overview_view.ex:37 @@ -872,6 +871,7 @@ msgid "Top Accounts - %{subnetwork} Explorer" msgstr "" #, elixir-format +#: lib/block_scout_web/templates/address_logs/index.html.eex:33 #: lib/block_scout_web/templates/transaction_log/index.html.eex:103 msgid "Topics" msgstr "" @@ -892,6 +892,7 @@ msgid "Total transactions" msgstr "" #, elixir-format +#: lib/block_scout_web/templates/address_logs/index.html.eex:22 #: lib/block_scout_web/views/transaction_view.ex:287 msgid "Transaction" msgstr "" @@ -979,7 +980,8 @@ msgid "Value" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:9 +#: lib/block_scout_web/templates/address_contract/index.html.eex:11 +#: lib/block_scout_web/templates/address_contract/index.html.eex:16 msgid "Verify & Publish" msgstr "" @@ -1044,7 +1046,7 @@ msgid "at" msgstr "" #, elixir-format -#: lib/block_scout_web/views/address_contract_view.ex:20 +#: lib/block_scout_web/views/address_contract_view.ex:22 msgid "false" msgstr "" @@ -1062,7 +1064,7 @@ msgid "string" msgstr "" #, elixir-format -#: lib/block_scout_web/views/address_contract_view.ex:19 +#: lib/block_scout_web/views/address_contract_view.ex:21 msgid "true" msgstr "" @@ -1466,17 +1468,17 @@ msgid "Support" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:48 +#: lib/block_scout_web/templates/address_contract/index.html.eex:55 msgid "Copy ABI" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:62 +#: lib/block_scout_web/templates/address_contract/index.html.eex:71 msgid "Copy Contract Creation Code" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:36 +#: lib/block_scout_web/templates/address_contract/index.html.eex:43 msgid "Copy Source Code" msgstr "" @@ -1618,7 +1620,7 @@ msgid "Decompiled Code" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address/_tabs.html.eex:47 +#: lib/block_scout_web/templates/address/_tabs.html.eex:52 msgid "Decompiled code" msgstr "" @@ -1704,6 +1706,36 @@ msgid "of" msgstr "" #, elixir-format +<<<<<<< HEAD #: lib/block_scout_web/templates/block_transaction/404.html.eex:7 msgid "Block Details" +======= +#: lib/block_scout_web/templates/address_contract/index.html.eex:83 +msgid "Contract Byte Code" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/address_contract/index.html.eex:69 +msgid "Contract Creation Code" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/address_contract/index.html.eex:75 +msgid "Contracts that self destruct in their constructors have no contract code published and cannot be verified." +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/address_contract/index.html.eex:85 +msgid "Copy Contract Byte Code" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/address_contract/index.html.eex:76 +msgid "Displaying the init data provided of the creating transaction." +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/address_logs/index.html.eex:77 +msgid "There are no logs for this address." +>>>>>>> 59d33a42ddf629ec042ae03fa17818f4c6eb8211 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 8925a95acf..1dd2465781 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 @@ -187,7 +187,7 @@ msgid "Blocks Indexed" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address/_tabs.html.eex:27 +#: lib/block_scout_web/templates/address/_tabs.html.eex:32 #: lib/block_scout_web/templates/address/overview.html.eex:95 #: lib/block_scout_web/templates/address_validation/index.html.eex:13 #: lib/block_scout_web/views/address_view.ex:302 @@ -215,7 +215,7 @@ msgid "Close" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address/_tabs.html.eex:37 +#: lib/block_scout_web/templates/address/_tabs.html.eex:42 #: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:165 #: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:187 #: lib/block_scout_web/views/address_view.ex:298 @@ -228,7 +228,7 @@ msgid "Compiler" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:23 +#: lib/block_scout_web/templates/address_contract/index.html.eex:30 msgid "Compiler version" msgstr "" @@ -256,7 +256,7 @@ msgid "Connection Lost, click to load newer validations" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:46 +#: lib/block_scout_web/templates/address_contract/index.html.eex:53 msgid "Contract ABI" msgstr "" @@ -289,17 +289,12 @@ msgid "Contract Name" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:60 -msgid "Contract creation code" -msgstr "" - -#, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:19 +#: lib/block_scout_web/templates/address_contract/index.html.eex:26 msgid "Contract name:" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:34 +#: lib/block_scout_web/templates/address_contract/index.html.eex:41 msgid "Contract source code" msgstr "" @@ -334,6 +329,7 @@ msgid "Curl" msgstr "" #, elixir-format +#: lib/block_scout_web/templates/address_logs/index.html.eex:63 #: lib/block_scout_web/templates/transaction/_decoded_input_body.html.eex:18 #: lib/block_scout_web/templates/transaction_log/index.html.eex:67 #: lib/block_scout_web/templates/transaction_log/index.html.eex:133 @@ -505,8 +501,11 @@ msgid "Limit" msgstr "" #, elixir-format +#: lib/block_scout_web/templates/address/_tabs.html.eex:26 +#: lib/block_scout_web/templates/address_logs/index.html.eex:8 #: lib/block_scout_web/templates/transaction/_tabs.html.eex:17 #: lib/block_scout_web/templates/transaction_log/index.html.eex:8 +#: lib/block_scout_web/views/address_view.ex:303 #: lib/block_scout_web/views/transaction_view.ex:340 msgid "Logs" msgstr "" @@ -621,7 +620,7 @@ msgid "Older" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:27 +#: lib/block_scout_web/templates/address_contract/index.html.eex:34 msgid "Optimization enabled" msgstr "" @@ -682,7 +681,7 @@ msgid "Query" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address/_tabs.html.eex:53 +#: lib/block_scout_web/templates/address/_tabs.html.eex:58 #: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:25 #: lib/block_scout_web/views/address_view.ex:300 #: lib/block_scout_web/views/tokens/overview_view.ex:37 @@ -874,6 +873,7 @@ msgid "Top Accounts - %{subnetwork} Explorer" msgstr "" #, elixir-format +#: lib/block_scout_web/templates/address_logs/index.html.eex:33 #: lib/block_scout_web/templates/transaction_log/index.html.eex:103 msgid "Topics" msgstr "" @@ -894,6 +894,7 @@ msgid "Total transactions" msgstr "" #, elixir-format +#: lib/block_scout_web/templates/address_logs/index.html.eex:22 #: lib/block_scout_web/views/transaction_view.ex:287 msgid "Transaction" msgstr "" @@ -982,7 +983,8 @@ msgid "Value" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:9 +#: lib/block_scout_web/templates/address_contract/index.html.eex:11 +#: lib/block_scout_web/templates/address_contract/index.html.eex:16 msgid "Verify & Publish" msgstr "" @@ -1047,7 +1049,7 @@ msgid "at" msgstr "" #, elixir-format -#: lib/block_scout_web/views/address_contract_view.ex:20 +#: lib/block_scout_web/views/address_contract_view.ex:22 msgid "false" msgstr "" @@ -1065,7 +1067,7 @@ msgid "string" msgstr "" #, elixir-format -#: lib/block_scout_web/views/address_contract_view.ex:19 +#: lib/block_scout_web/views/address_contract_view.ex:21 msgid "true" msgstr "" @@ -1357,7 +1359,7 @@ msgid "Loading balances" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:23 +#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:22 #: lib/block_scout_web/templates/chain/show.html.eex:13 msgid "Loading chart" msgstr "" @@ -1368,7 +1370,7 @@ msgid "There is no coin history for this address." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:26 +#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:25 #: lib/block_scout_web/templates/chain/show.html.eex:16 msgid "There was a problem loading the chart." msgstr "" @@ -1509,17 +1511,17 @@ msgid "Support" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:48 +#: lib/block_scout_web/templates/address_contract/index.html.eex:55 msgid "Copy ABI" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:62 +#: lib/block_scout_web/templates/address_contract/index.html.eex:71 msgid "Copy Contract Creation Code" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_contract/index.html.eex:36 +#: lib/block_scout_web/templates/address_contract/index.html.eex:43 msgid "Copy Source Code" msgstr "" @@ -1661,7 +1663,7 @@ msgid "Decompiled Code" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address/_tabs.html.eex:47 +#: lib/block_scout_web/templates/address/_tabs.html.eex:52 msgid "Decompiled code" msgstr "" @@ -1745,3 +1747,33 @@ msgstr "" #: lib/block_scout_web/templates/common_components/_pagination_container.html.eex:37 msgid "of" msgstr "" + +#, elixir-format, fuzzy +#: lib/block_scout_web/templates/address_contract/index.html.eex:83 +msgid "Contract Byte Code" +msgstr "" + +#, elixir-format, fuzzy +#: lib/block_scout_web/templates/address_contract/index.html.eex:69 +msgid "Contract Creation Code" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/address_contract/index.html.eex:75 +msgid "Contracts that self destruct in their constructors have no contract code published and cannot be verified." +msgstr "" + +#, elixir-format, fuzzy +#: lib/block_scout_web/templates/address_contract/index.html.eex:85 +msgid "Copy Contract Byte Code" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/address_contract/index.html.eex:76 +msgid "Displaying the init data provided of the creating transaction." +msgstr "" + +#, elixir-format, fuzzy +#: lib/block_scout_web/templates/address_logs/index.html.eex:77 +msgid "There are no logs for this address." +msgstr "" diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs index b3b8f0b08b..993b25fcec 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs @@ -25,7 +25,6 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do start_supervised!(AddressesWithBalanceCounter) Application.put_env(:explorer, AverageBlockTime, enabled: true) - BlockNumberCache.setup(cache_period: 0) on_exit(fn -> Application.put_env(:explorer, AverageBlockTime, enabled: false) diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs index 43169a65d2..399e49bfff 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/contract_controller_test.exs @@ -100,6 +100,34 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do ] end + test "filtering for only unverified contracts does not show self destructed contracts", %{ + params: params, + conn: conn + } do + address = insert(:contract_address) + insert(:smart_contract) + insert(:contract_address, contract_code: "0x") + + response = + conn + |> get("/api", Map.put(params, "filter", "unverified")) + |> json_response(200) + + assert response["message"] == "OK" + assert response["status"] == "1" + + assert response["result"] == [ + %{ + "ABI" => "Contract source code not verified", + "Address" => to_string(address.hash), + "CompilerVersion" => "", + "ContractName" => "", + "DecompilerVersion" => "", + "OptimizationUsed" => "" + } + ] + end + test "filtering for only verified contracts shows only verified contracts", %{params: params, conn: conn} do insert(:contract_address) contract = insert(:smart_contract) @@ -222,6 +250,35 @@ defmodule BlockScoutWeb.API.RPC.ContractControllerTest do } ] end + + test "filtering for only not_decompiled (and by extension not verified contracts) does not show empty contracts", %{ + params: params, + conn: conn + } do + insert(:decompiled_smart_contract) + insert(:smart_contract) + insert(:contract_address, contract_code: "0x") + contract_address = insert(:contract_address) + + response = + conn + |> get("/api", Map.put(params, "filter", "not_decompiled")) + |> json_response(200) + + assert response["message"] == "OK" + assert response["status"] == "1" + + assert response["result"] == [ + %{ + "ABI" => "Contract source code not verified", + "Address" => to_string(contract_address.hash), + "CompilerVersion" => "", + "ContractName" => "", + "DecompilerVersion" => "", + "OptimizationUsed" => "" + } + ] + end end describe "getabi" do diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/transaction_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/transaction_controller_test.exs index 92a2f969ab..9d151e382c 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/transaction_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/transaction_controller_test.exs @@ -460,7 +460,8 @@ defmodule BlockScoutWeb.API.RPC.TransactionControllerTest do %{ "address" => "#{address.hash}", "data" => "#{log.data}", - "topics" => ["first topic", "second topic", nil, nil] + "topics" => ["first topic", "second topic", nil, nil], + "index" => "#{log.index}" } ], "next_page_params" => nil diff --git a/apps/block_scout_web/test/block_scout_web/views/address_decompiled_contract_view_test.exs b/apps/block_scout_web/test/block_scout_web/views/address_decompiled_contract_view_test.exs index 8f1636ba06..334ea36baa 100644 --- a/apps/block_scout_web/test/block_scout_web/views/address_decompiled_contract_view_test.exs +++ b/apps/block_scout_web/test/block_scout_web/views/address_decompiled_contract_view_test.exs @@ -56,7 +56,7 @@ defmodule BlockScoutWeb.AddressDecompiledContractViewTest do result = AddressDecompiledContractView.highlight_decompiled_code(code) assert result == - " #\n # eveem.org 6 Feb 2019\n # Decompiled source of 0x00Bd9e214FAb74d6fC21bf1aF34261765f57e875\n #\n # Let's make the world open source\n # \n #\n # I failed with these:\n # - unknowne77c646d(?)\n # - transferFromWithData(address _from, address _to, uint256 _value, bytes _data)\n # All the rest is below.\n #\n\n\n # Storage definitions and getters\n\n def storage:\n allowance is uint256 => uint256 # mask(256, 0) at storage #2\n stor4 is uint256 => uint8 # mask(8, 0) at storage #4\n\n def allowance(address _owner, address _spender) payable: 64\n return allowance[_owner_spender(320 - 1))]\n\n\n #\n # Regular functions - see Tutorial for understanding quirks of the code\n #\n\n\n # folder failed in this function - may be terribly long, sorry\n def unknownc47d033b(?) payable: not cd[4]:\n revert\n else:\n mem[0]cd[4]\n mem[32] = 4\n mem[96] = bool(stor4[cd[4])])\n return bool(stor4[cd[4])])\n\n def _fallback() payable: # default function\n revert\n\n\n" + " #\n # eveem.org 6 Feb 2019\n # Decompiled source of 0x00Bd9e214FAb74d6fC21bf1aF34261765f57e875\n #\n # Let's make the world open source\n # \n #\n # I failed with these:\n # - unknowne77c646d(?)\n # - transferFromWithData(address _from, address _to, uint256 _value, bytes _data)\n # All the rest is below.\n #\n\n\n # Storage definitions and getters\n\n def storage:\n allowance is uint256 => uint256 # mask(256, 0) at storage #2\n stor4 is uint256 => uint8 # mask(8, 0) at storage #4\n\n def allowance(address _owner, address _spender) payable: \n require (calldata.size - 4) >= 64\n return allowance[sha3(((320 - 1) and (320 - 1) and _owner), 1), ((320 - 1) and _spender and (320 - 1))]\n\n\n #\n # Regular functions - see Tutorial for understanding quirks of the code\n #\n\n\n # folder failed in this function - may be terribly long, sorry\n def unknownc47d033b(?) payable: \n if (calldata.size - 4) < 32:\n revert\n else:\n if not (320 - 1) or not cd[4]:\n revert\n else:\n mem[0] = (320 - 1) and (320 - 1) and cd[4]\n mem[32] = 4\n mem[96] = bool(stor4[((320 - 1) and (320 - 1) and cd[4])])\n return bool(stor4[((320 - 1) and (320 - 1) and cd[4])])\n\n def _fallback() payable: # default function\n revert\n\n\n" end test "adds style span to every line" do @@ -72,6 +72,28 @@ defmodule BlockScoutWeb.AddressDecompiledContractViewTest do assert AddressDecompiledContractView.highlight_decompiled_code(code) == " #\n # eveem.org 6 Feb 2019\n # Decompiled source of 0x00Bd9e214FAb74d6fC21bf1aF34261765f57e875\n #\n # Let's make the world open source\n # \n\n\n" end + + test "does not remove bold text" do + code = """ + # + # Eveem.org 26 Apr 2019 + # Decompiled source of 0x06012c8cf97bead5deae237070f9587f8e7a266d + # + # Let's make the world open source + #  + + const name = 'CryptoKitties' + const symbol = 'CK' + const GEN0_STARTING_PRICE = 10^16 + const GEN0_AUCTION_DURATION = (24 * 3600) + const GEN0_CREATION_LIMIT = 45000 + const PROMO_CREATION_LIMIT = 5000 + + """ + + assert AddressDecompiledContractView.highlight_decompiled_code(code) == + "#\n# Eveem.org 26 Apr 2019\n# Decompiled source of 0x06012c8cf97bead5deae237070f9587f8e7a266d\n#\n# Let's make the world open source\n# \n\nconst name = 'CryptoKitties'\nconst symbol = 'CK'\nconst GEN0_STARTING_PRICE = 10^16\nconst GEN0_AUCTION_DURATION = (24 * 3600)\nconst GEN0_CREATION_LIMIT = 45000\nconst PROMO_CREATION_LIMIT = 5000\n\n\n\n" + end end describe "sort_contracts_by_version/1" do diff --git a/apps/block_scout_web/test/block_scout_web/views/layout_view_test.exs b/apps/block_scout_web/test/block_scout_web/views/layout_view_test.exs index 5e3302ceec..5787159b14 100644 --- a/apps/block_scout_web/test/block_scout_web/views/layout_view_test.exs +++ b/apps/block_scout_web/test/block_scout_web/views/layout_view_test.exs @@ -62,16 +62,32 @@ defmodule BlockScoutWeb.LayoutViewTest do end describe "release_link/1" do - test "use the version when there is no release_link env configured for it" do + test "set empty string if no blockscout version configured" do + Application.put_env(:block_scout_web, :blockscout_version, nil) + + assert LayoutView.release_link(nil) == "" + end + + test "set empty string if blockscout version is empty string" do + Application.put_env(:block_scout_web, :blockscout_version, "") + + assert LayoutView.release_link("") == "" + end + + test "use the default value when there is no release_link env configured for it" do Application.put_env(:block_scout_web, :release_link, nil) - assert LayoutView.release_link("1.3.4") == "1.3.4" + assert LayoutView.release_link("v1.3.4-beta") == + {:safe, + ~s(v1.3.4-beta)} end - test "use the version when empty release_link env configured for it" do + test "use the default value when empty release_link env configured for it" do Application.put_env(:block_scout_web, :release_link, "") - assert LayoutView.release_link("1.3.4") == "1.3.4" + assert LayoutView.release_link("v1.3.4-beta") == + {:safe, + ~s(v1.3.4-beta)} end test "use the enviroment release link when it's configured" do @@ -81,9 +97,9 @@ defmodule BlockScoutWeb.LayoutViewTest do "https://github.com/poanetwork/blockscout/releases/tag/v1.3.4-beta" ) - assert LayoutView.release_link("1.3.4") == + assert LayoutView.release_link("v1.3.4-beta") == {:safe, - ~s(1.3.4)} + ~s(v1.3.4-beta)} end end diff --git a/apps/explorer/config/config.exs b/apps/explorer/config/config.exs index e6eb5c4813..0ba1a960c7 100644 --- a/apps/explorer/config/config.exs +++ b/apps/explorer/config/config.exs @@ -13,6 +13,8 @@ config :explorer, config :explorer, Explorer.Counters.AverageBlockTime, enabled: true +config :explorer, Explorer.Chain.BlockNumberCache, enabled: true + config :explorer, Explorer.ExchangeRates.Source.CoinMarketCap, pages: String.to_integer(System.get_env("COINMARKETCAP_PAGES") || "10") @@ -58,6 +60,14 @@ config :explorer, Explorer.Staking.PoolsReader, validators_contract_address: System.get_env("POS_VALIDATORS_CONTRACT"), staking_contract_address: System.get_env("POS_STAKING_CONTRACT") +if System.get_env("POS_STAKING_CONTRACT") do + config :explorer, Explorer.Staking.EpochCounter, + enabled: true, + staking_contract_address: System.get_env("POS_STAKING_CONTRACT") +else + config :explorer, Explorer.Staking.EpochCounter, enabled: false +end + if System.get_env("SUPPLY_MODULE") == "TokenBridge" do config :explorer, supply: Explorer.Chain.Supply.TokenBridge end diff --git a/apps/explorer/config/test.exs b/apps/explorer/config/test.exs index 0a717d8b27..f27fc3b350 100644 --- a/apps/explorer/config/test.exs +++ b/apps/explorer/config/test.exs @@ -13,6 +13,8 @@ config :explorer, Explorer.Repo, config :explorer, Explorer.ExchangeRates, enabled: false, store: :ets +config :explorer, Explorer.Chain.BlockNumberCache, enabled: false + config :explorer, Explorer.KnownTokens, enabled: false, store: :ets config :explorer, Explorer.Counters.AverageBlockTime, enabled: false diff --git a/apps/explorer/lib/explorer/application.ex b/apps/explorer/lib/explorer/application.ex index 417b1eaa8f..d886dd8a0b 100644 --- a/apps/explorer/lib/explorer/application.ex +++ b/apps/explorer/lib/explorer/application.ex @@ -51,7 +51,8 @@ defmodule Explorer.Application do configure(Explorer.Market.History.Cataloger), configure(Explorer.Counters.AddressesWithBalanceCounter), configure(Explorer.Counters.AverageBlockTime), - configure(Explorer.Validator.MetadataProcessor) + configure(Explorer.Validator.MetadataProcessor), + configure(Explorer.Staking.EpochCounter) ] |> List.flatten() end diff --git a/apps/explorer/lib/explorer/chain.ex b/apps/explorer/lib/explorer/chain.ex index 713d449d72..0977df1dd2 100644 --- a/apps/explorer/lib/explorer/chain.ex +++ b/apps/explorer/lib/explorer/chain.ex @@ -10,6 +10,7 @@ defmodule Explorer.Chain do limit: 2, order_by: 2, order_by: 3, + offset: 2, preload: 2, select: 2, subquery: 1, @@ -279,6 +280,38 @@ defmodule Explorer.Chain do |> Enum.take(paging_options.page_size) end + @spec address_to_logs(Address.t(), [paging_options]) :: [ + Log.t() + ] + def address_to_logs( + %Address{hash: %Hash{byte_count: unquote(Hash.Address.byte_count())} = address_hash}, + options \\ [] + ) + when is_list(options) do + paging_options = Keyword.get(options, :paging_options) || %PagingOptions{page_size: 50} + + {block_number, transaction_index, log_index} = paging_options.key || {BlockNumberCache.max_number(), 0, 0} + + query = + from(log in Log, + inner_join: transaction in assoc(log, :transaction), + order_by: [desc: transaction.block_number, desc: transaction.index], + preload: [:transaction], + where: + log.address_hash == ^address_hash and + (transaction.block_number < ^block_number or + (transaction.block_number == ^block_number and transaction.index > ^transaction_index) or + (transaction.block_number == ^block_number and transaction.index == ^transaction_index and + log.index > ^log_index)), + limit: ^paging_options.page_size, + select: log + ) + + query + |> Repo.all() + |> Enum.take(paging_options.page_size) + end + @doc """ Finds all `t:Explorer.Chain.Transaction.t/0`s given the address_hash and the token contract address hash. @@ -2565,7 +2598,7 @@ defmodule Explorer.Chain do join: duplicate in subquery(query), on: duplicate.nonce == pending.nonce, on: duplicate.from_address_hash == pending.from_address_hash, - where: pending.hash in ^hashes + where: pending.hash in ^hashes and is_nil(pending.block_hash) ) Repo.update_all(transactions_to_update, [set: [error: "dropped/replaced", status: :error]], timeout: timeout) @@ -2802,6 +2835,7 @@ defmodule Explorer.Chain do on: smart_contract.address_hash == address.hash, where: not is_nil(address.contract_code), where: is_nil(smart_contract.address_hash), + where: address.contract_code != <<>>, preload: [{:smart_contract, smart_contract}, :decompiled_smart_contracts], order_by: [asc: address.inserted_at], limit: ^limit, @@ -2811,6 +2845,19 @@ defmodule Explorer.Chain do Repo.all(query) end + def list_empty_contracts(limit, offset) do + query = + from(address in Address, + where: address.contract_code == <<>>, + preload: [:smart_contract, :decompiled_smart_contracts], + order_by: [asc: address.inserted_at], + limit: ^limit, + offset: ^offset + ) + + Repo.all(query) + end + def list_not_decompiled_contracts(limit, offset) do query = from( @@ -2820,6 +2867,7 @@ defmodule Explorer.Chain do "NOT EXISTS (SELECT 1 FROM decompiled_smart_contracts WHERE decompiled_smart_contracts.address_hash = ?)", address.hash ), + where: address.contract_code != <<>>, left_join: smart_contract in SmartContract, on: smart_contract.address_hash == address.hash, left_join: decompiled_smart_contract in DecompiledSmartContract, @@ -2856,6 +2904,75 @@ defmodule Explorer.Chain do value end + @doc "Get staking pools from the DB" + @spec staking_pools(filter :: :validator | :active | :inactive, options :: PagingOptions.t()) :: [map()] + def staking_pools(filter, %PagingOptions{page_size: page_size, page_number: page_number} \\ @default_paging_options) do + off = page_size * (page_number - 1) + + Address.Name + |> staking_pool_filter(filter) + |> limit(^page_size) + |> offset(^off) + |> Repo.all() + end + + @doc "Get count of staking pools from the DB" + @spec staking_pools_count(filter :: :validator | :active | :inactive) :: integer + def staking_pools_count(filter) do + Address.Name + |> staking_pool_filter(filter) + |> Repo.aggregate(:count, :address_hash) + end + + defp staking_pool_filter(query, :validator) do + where( + query, + [address], + fragment( + """ + (?->>'is_active')::boolean = true and + (?->>'deleted')::boolean is not true and + (?->>'is_validator')::boolean = true + """, + address.metadata, + address.metadata, + address.metadata + ) + ) + end + + defp staking_pool_filter(query, :active) do + where( + query, + [address], + fragment( + """ + (?->>'is_active')::boolean = true and + (?->>'deleted')::boolean is not true + """, + address.metadata, + address.metadata + ) + ) + end + + defp staking_pool_filter(query, :inactive) do + where( + query, + [address], + fragment( + """ + (?->>'is_active')::boolean = false and + (?->>'deleted')::boolean is not true + """, + address.metadata, + address.metadata + ) + ) + end + + defp staking_pool_filter(query, _), do: query + defp with_decompiled_code_flag(query, hash) do has_decompiled_code_query = from(decompiled_contract in DecompiledSmartContract, diff --git a/apps/explorer/lib/explorer/chain/block.ex b/apps/explorer/lib/explorer/chain/block.ex index 58d187d9d8..9cf86b570b 100644 --- a/apps/explorer/lib/explorer/chain/block.ex +++ b/apps/explorer/lib/explorer/chain/block.ex @@ -10,7 +10,7 @@ defmodule Explorer.Chain.Block do alias Explorer.Chain.{Address, Gas, Hash, Transaction} alias Explorer.Chain.Block.{Reward, SecondDegreeRelation} - @optional_attrs ~w(internal_transactions_indexed_at size)a + @optional_attrs ~w(internal_transactions_indexed_at size refetch_needed)a @required_attrs ~w(consensus difficulty gas_limit gas_used hash miner_hash nonce number parent_hash timestamp total_difficulty)a @@ -63,7 +63,8 @@ defmodule Explorer.Chain.Block do timestamp: DateTime.t(), total_difficulty: difficulty(), transactions: %Ecto.Association.NotLoaded{} | [Transaction.t()], - internal_transactions_indexed_at: DateTime.t() + internal_transactions_indexed_at: DateTime.t(), + refetch_needed: boolean() } @primary_key {:hash, Hash.Full, autogenerate: false} @@ -78,6 +79,7 @@ defmodule Explorer.Chain.Block do field(:timestamp, :utc_datetime_usec) field(:total_difficulty, :decimal) field(:internal_transactions_indexed_at, :utc_datetime_usec) + field(:refetch_needed, :boolean) timestamps() diff --git a/apps/explorer/lib/explorer/chain/block_number_cache.ex b/apps/explorer/lib/explorer/chain/block_number_cache.ex index 28d8d83e9c..2f335c7a2c 100644 --- a/apps/explorer/lib/explorer/chain/block_number_cache.ex +++ b/apps/explorer/lib/explorer/chain/block_number_cache.ex @@ -6,13 +6,10 @@ defmodule Explorer.Chain.BlockNumberCache do alias Explorer.Chain @tab :block_number_cache - # 30 minutes - @cache_period 1_000 * 60 * 30 @key "min_max" - @opts_key "opts" - @spec setup(Keyword.t()) :: :ok - def setup(opts \\ []) do + @spec setup() :: :ok + def setup do if :ets.whereis(@tab) == :undefined do :ets.new(@tab, [ :set, @@ -22,7 +19,6 @@ defmodule Explorer.Chain.BlockNumberCache do ]) end - setup_opts(opts) update_cache() :ok @@ -41,15 +37,11 @@ defmodule Explorer.Chain.BlockNumberCache do end defp value(type) do - initial_cache = {_min, _max, old_current_time} = cached_values() - - {min, max, _current_time} = - if current_time() - old_current_time > cache_period() do - update_cache() - + {min, max} = + if Application.get_env(:explorer, __MODULE__)[:enabled] do cached_values() else - initial_cache + min_and_max_from_db() end case type do @@ -59,18 +51,29 @@ defmodule Explorer.Chain.BlockNumberCache do end end - defp update_cache do - current_time = current_time() - {min, max} = min_and_max_from_db() - tuple = {min, max, current_time} + @spec update(non_neg_integer()) :: boolean() + def update(number) do + {old_min, old_max} = cached_values() - :ets.insert(@tab, {@key, tuple}) + cond do + number > old_max -> + tuple = {old_min, number} + :ets.insert(@tab, {@key, tuple}) + + number < old_min -> + tuple = {number, old_max} + :ets.insert(@tab, {@key, tuple}) + + true -> + false + end end - defp setup_opts(opts) do - cache_period = opts[:cache_period] || @cache_period + defp update_cache do + {min, max} = min_and_max_from_db() + tuple = {min, max} - :ets.insert(@tab, {@opts_key, cache_period}) + :ets.insert(@tab, {@key, tuple}) end defp cached_values do @@ -79,22 +82,10 @@ defmodule Explorer.Chain.BlockNumberCache do cached_values end - defp cache_period do - [{_, cache_period}] = :ets.lookup(@tab, @opts_key) - - cache_period - end - defp min_and_max_from_db do Chain.fetch_min_and_max_block_numbers() rescue _e -> {0, 0} end - - defp current_time do - utc_now = DateTime.utc_now() - - DateTime.to_unix(utc_now, :millisecond) - end end diff --git a/apps/explorer/lib/explorer/chain/import/runner/staking_pools.ex b/apps/explorer/lib/explorer/chain/import/runner/staking_pools.ex index aaf5d7242e..b21c0c4441 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/staking_pools.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/staking_pools.ex @@ -41,6 +41,9 @@ defmodule Explorer.Chain.Import.Runner.StakingPools do |> Map.put(:timestamps, timestamps) multi + |> Multi.run(:mark_as_deleted, fn repo, _ -> + mark_as_deleted(repo, changes_list, insert_options) + end) |> Multi.run(:insert_staking_pools, fn repo, _ -> insert(repo, changes_list, insert_options) end) @@ -49,6 +52,32 @@ defmodule Explorer.Chain.Import.Runner.StakingPools do @impl Import.Runner def timeout, do: @timeout + defp mark_as_deleted(repo, changes_list, %{timeout: timeout}) when is_list(changes_list) do + addresses = Enum.map(changes_list, & &1.address_hash) + + query = + from( + address_name in Address.Name, + where: + address_name.address_hash not in ^addresses and + fragment("(?->>'is_pool')::boolean = true", address_name.metadata), + update: [ + set: [ + metadata: fragment("? || '{\"deleted\": true}'::jsonb", address_name.metadata) + ] + ] + ) + + try do + {_, result} = repo.update_all(query, [], timeout: timeout) + + {:ok, result} + rescue + postgrex_error in Postgrex.Error -> + {:error, %{exception: postgrex_error}} + end + end + @spec insert(Repo.t(), [map()], %{ optional(:on_conflict) => Import.Runner.on_conflict(), required(:timeout) => timeout, @@ -62,7 +91,7 @@ defmodule Explorer.Chain.Import.Runner.StakingPools do {:ok, _} = Import.insert_changes_list( repo, - changes_list, + stakes_ratio(changes_list), conflict_target: {:unsafe_fragment, "(address_hash) where \"primary\" = true"}, on_conflict: on_conflict, for: Address.Name, @@ -85,4 +114,20 @@ defmodule Explorer.Chain.Import.Runner.StakingPools do ] ) end + + # Calculates staked ratio for each pool + defp stakes_ratio(pools) do + active_pools = Enum.filter(pools, & &1.metadata[:is_active]) + + stakes_total = + Enum.reduce(pools, 0, fn pool, acc -> + acc + pool.metadata[:staked_amount] + end) + + Enum.map(active_pools, fn pool -> + staked_ratio = if stakes_total > 0, do: pool.metadata[:staked_amount] / stakes_total, else: 0 + + put_in(pool, [:metadata, :staked_ratio], staked_ratio) + end) + end end diff --git a/apps/explorer/lib/explorer/chain/import/runner/transactions.ex b/apps/explorer/lib/explorer/chain/import/runner/transactions.ex index 4f1d2d6fe5..1a9fcc497d 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/transactions.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/transactions.ex @@ -8,7 +8,7 @@ defmodule Explorer.Chain.Import.Runner.Transactions do import Ecto.Query, only: [from: 2] alias Ecto.{Multi, Repo} - alias Explorer.Chain.{Data, Hash, Import, Transaction} + alias Explorer.Chain.{Block, Data, Hash, Import, Transaction} alias Explorer.Chain.Import.Runner.TokenTransfers @behaviour Import.Runner @@ -42,9 +42,13 @@ defmodule Explorer.Chain.Import.Runner.Transactions do |> Map.put(:timestamps, timestamps) |> Map.put(:token_transfer_transaction_hash_set, token_transfer_transaction_hash_set(options)) - Multi.run(multi, :transactions, fn repo, _ -> + multi + |> Multi.run(:transactions, fn repo, _ -> insert(repo, changes_list, insert_options) end) + |> Multi.run(:recollated_transactions, fn repo, %{transactions: transactions} -> + discard_blocks_for_recollated_transactions(repo, transactions, insert_options) + end) end @impl Import.Runner @@ -87,7 +91,7 @@ defmodule Explorer.Chain.Import.Runner.Transactions do on_conflict: on_conflict, for: Transaction, returning: - ~w(block_number index hash internal_transactions_indexed_at block_hash nonce from_address_hash created_contract_address_hash)a, + ~w(block_number index hash internal_transactions_indexed_at block_hash old_block_hash nonce from_address_hash created_contract_address_hash)a, timeout: timeout, timestamps: timestamps ) @@ -99,6 +103,7 @@ defmodule Explorer.Chain.Import.Runner.Transactions do update: [ set: [ block_hash: fragment("EXCLUDED.block_hash"), + old_block_hash: transaction.block_hash, block_number: fragment("EXCLUDED.block_number"), created_contract_address_hash: fragment("EXCLUDED.created_contract_address_hash"), cumulative_gas_used: fragment("EXCLUDED.cumulative_gas_used"), @@ -179,4 +184,43 @@ defmodule Explorer.Chain.Import.Runner.Transactions do end defp put_internal_transactions_indexed_at?(_, _), do: false + + defp discard_blocks_for_recollated_transactions(repo, transactions, %{ + timeout: timeout, + timestamps: %{updated_at: updated_at} + }) + when is_list(transactions) do + ordered_block_hashes = + transactions + |> Enum.filter(fn %{block_hash: block_hash, old_block_hash: old_block_hash} -> + not is_nil(old_block_hash) and block_hash != old_block_hash + end) + |> MapSet.new(& &1.old_block_hash) + |> Enum.sort() + + if Enum.empty?(ordered_block_hashes) do + {:ok, []} + else + query = + from( + block in Block, + where: block.hash in ^ordered_block_hashes, + update: [ + set: [ + consensus: false, + updated_at: ^updated_at + ] + ] + ) + + try do + {_, result} = repo.update_all(query, [], timeout: timeout) + + {:ok, result} + rescue + postgrex_error in Postgrex.Error -> + {:error, %{exception: postgrex_error, block_hashes: ordered_block_hashes}} + end + end + end end diff --git a/apps/explorer/lib/explorer/chain/internal_transaction/result.ex b/apps/explorer/lib/explorer/chain/internal_transaction/result.ex index 5b4e3102fc..c4c680e7d4 100644 --- a/apps/explorer/lib/explorer/chain/internal_transaction/result.ex +++ b/apps/explorer/lib/explorer/chain/internal_transaction/result.ex @@ -19,7 +19,7 @@ defmodule Explorer.Chain.InternalTransaction.Result do {key, to_string(hash)} end - defp entry_to_raw({"code", _} = entry), do: entry + defp entry_to_raw({"code", code}), do: {"code", Data.to_string(code)} defp entry_to_raw({key, decimal}) when key in ~w(gasUsed) do integer = diff --git a/apps/explorer/lib/explorer/chain/transaction.ex b/apps/explorer/lib/explorer/chain/transaction.ex index 1cf9c8f116..de99198181 100644 --- a/apps/explorer/lib/explorer/chain/transaction.ex +++ b/apps/explorer/lib/explorer/chain/transaction.ex @@ -205,6 +205,11 @@ defmodule Explorer.Chain.Transaction do field(:v, :decimal) field(:value, Wei) + # A transient field for deriving old block hash during transaction upserts. + # Used to force refetch of a block in case a transaction is re-collated + # in a different block. See: https://github.com/poanetwork/blockscout/issues/1911 + field(:old_block_hash, Hash.Full) + timestamps() belongs_to(:block, Block, foreign_key: :block_hash, references: :hash, type: Hash.Full) diff --git a/apps/explorer/lib/explorer/paging_options.ex b/apps/explorer/lib/explorer/paging_options.ex index 1bac2dc71a..0828a0afad 100644 --- a/apps/explorer/lib/explorer/paging_options.ex +++ b/apps/explorer/lib/explorer/paging_options.ex @@ -4,10 +4,11 @@ defmodule Explorer.PagingOptions do number and index. """ - @type t :: %__MODULE__{key: key, page_size: page_size} + @type t :: %__MODULE__{key: key, page_size: page_size, page_number: page_number} @typep key :: any() @typep page_size :: non_neg_integer() + @typep page_number :: pos_integer() - defstruct [:key, :page_size] + defstruct [:key, :page_size, page_number: 1] end diff --git a/apps/explorer/lib/explorer/staking/epoch_counter.ex b/apps/explorer/lib/explorer/staking/epoch_counter.ex new file mode 100644 index 0000000000..c35ec1a6c9 --- /dev/null +++ b/apps/explorer/lib/explorer/staking/epoch_counter.ex @@ -0,0 +1,124 @@ +defmodule Explorer.Staking.EpochCounter do + @moduledoc """ + Fetches current staking epoch number and the epoch end block number. + It subscribes to handle new blocks and conclude whether the epoch is over. + """ + + use GenServer + + alias Explorer.Chain.Events.Subscriber + alias Explorer.SmartContract.Reader + + @table_name __MODULE__ + @epoch_key "epoch_num" + @epoch_end_key "epoch_end_block" + + @doc "Current staking epoch number" + def epoch_number do + if :ets.info(@table_name) != :undefined do + case :ets.lookup(@table_name, @epoch_key) do + [{_, epoch_num}] -> + epoch_num + + _ -> + 0 + end + end + end + + @doc "Block number on which will start new epoch" + def epoch_end_block do + if :ets.info(@table_name) != :undefined do + case :ets.lookup(@table_name, @epoch_end_key) do + [{_, epoch_end}] -> + epoch_end + + _ -> + 0 + end + end + end + + def start_link([]) do + GenServer.start_link(__MODULE__, [], name: __MODULE__) + end + + def init([]) do + :ets.new(@table_name, [ + :set, + :named_table, + :public, + write_concurrency: true + ]) + + Subscriber.to(:blocks, :realtime) + {:ok, [], {:continue, :epoch_info}} + end + + def handle_continue(:epoch_info, state) do + fetch_epoch_info() + {:noreply, state} + end + + @doc "Handles new blocks and decides to fetch new epoch info" + def handle_info({:chain_event, :blocks, :realtime, blocks}, state) do + new_block_number = + blocks + |> Enum.map(&Map.get(&1, :number, 0)) + |> Enum.max(fn -> 0 end) + + case :ets.lookup(@table_name, @epoch_end_key) do + [] -> + fetch_epoch_info() + + [{_, epoch_end_block}] when epoch_end_block < new_block_number -> + fetch_epoch_info() + + _ -> + :ok + end + + {:noreply, state} + end + + defp fetch_epoch_info do + with data <- get_epoch_info(), + {:ok, [epoch_num]} <- data["stakingEpoch"], + {:ok, [epoch_end_block]} <- data["stakingEpochEndBlock"] do + :ets.insert(@table_name, {@epoch_key, epoch_num}) + :ets.insert(@table_name, {@epoch_end_key, epoch_end_block}) + end + end + + defp get_epoch_info do + contract_abi = abi("staking.json") + + functions = ["stakingEpoch", "stakingEpochEndBlock"] + + functions + |> Enum.map(fn function -> + %{ + contract_address: staking_address(), + function_name: function, + args: [] + } + end) + |> Reader.query_contracts(contract_abi) + |> Enum.zip(functions) + |> Enum.into(%{}, fn {response, function} -> + {function, response} + end) + end + + defp staking_address do + Application.get_env(:explorer, __MODULE__, [])[:staking_contract_address] + end + + # sobelow_skip ["Traversal"] + defp abi(file_name) do + :explorer + |> Application.app_dir("priv/contracts_abi/pos/#{file_name}") + |> File.read!() + |> Jason.decode!() + end +end diff --git a/apps/explorer/lib/explorer/staking/pools_reader.ex b/apps/explorer/lib/explorer/staking/pools_reader.ex index de03ff10b5..608fea3863 100644 --- a/apps/explorer/lib/explorer/staking/pools_reader.ex +++ b/apps/explorer/lib/explorer/staking/pools_reader.ex @@ -29,6 +29,7 @@ defmodule Explorer.Staking.PoolsReader do {:ok, [delegator_addresses]} <- data["poolDelegators"], delegators_count = Enum.count(delegator_addresses), {:ok, [staked_amount]} <- data["stakeAmountTotalMinusOrderedWithdraw"], + {:ok, [self_staked_amount]} <- data["stakeAmountMinusOrderedWithdraw"], {:ok, [is_validator]} <- data["isValidator"], {:ok, [was_validator_count]} <- data["validatorCounter"], {:ok, [is_banned]} <- data["isValidatorBanned"], @@ -42,6 +43,7 @@ defmodule Explorer.Staking.PoolsReader do is_active: is_active, delegators_count: delegators_count, staked_amount: staked_amount, + self_staked_amount: self_staked_amount, is_validator: is_validator, was_validator_count: was_validator_count, is_banned: is_banned, @@ -77,14 +79,15 @@ defmodule Explorer.Staking.PoolsReader do contract_abi = abi("staking.json") ++ abi("validators.json") methods = [ - {:staking, "isPoolActive", staking_address}, - {:staking, "poolDelegators", staking_address}, - {:staking, "stakeAmountTotalMinusOrderedWithdraw", staking_address}, - {:validators, "isValidator", mining_address}, - {:validators, "validatorCounter", mining_address}, - {:validators, "isValidatorBanned", mining_address}, - {:validators, "bannedUntil", mining_address}, - {:validators, "banCounter", mining_address} + {:staking, "isPoolActive", [staking_address]}, + {:staking, "poolDelegators", [staking_address]}, + {:staking, "stakeAmountTotalMinusOrderedWithdraw", [staking_address]}, + {:staking, "stakeAmountMinusOrderedWithdraw", [staking_address, staking_address]}, + {:validators, "isValidator", [mining_address]}, + {:validators, "validatorCounter", [mining_address]}, + {:validators, "isValidatorBanned", [mining_address]}, + {:validators, "bannedUntil", [mining_address]}, + {:validators, "banCounter", [mining_address]} ] methods @@ -96,11 +99,11 @@ defmodule Explorer.Staking.PoolsReader do end) end - defp format_request({contract_name, function_name, param}) do + defp format_request({contract_name, function_name, params}) do %{ contract_address: contract(contract_name), function_name: function_name, - args: [param] + args: params } end diff --git a/apps/explorer/priv/contracts_abi/pos/staking.json b/apps/explorer/priv/contracts_abi/pos/staking.json index 7bcbcfb18c..33f773ea61 100644 --- a/apps/explorer/priv/contracts_abi/pos/staking.json +++ b/apps/explorer/priv/contracts_abi/pos/staking.json @@ -1,36 +1,17 @@ [ { "constant": true, - "inputs": [], - "name": "STAKE_UNIT", - "outputs": [ + "inputs": [ { - "name": "", - "type": "uint256" - } - ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "MAX_DELEGATORS_PER_POOL", - "outputs": [ + "name": "_poolStakingAddress", + "type": "address" + }, { - "name": "", - "type": "uint256" + "name": "_delegator", + "type": "address" } ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "MAX_CANDIDATES", + "name": "poolDelegatorIndex", "outputs": [ { "name": "", @@ -42,154 +23,55 @@ "type": "function" }, { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "name": "fromPoolStakingAddress", - "type": "address" - }, - { - "indexed": true, - "name": "staker", - "type": "address" - }, - { - "indexed": true, - "name": "stakingEpoch", - "type": "uint256" - }, - { - "indexed": false, - "name": "amount", - "type": "uint256" - } - ], - "name": "Claimed", - "type": "event" - }, - { - "anonymous": false, + "constant": true, "inputs": [ { - "indexed": true, - "name": "toPoolStakingAddress", - "type": "address" - }, - { - "indexed": true, - "name": "staker", + "name": "_poolStakingAddress", "type": "address" - }, - { - "indexed": true, - "name": "stakingEpoch", - "type": "uint256" - }, - { - "indexed": false, - "name": "amount", - "type": "uint256" } ], - "name": "Staked", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "name": "fromPoolStakingAddress", - "type": "address" - }, - { - "indexed": true, - "name": "toPoolStakingAddress", - "type": "address" - }, - { - "indexed": true, - "name": "staker", - "type": "address" - }, - { - "indexed": true, - "name": "stakingEpoch", - "type": "uint256" - }, + "name": "stakeAmountTotalMinusOrderedWithdraw", + "outputs": [ { - "indexed": false, - "name": "amount", + "name": "", "type": "uint256" } ], - "name": "StakeMoved", - "type": "event" + "payable": false, + "stateMutability": "view", + "type": "function" }, { - "anonymous": false, + "constant": false, "inputs": [ { - "indexed": true, - "name": "fromPoolStakingAddress", - "type": "address" - }, - { - "indexed": true, - "name": "staker", + "name": "_erc20TokenContract", "type": "address" - }, - { - "indexed": true, - "name": "stakingEpoch", - "type": "uint256" - }, - { - "indexed": false, - "name": "amount", - "type": "int256" } ], - "name": "WithdrawalOrdered", - "type": "event" + "name": "setErc20TokenContract", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" }, { - "anonymous": false, + "constant": false, "inputs": [ { - "indexed": true, - "name": "fromPoolStakingAddress", + "name": "_fromPoolStakingAddress", "type": "address" }, { - "indexed": true, - "name": "staker", + "name": "_toPoolStakingAddress", "type": "address" }, { - "indexed": true, - "name": "stakingEpoch", - "type": "uint256" - }, - { - "indexed": false, - "name": "amount", + "name": "_amount", "type": "uint256" } ], - "name": "Withdrawn", - "type": "event" - }, - { - "constant": false, - "inputs": [ - { - "name": "_unremovableStakingAddress", - "type": "address" - } - ], - "name": "clearUnremovableValidator", + "name": "moveStake", "outputs": [], "payable": false, "stateMutability": "nonpayable", @@ -197,8 +79,13 @@ }, { "constant": false, - "inputs": [], - "name": "incrementStakingEpoch", + "inputs": [ + { + "name": "_minStake", + "type": "uint256" + } + ], + "name": "setDelegatorMinStake", "outputs": [], "payable": false, "stateMutability": "nonpayable", @@ -231,132 +118,75 @@ "constant": false, "inputs": [ { - "name": "_fromPoolStakingAddress", - "type": "address" - }, - { - "name": "_toPoolStakingAddress", - "type": "address" - }, - { - "name": "_amount", + "name": "_minStake", "type": "uint256" } ], - "name": "moveStake", + "name": "setCandidateMinStake", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { - "constant": false, + "constant": true, "inputs": [ { - "name": "_toPoolStakingAddress", + "name": "_poolStakingAddress", "type": "address" - }, - { - "name": "_amount", - "type": "uint256" } ], - "name": "stake", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "_fromPoolStakingAddress", - "type": "address" - }, + "name": "stakeAmountTotal", + "outputs": [ { - "name": "_amount", + "name": "", "type": "uint256" } ], - "name": "withdraw", - "outputs": [], "payable": false, - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { - "constant": false, + "constant": true, "inputs": [ { "name": "_poolStakingAddress", "type": "address" }, { - "name": "_amount", - "type": "int256" - } - ], - "name": "orderWithdraw", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ - { - "name": "_poolStakingAddress", + "name": "_staker", "type": "address" } ], - "name": "claimOrderedWithdraw", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ + "name": "stakeAmountMinusOrderedWithdraw", + "outputs": [ { - "name": "_erc20TokenContract", - "type": "address" + "name": "", + "type": "uint256" } ], - "name": "setErc20TokenContract", - "outputs": [], "payable": false, - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { - "constant": false, + "constant": true, "inputs": [ { - "name": "_minStake", - "type": "uint256" + "name": "_stakingAddress", + "type": "address" } ], - "name": "setCandidateMinStake", - "outputs": [], - "payable": false, - "stateMutability": "nonpayable", - "type": "function" - }, - { - "constant": false, - "inputs": [ + "name": "poolInactiveIndex", + "outputs": [ { - "name": "_minStake", + "name": "", "type": "uint256" } ], - "name": "setDelegatorMinStake", - "outputs": [], "payable": false, - "stateMutability": "nonpayable", + "stateMutability": "view", "type": "function" }, { @@ -375,12 +205,21 @@ }, { "constant": true, - "inputs": [], - "name": "getPoolsInactive", + "inputs": [ + { + "name": "_poolStakingAddress", + "type": "address" + }, + { + "name": "_staker", + "type": "address" + } + ], + "name": "maxWithdrawAllowed", "outputs": [ { "name": "", - "type": "address[]" + "type": "uint256" } ], "payable": false, @@ -389,30 +228,21 @@ }, { "constant": true, - "inputs": [], - "name": "getPoolsLikelihood", - "outputs": [ + "inputs": [ { - "name": "likelihoods", - "type": "int256[]" + "name": "_poolStakingAddress", + "type": "address" }, { - "name": "sum", - "type": "int256" + "name": "_staker", + "type": "address" } ], - "payable": false, - "stateMutability": "view", - "type": "function" - }, - { - "constant": true, - "inputs": [], - "name": "getPoolsToBeElected", + "name": "stakeAmountByCurrentEpoch", "outputs": [ { "name": "", - "type": "address[]" + "type": "uint256" } ], "payable": false, @@ -422,11 +252,11 @@ { "constant": true, "inputs": [], - "name": "getPoolsToBeRemoved", + "name": "stakingEpoch", "outputs": [ { "name": "", - "type": "address[]" + "type": "uint256" } ], "payable": false, @@ -436,11 +266,11 @@ { "constant": true, "inputs": [], - "name": "areStakeAndWithdrawAllowed", + "name": "getDelegatorMinStake", "outputs": [ { "name": "", - "type": "bool" + "type": "uint256" } ], "payable": false, @@ -477,8 +307,17 @@ }, { "constant": true, - "inputs": [], - "name": "getDelegatorMinStake", + "inputs": [ + { + "name": "_poolStakingAddress", + "type": "address" + }, + { + "name": "_staker", + "type": "address" + } + ], + "name": "maxWithdrawOrderAllowed", "outputs": [ { "name": "", @@ -493,15 +332,19 @@ "constant": true, "inputs": [ { - "name": "_stakingAddress", + "name": "_poolStakingAddress", + "type": "address" + }, + { + "name": "_delegator", "type": "address" } ], - "name": "isPoolActive", + "name": "poolDelegatorInactiveIndex", "outputs": [ { "name": "", - "type": "bool" + "type": "uint256" } ], "payable": false, @@ -510,21 +353,49 @@ }, { "constant": true, + "inputs": [], + "name": "getPoolsLikelihood", + "outputs": [ + { + "name": "likelihoods", + "type": "int256[]" + }, + { + "name": "sum", + "type": "int256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, "inputs": [ { - "name": "_poolStakingAddress", + "name": "_unremovableStakingAddress", "type": "address" - }, + } + ], + "name": "clearUnremovableValidator", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ { - "name": "_staker", + "name": "_poolStakingAddress", "type": "address" } ], - "name": "maxWithdrawAllowed", + "name": "poolDelegators", "outputs": [ { "name": "", - "type": "uint256" + "type": "address[]" } ], "payable": false, @@ -543,7 +414,7 @@ "type": "address" } ], - "name": "maxWithdrawOrderAllowed", + "name": "orderWithdrawEpoch", "outputs": [ { "name": "", @@ -581,6 +452,20 @@ "stateMutability": "pure", "type": "function" }, + { + "constant": true, + "inputs": [], + "name": "getPoolsToBeElected", + "outputs": [ + { + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, { "constant": true, "inputs": [ @@ -593,7 +478,7 @@ "type": "address" } ], - "name": "orderedWithdrawAmount", + "name": "stakeAmount", "outputs": [ { "name": "", @@ -608,34 +493,80 @@ "constant": true, "inputs": [ { - "name": "_poolStakingAddress", + "name": "_stakingAddress", "type": "address" } ], - "name": "orderedWithdrawAmountTotal", + "name": "isPoolActive", "outputs": [ { "name": "", - "type": "uint256" + "type": "bool" } ], "payable": false, "stateMutability": "view", "type": "function" }, + { + "constant": false, + "inputs": [ + { + "name": "_toPoolStakingAddress", + "type": "address" + }, + { + "name": "_amount", + "type": "uint256" + } + ], + "name": "stake", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, { "constant": true, + "inputs": [ + { + "name": "_stakingAddress", + "type": "address" + } + ], + "name": "poolIndex", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, "inputs": [ { "name": "_poolStakingAddress", "type": "address" }, { - "name": "_staker", - "type": "address" + "name": "_amount", + "type": "int256" } ], - "name": "orderWithdrawEpoch", + "name": "orderWithdraw", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "STAKE_UNIT", "outputs": [ { "name": "", @@ -646,6 +577,34 @@ "stateMutability": "view", "type": "function" }, + { + "constant": false, + "inputs": [ + { + "name": "_poolStakingAddress", + "type": "address" + } + ], + "name": "claimOrderedWithdraw", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getPoolsToBeRemoved", + "outputs": [ + { + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, { "constant": true, "inputs": [ @@ -654,7 +613,7 @@ "type": "address" } ], - "name": "stakeAmountTotal", + "name": "orderedWithdrawAmountTotal", "outputs": [ { "name": "", @@ -665,19 +624,51 @@ "stateMutability": "view", "type": "function" }, + { + "constant": true, + "inputs": [], + "name": "getPoolsInactive", + "outputs": [ + { + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "validatorSetContract", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, { "constant": true, "inputs": [ { "name": "_poolStakingAddress", "type": "address" + }, + { + "name": "_staker", + "type": "address" } ], - "name": "poolDelegators", + "name": "orderedWithdrawAmount", "outputs": [ { "name": "", - "type": "address[]" + "type": "uint256" } ], "payable": false, @@ -686,17 +677,8 @@ }, { "constant": true, - "inputs": [ - { - "name": "_poolStakingAddress", - "type": "address" - }, - { - "name": "_delegator", - "type": "address" - } - ], - "name": "poolDelegatorIndex", + "inputs": [], + "name": "MAX_DELEGATORS_PER_POOL", "outputs": [ { "name": "", @@ -711,15 +693,11 @@ "constant": true, "inputs": [ { - "name": "_poolStakingAddress", - "type": "address" - }, - { - "name": "_delegator", + "name": "_stakingAddress", "type": "address" } ], - "name": "poolDelegatorInactiveIndex", + "name": "poolToBeRemovedIndex", "outputs": [ { "name": "", @@ -730,15 +708,19 @@ "stateMutability": "view", "type": "function" }, + { + "constant": false, + "inputs": [], + "name": "incrementStakingEpoch", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, { "constant": true, - "inputs": [ - { - "name": "_stakingAddress", - "type": "address" - } - ], - "name": "poolIndex", + "inputs": [], + "name": "MAX_CANDIDATES", "outputs": [ { "name": "", @@ -750,22 +732,21 @@ "type": "function" }, { - "constant": true, + "constant": false, "inputs": [ { - "name": "_stakingAddress", + "name": "_fromPoolStakingAddress", "type": "address" - } - ], - "name": "poolInactiveIndex", - "outputs": [ + }, { - "name": "", + "name": "_amount", "type": "uint256" } ], + "name": "withdraw", + "outputs": [], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function" }, { @@ -788,87 +769,223 @@ "type": "function" }, { - "constant": true, + "anonymous": false, "inputs": [ { - "name": "_stakingAddress", + "indexed": true, + "name": "fromPoolStakingAddress", + "type": "address" + }, + { + "indexed": true, + "name": "staker", "type": "address" + }, + { + "indexed": true, + "name": "stakingEpoch", + "type": "uint256" + }, + { + "indexed": false, + "name": "amount", + "type": "uint256" } ], - "name": "poolToBeRemovedIndex", - "outputs": [ + "name": "Claimed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ { - "name": "", + "indexed": true, + "name": "toPoolStakingAddress", + "type": "address" + }, + { + "indexed": true, + "name": "staker", + "type": "address" + }, + { + "indexed": true, + "name": "stakingEpoch", + "type": "uint256" + }, + { + "indexed": false, + "name": "amount", "type": "uint256" } ], - "payable": false, - "stateMutability": "view", - "type": "function" + "name": "Staked", + "type": "event" }, { - "constant": true, + "anonymous": false, "inputs": [ { - "name": "_poolStakingAddress", + "indexed": false, + "name": "fromPoolStakingAddress", "type": "address" }, { - "name": "_staker", + "indexed": true, + "name": "toPoolStakingAddress", + "type": "address" + }, + { + "indexed": true, + "name": "staker", "type": "address" + }, + { + "indexed": true, + "name": "stakingEpoch", + "type": "uint256" + }, + { + "indexed": false, + "name": "amount", + "type": "uint256" } ], - "name": "stakeAmount", - "outputs": [ + "name": "StakeMoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ { - "name": "", + "indexed": true, + "name": "fromPoolStakingAddress", + "type": "address" + }, + { + "indexed": true, + "name": "staker", + "type": "address" + }, + { + "indexed": true, + "name": "stakingEpoch", "type": "uint256" + }, + { + "indexed": false, + "name": "amount", + "type": "int256" } ], - "payable": false, - "stateMutability": "view", - "type": "function" + "name": "WithdrawalOrdered", + "type": "event" }, { - "constant": true, + "anonymous": false, "inputs": [ { - "name": "_poolStakingAddress", + "indexed": true, + "name": "fromPoolStakingAddress", "type": "address" }, { - "name": "_staker", + "indexed": true, + "name": "staker", "type": "address" + }, + { + "indexed": true, + "name": "stakingEpoch", + "type": "uint256" + }, + { + "indexed": false, + "name": "amount", + "type": "uint256" } ], - "name": "stakeAmountByCurrentEpoch", - "outputs": [ + "name": "Withdrawn", + "type": "event" + }, + { + "constant": false, + "inputs": [ { - "name": "", + "name": "_amount", "type": "uint256" + }, + { + "name": "_miningAddress", + "type": "address" } ], + "name": "addPool", + "outputs": [], "payable": false, - "stateMutability": "view", + "stateMutability": "nonpayable", "type": "function" }, { - "constant": true, + "constant": false, "inputs": [ { - "name": "_poolStakingAddress", + "name": "_validatorSetContract", "type": "address" }, { - "name": "_staker", + "name": "_erc20TokenContract", "type": "address" + }, + { + "name": "_initialStakingAddresses", + "type": "address[]" + }, + { + "name": "_delegatorMinStake", + "type": "uint256" + }, + { + "name": "_candidateMinStake", + "type": "uint256" + }, + { + "name": "_stakingEpochDuration", + "type": "uint256" + }, + { + "name": "_stakeWithdrawDisallowPeriod", + "type": "uint256" } ], - "name": "stakeAmountMinusOrderedWithdraw", + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_blockNumber", + "type": "uint256" + } + ], + "name": "setStakingEpochStartBlock", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "areStakeAndWithdrawAllowed", "outputs": [ { "name": "", - "type": "uint256" + "type": "bool" } ], "payable": false, @@ -877,13 +994,22 @@ }, { "constant": true, - "inputs": [ + "inputs": [], + "name": "stakeWithdrawDisallowPeriod", + "outputs": [ { - "name": "_poolStakingAddress", - "type": "address" + "name": "", + "type": "uint256" } ], - "name": "stakeAmountTotalMinusOrderedWithdraw", + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "stakingEpochDuration", "outputs": [ { "name": "", @@ -897,7 +1023,7 @@ { "constant": true, "inputs": [], - "name": "stakingEpoch", + "name": "stakingEpochStartBlock", "outputs": [ { "name": "", @@ -911,11 +1037,11 @@ { "constant": true, "inputs": [], - "name": "validatorSetContract", + "name": "stakingEpochEndBlock", "outputs": [ { "name": "", - "type": "address" + "type": "uint256" } ], "payable": false, diff --git a/apps/explorer/priv/repo/migrations/20190508152922_add_old_block_hash_for_transactions.exs b/apps/explorer/priv/repo/migrations/20190508152922_add_old_block_hash_for_transactions.exs new file mode 100644 index 0000000000..daf04c22f3 --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20190508152922_add_old_block_hash_for_transactions.exs @@ -0,0 +1,12 @@ +defmodule Explorer.Repo.Migrations.AddOldBlockHashForTransactions do + use Ecto.Migration + + def change do + alter table(:transactions) do + # A transient field for deriving old block hash during transaction upserts. + # Used to force refetch of a block in case a transaction is re-collated + # in a different block. See: https://github.com/poanetwork/blockscout/issues/1911 + add(:old_block_hash, :bytea, null: true) + end + end +end diff --git a/apps/explorer/priv/repo/migrations/20190513134025_add_refetch_needed_to_block.exs b/apps/explorer/priv/repo/migrations/20190513134025_add_refetch_needed_to_block.exs new file mode 100644 index 0000000000..70ddac3e03 --- /dev/null +++ b/apps/explorer/priv/repo/migrations/20190513134025_add_refetch_needed_to_block.exs @@ -0,0 +1,11 @@ +defmodule Explorer.Repo.Migrations.AddRefetchNeededToBlock do + use Ecto.Migration + + def change do + alter table(:blocks) do + add(:refetch_needed, :boolean, default: false) + end + + execute("UPDATE blocks SET refetch_needed = TRUE;", "") + end +end diff --git a/apps/explorer/test/explorer/chain/block_number_cache_test.exs b/apps/explorer/test/explorer/chain/block_number_cache_test.exs index a33263293c..7b501a718b 100644 --- a/apps/explorer/test/explorer/chain/block_number_cache_test.exs +++ b/apps/explorer/test/explorer/chain/block_number_cache_test.exs @@ -3,6 +3,14 @@ defmodule Explorer.Chain.BlockNumberCacheTest do alias Explorer.Chain.BlockNumberCache + setup do + Application.put_env(:explorer, Explorer.Chain.BlockNumberCache, enabled: true) + + on_exit(fn -> + Application.put_env(:explorer, Explorer.Chain.BlockNumberCache, enabled: false) + end) + end + describe "max_number/1" do test "returns max number" do insert(:block, number: 5) @@ -11,33 +19,6 @@ defmodule Explorer.Chain.BlockNumberCacheTest do assert BlockNumberCache.max_number() == 5 end - - test "invalidates cache if period did pass" do - insert(:block, number: 5) - - BlockNumberCache.setup(cache_period: 2_000) - - assert BlockNumberCache.max_number() == 5 - - insert(:block, number: 10) - - Process.sleep(2_000) - - assert BlockNumberCache.max_number() == 10 - assert BlockNumberCache.min_number() == 5 - end - - test "does not invalidate cache if period time did not pass" do - insert(:block, number: 5) - - BlockNumberCache.setup(cache_period: 10_000) - - assert BlockNumberCache.max_number() == 5 - - insert(:block, number: 10) - - assert BlockNumberCache.max_number() == 5 - end end describe "min_number/1" do @@ -48,32 +29,31 @@ defmodule Explorer.Chain.BlockNumberCacheTest do assert BlockNumberCache.max_number() == 2 end + end - test "invalidates cache" do - insert(:block, number: 5) - - BlockNumberCache.setup(cache_period: 2_000) + describe "update/1" do + test "updates max number" do + insert(:block, number: 2) - assert BlockNumberCache.min_number() == 5 + BlockNumberCache.setup() - insert(:block, number: 2) + assert BlockNumberCache.max_number() == 2 - Process.sleep(2_000) + assert BlockNumberCache.update(3) - assert BlockNumberCache.min_number() == 2 - assert BlockNumberCache.max_number() == 5 + assert BlockNumberCache.max_number() == 3 end - test "does not invalidate cache if period time did not pass" do - insert(:block, number: 5) + test "updates min number" do + insert(:block, number: 2) - BlockNumberCache.setup(cache_period: 10_000) + BlockNumberCache.setup() - assert BlockNumberCache.max_number() == 5 + assert BlockNumberCache.min_number() == 2 - insert(:block, number: 2) + assert BlockNumberCache.update(1) - assert BlockNumberCache.max_number() == 5 + assert BlockNumberCache.min_number() == 1 end end end diff --git a/apps/explorer/test/explorer/chain/import/runner/blocks_test.exs b/apps/explorer/test/explorer/chain/import/runner/blocks_test.exs index ecf1d71dfb..63110cba40 100644 --- a/apps/explorer/test/explorer/chain/import/runner/blocks_test.exs +++ b/apps/explorer/test/explorer/chain/import/runner/blocks_test.exs @@ -6,7 +6,7 @@ defmodule Explorer.Chain.Import.Runner.BlocksTest do import Explorer.Chain.Import.RunnerCase, only: [insert_address_with_token_balances: 1, update_holder_count!: 2] alias Ecto.Multi - alias Explorer.Chain.Import.Runner.{Blocks, Transaction} + alias Explorer.Chain.Import.Runner.{Blocks, Transactions} alias Explorer.Chain.{Address, Block, Transaction} alias Explorer.Chain alias Explorer.Repo @@ -283,6 +283,29 @@ defmodule Explorer.Chain.Import.Runner.BlocksTest do insert_block(new_block1, options) assert Chain.missing_block_number_ranges(range) == [] end + + # Regression test for https://github.com/poanetwork/blockscout/issues/1911 + test "forces block refetch if transaction is re-collated in a different block", + %{consensus_block: %Block{number: block_number, hash: block_hash, miner_hash: miner_hash}, options: options} do + new_block1 = params_for(:block, miner_hash: miner_hash, parent_hash: block_hash, number: block_number + 1) + new_block2 = params_for(:block, miner_hash: miner_hash, parent_hash: new_block1.hash, number: block_number + 2) + + range = block_number..(block_number + 2) + + insert_block(new_block1, options) + insert_block(new_block2, options) + assert Chain.missing_block_number_ranges(range) == [] + + trans_hash = transaction_hash() + + transaction1 = transaction_params_with_block([hash: trans_hash], new_block1) + insert_transaction(transaction1, options) + assert Chain.missing_block_number_ranges(range) == [] + + transaction2 = transaction_params_with_block([hash: trans_hash], new_block2) + insert_transaction(transaction2, options) + assert Chain.missing_block_number_ranges(range) == [(block_number + 1)..(block_number + 1)] + end end defp insert_block(block_params, options) do @@ -293,6 +316,28 @@ defmodule Explorer.Chain.Import.Runner.BlocksTest do |> Repo.transaction() end + defp transaction_params_with_block(transaction_params, block_params) do + params_for(:transaction, transaction_params) + |> Map.merge(%{ + block_hash: block_params.hash, + block_number: block_params.number, + cumulative_gas_used: 50_000, + error: nil, + gas_used: 50_000, + index: 0, + from_address_hash: insert(:address).hash + }) + end + + defp insert_transaction(transaction_params, options) do + %Ecto.Changeset{valid?: true, changes: transaction_changes} = + Transaction.changeset(%Transaction{}, transaction_params) + + Multi.new() + |> Transactions.run([transaction_changes], options) + |> Repo.transaction() + end + defp count(schema) do Repo.one!(select(schema, fragment("COUNT(*)"))) end diff --git a/apps/explorer/test/explorer/chain/import/runner/staking_pools_test.exs b/apps/explorer/test/explorer/chain/import/runner/staking_pools_test.exs index d5bc6ecfca..af25368679 100644 --- a/apps/explorer/test/explorer/chain/import/runner/staking_pools_test.exs +++ b/apps/explorer/test/explorer/chain/import/runner/staking_pools_test.exs @@ -1,85 +1,29 @@ defmodule Explorer.Chain.Import.Runner.StakingPoolsTest do use Explorer.DataCase + import Explorer.Factory + alias Ecto.Multi alias Explorer.Chain.Import.Runner.StakingPools describe "run/1" do test "insert new pools list" do - pools = [ - %{ - address_hash: %Explorer.Chain.Hash{ - byte_count: 20, - bytes: <<11, 47, 94, 47, 60, 189, 134, 78, 170, 44, 100, 46, 55, 105, 193, 88, 35, 97, 202, 246>> - }, - metadata: %{ - banned_unitil: 0, - delegators_count: 0, - is_active: true, - is_banned: false, - is_validator: true, - mining_address: %Explorer.Chain.Hash{ - byte_count: 20, - bytes: <<187, 202, 168, 212, 130, 137, 187, 31, 252, 249, 128, 141, 154, 164, 177, 210, 21, 5, 76, 120>> - }, - retries_count: 1, - staked_amount: 0, - was_banned_count: 0, - was_validator_count: 1 - }, - name: "anonymous", - primary: true - }, - %{ - address_hash: %Explorer.Chain.Hash{ - byte_count: 20, - bytes: <<170, 148, 182, 135, 211, 249, 85, 42, 69, 59, 129, 178, 131, 76, 165, 55, 120, 152, 13, 192>> - }, - metadata: %{ - banned_unitil: 0, - delegators_count: 0, - is_active: true, - is_banned: false, - is_validator: true, - mining_address: %Explorer.Chain.Hash{ - byte_count: 20, - bytes: <<117, 223, 66, 56, 58, 254, 107, 245, 25, 74, 168, 250, 14, 155, 61, 95, 158, 134, 148, 65>> - }, - retries_count: 1, - staked_amount: 0, - was_banned_count: 0, - was_validator_count: 1 - }, - name: "anonymous", - primary: true - }, - %{ - address_hash: %Explorer.Chain.Hash{ - byte_count: 20, - bytes: <<49, 44, 35, 14, 125, 109, 176, 82, 36, 246, 2, 8, 166, 86, 227, 84, 28, 92, 66, 186>> - }, - metadata: %{ - banned_unitil: 0, - delegators_count: 0, - is_active: true, - is_banned: false, - is_validator: true, - mining_address: %Explorer.Chain.Hash{ - byte_count: 20, - bytes: <<82, 45, 243, 150, 174, 112, 160, 88, 189, 105, 119, 132, 8, 99, 15, 219, 2, 51, 137, 178>> - }, - retries_count: 1, - staked_amount: 0, - was_banned_count: 0, - was_validator_count: 1 - }, - name: "anonymous", - primary: true - } - ] + pools = [pool1, pool2, pool3, pool4] = build_list(4, :staking_pool) assert {:ok, %{insert_staking_pools: list}} = run_changes(pools) assert Enum.count(list) == Enum.count(pools) + + saved_list = + Explorer.Chain.Address.Name + |> Repo.all() + |> Enum.reduce(%{}, fn pool, acc -> + Map.put(acc, pool.address_hash, pool) + end) + + assert saved_list[pool1.address_hash].metadata["staked_ratio"] == 0.25 + assert saved_list[pool2.address_hash].metadata["staked_ratio"] == 0.25 + assert saved_list[pool3.address_hash].metadata["staked_ratio"] == 0.25 + assert saved_list[pool4.address_hash].metadata["staked_ratio"] == 0.25 end end diff --git a/apps/explorer/test/explorer/chain/internal_transaction_test.exs b/apps/explorer/test/explorer/chain/internal_transaction_test.exs index 54ace519bd..fc3977a998 100644 --- a/apps/explorer/test/explorer/chain/internal_transaction_test.exs +++ b/apps/explorer/test/explorer/chain/internal_transaction_test.exs @@ -1,7 +1,7 @@ defmodule Explorer.Chain.InternalTransactionTest do use Explorer.DataCase - alias Explorer.Chain.{InternalTransaction, Wei} + alias Explorer.Chain.{Data, InternalTransaction, Wei} alias Explorer.Factory import EthereumJSONRPC, only: [integer_to_quantity: 1] @@ -173,7 +173,7 @@ defmodule Explorer.Chain.InternalTransactionTest do end test "it correctly formats a create" do - contract_code = Factory.contract_code_info().bytecode + {:ok, contract_code} = Data.cast(Factory.contract_code_info().bytecode) contract_address = Factory.address_hash() from = Factory.address_hash() gas = 50_000 diff --git a/apps/explorer/test/explorer/chain_test.exs b/apps/explorer/test/explorer/chain_test.exs index a9b479c3bd..cafd742e2a 100644 --- a/apps/explorer/test/explorer/chain_test.exs +++ b/apps/explorer/test/explorer/chain_test.exs @@ -50,6 +50,51 @@ defmodule Explorer.ChainTest do end end + describe "address_to_logs/2" do + test "fetches logs" do + address = insert(:address) + + transaction1 = + :transaction + |> insert(to_address: address) + |> with_block() + + insert(:log, transaction: transaction1, index: 1, address: address) + + transaction2 = + :transaction + |> insert(from_address: address) + |> with_block() + + insert(:log, transaction: transaction2, index: 2, address: address) + + assert Enum.count(Chain.address_to_logs(address)) == 2 + end + + test "paginates logs" do + address = insert(:address) + + transaction = + :transaction + |> insert(to_address: address) + |> with_block() + + log1 = insert(:log, transaction: transaction, index: 1, address: address) + + 2..51 + |> Enum.map(fn index -> insert(:log, transaction: transaction, index: index, address: address) end) + |> Enum.map(& &1.index) + + paging_options1 = %PagingOptions{page_size: 1} + + [_log] = Chain.address_to_logs(address, paging_options: paging_options1) + + paging_options2 = %PagingOptions{page_size: 60, key: {transaction.block_number, transaction.index, log1.index}} + + assert Enum.count(Chain.address_to_logs(address, paging_options: paging_options2)) == 50 + end + end + describe "address_to_transactions_with_rewards/2" do test "without transactions" do address = insert(:address) @@ -3903,4 +3948,59 @@ defmodule Explorer.ChainTest do refute Chain.contract_address?(to_string(hash), 1, json_rpc_named_arguments) end end + + describe "staking_pools/3" do + test "validators staking pools" do + inserted_validator = insert(:address_name, primary: true, metadata: %{is_active: true, is_validator: true}) + insert(:address_name, primary: true, metadata: %{is_active: true, is_validator: false}) + + options = %PagingOptions{page_size: 20, page_number: 1} + + assert [gotten_validator] = Chain.staking_pools(:validator, options) + assert inserted_validator.address_hash == gotten_validator.address_hash + end + + test "active staking pools" do + inserted_validator = insert(:address_name, primary: true, metadata: %{is_active: true}) + insert(:address_name, primary: true, metadata: %{is_active: false}) + + options = %PagingOptions{page_size: 20, page_number: 1} + + assert [gotten_validator] = Chain.staking_pools(:active, options) + assert inserted_validator.address_hash == gotten_validator.address_hash + end + + test "inactive staking pools" do + insert(:address_name, primary: true, metadata: %{is_active: true}) + inserted_validator = insert(:address_name, primary: true, metadata: %{is_active: false}) + + options = %PagingOptions{page_size: 20, page_number: 1} + + assert [gotten_validator] = Chain.staking_pools(:inactive, options) + assert inserted_validator.address_hash == gotten_validator.address_hash + end + end + + describe "staking_pools_count/1" do + test "validators staking pools" do + insert(:address_name, primary: true, metadata: %{is_active: true, is_validator: true}) + insert(:address_name, primary: true, metadata: %{is_active: true, is_validator: false}) + + assert Chain.staking_pools_count(:validator) == 1 + end + + test "active staking pools" do + insert(:address_name, primary: true, metadata: %{is_active: true}) + insert(:address_name, primary: true, metadata: %{is_active: false}) + + assert Chain.staking_pools_count(:active) == 1 + end + + test "inactive staking pools" do + insert(:address_name, primary: true, metadata: %{is_active: true}) + insert(:address_name, primary: true, metadata: %{is_active: false}) + + assert Chain.staking_pools_count(:inactive) == 1 + end + end end diff --git a/apps/explorer/test/explorer/staking/epoch_counter_test.exs b/apps/explorer/test/explorer/staking/epoch_counter_test.exs new file mode 100644 index 0000000000..278377ce05 --- /dev/null +++ b/apps/explorer/test/explorer/staking/epoch_counter_test.exs @@ -0,0 +1,97 @@ +defmodule Explorer.Staking.EpochCounterTest do + use ExUnit.Case, async: false + + import Mox + + alias Explorer.Staking.EpochCounter + alias Explorer.Chain.Events.Publisher + + setup :verify_on_exit! + setup :set_mox_global + + test "when disabled, it returns nil" do + assert EpochCounter.epoch_number() == nil + assert EpochCounter.epoch_end_block() == nil + end + + test "fetch epoch data" do + set_mox(10, 880) + Application.put_env(:explorer, EpochCounter, enabled: true) + start_supervised!(EpochCounter) + + Process.sleep(1_000) + + assert EpochCounter.epoch_number() == 10 + assert EpochCounter.epoch_end_block() == 880 + end + + test "fetch new epoch data" do + set_mox(10, 880) + Application.put_env(:explorer, EpochCounter, enabled: true) + start_supervised!(EpochCounter) + + Process.sleep(1_000) + + assert EpochCounter.epoch_number() == 10 + assert EpochCounter.epoch_end_block() == 880 + + event_type = :blocks + broadcast_type = :realtime + event_data = [%Explorer.Chain.Block{number: 881}] + + set_mox(11, 960) + Publisher.broadcast([{event_type, event_data}], broadcast_type) + + Process.sleep(1_000) + + assert EpochCounter.epoch_number() == 11 + assert EpochCounter.epoch_end_block() == 960 + end + + defp set_mox(epoch_num, end_block_num) do + expect( + EthereumJSONRPC.Mox, + :json_rpc, + fn [ + %{ + id: 0, + jsonrpc: "2.0", + method: "eth_call", + params: _ + }, + %{ + id: 1, + jsonrpc: "2.0", + method: "eth_call", + params: _ + } + ], + _options -> + {:ok, + [ + %{ + id: 0, + jsonrpc: "2.0", + result: encode_num(epoch_num) + }, + %{ + id: 1, + jsonrpc: "2.0", + result: encode_num(end_block_num) + } + ]} + end + ) + end + + defp encode_num(num) do + selector = %ABI.FunctionSelector{function: nil, types: [uint: 32]} + + encoded_num = + [num] + |> ABI.TypeEncoder.encode(selector) + |> Base.encode16(case: :lower) + + "0x" <> encoded_num + end +end diff --git a/apps/explorer/test/explorer/staking/pools_reader_test.exs b/apps/explorer/test/explorer/staking/pools_reader_test.exs index ac6a600722..bb3af9fbcc 100644 --- a/apps/explorer/test/explorer/staking/pools_reader_test.exs +++ b/apps/explorer/test/explorer/staking/pools_reader_test.exs @@ -1,6 +1,5 @@ defmodule Explorer.Token.PoolsReaderTest do use EthereumJSONRPC.Case - use Explorer.DataCase alias Explorer.Staking.PoolsReader @@ -44,6 +43,7 @@ defmodule Explorer.Token.PoolsReaderTest do mining_address: <<187, 202, 168, 212, 130, 137, 187, 31, 252, 249, 128, 141, 154, 164, 177, 210, 21, 5, 76, 120>>, staked_amount: 0, + self_staked_amount: 0, staking_address: <<11, 47, 94, 47, 60, 189, 134, 78, 170, 44, 100, 46, 55, 105, 193, 88, 35, 97, 202, 246>>, was_banned_count: 0, was_validator_count: 2 @@ -162,6 +162,25 @@ defmodule Explorer.Token.PoolsReaderTest do result: "0x0000000000000000000000000000000000000000000000000000000000000000" } + # stakeAmountMinusOrderedWithdraw + %{ + id: id, + jsonrpc: "2.0", + method: "eth_call", + params: [ + %{ + data: + "0x58daab6a0000000000000000000000000b2f5e2f3cbd864eaa2c642e3769c1582361caf60000000000000000000000000b2f5e2f3cbd864eaa2c642e3769c1582361caf6", + to: _ + }, + "latest" + ] + } -> + %{ + id: id, + result: "0x0000000000000000000000000000000000000000000000000000000000000000" + } + # isValidator %{ id: id, diff --git a/apps/explorer/test/support/data_case.ex b/apps/explorer/test/support/data_case.ex index 75d3429e70..68bb21434c 100644 --- a/apps/explorer/test/support/data_case.ex +++ b/apps/explorer/test/support/data_case.ex @@ -39,7 +39,7 @@ defmodule Explorer.DataCase do Ecto.Adapters.SQL.Sandbox.mode(Explorer.Repo, {:shared, self()}) end - Explorer.Chain.BlockNumberCache.setup(cache_period: 0) + Explorer.Chain.BlockNumberCache.setup() :ok end diff --git a/apps/explorer/test/support/factory.ex b/apps/explorer/test/support/factory.ex index 4c030a4db1..115b07e066 100644 --- a/apps/explorer/test/support/factory.ex +++ b/apps/explorer/test/support/factory.ex @@ -609,4 +609,24 @@ defmodule Explorer.Factory do user: build(:user) } end + + def staking_pool_factory do + %{ + address_hash: address_hash(), + metadata: %{ + banned_unitil: 0, + delegators_count: 0, + is_active: true, + is_banned: false, + is_validator: true, + mining_address: address_hash(), + retries_count: 1, + staked_amount: 25, + was_banned_count: 0, + was_validator_count: 1 + }, + name: "anonymous", + primary: true + } + end end diff --git a/apps/indexer/README.md b/apps/indexer/README.md index 34fb6e6ef3..173df0c6ab 100644 --- a/apps/indexer/README.md +++ b/apps/indexer/README.md @@ -92,6 +92,7 @@ After all deployed instances get all needed data, these fetchers should be depre - `uncataloged_token_transfers`: extracts token transfers from logs, which previously weren't parsed due to unknown format - `uncles_without_index`: adds previously unfetched `index` field for unfetched blocks in `block_second_degree_relations` +- `blocks_transactions_mismatch`: refetches each block once and revokes consensus to those whose transaction number mismatches with the number currently stored. This is meant to force the correction of a race condition that caused successfully fetched transactions to be overwritten by a following non-consensus block: [#1911](https://github.com/poanetwork/blockscout/issues/1911). ## Memory Usage diff --git a/apps/indexer/lib/indexer/block/fetcher.ex b/apps/indexer/lib/indexer/block/fetcher.ex index deb0e4554f..06a702aa4b 100644 --- a/apps/indexer/lib/indexer/block/fetcher.ex +++ b/apps/indexer/lib/indexer/block/fetcher.ex @@ -11,7 +11,7 @@ defmodule Indexer.Block.Fetcher do alias EthereumJSONRPC.{Blocks, FetchedBeneficiaries} alias Explorer.Chain - alias Explorer.Chain.{Address, Block, Hash, Import, Transaction} + alias Explorer.Chain.{Address, Block, BlockNumberCache, Hash, Import, Transaction} alias Indexer.Block.Fetcher.Receipts alias Indexer.Fetcher.{ @@ -171,13 +171,23 @@ defmodule Indexer.Block.Fetcher do transactions: %{params: transactions_with_receipts} } ) do - {:ok, %{inserted: inserted, errors: blocks_errors}} + result = {:ok, %{inserted: inserted, errors: blocks_errors}} + update_block_cache(inserted[:blocks]) + result else {step, {:error, reason}} -> {:error, {step, reason}} {:import, {:error, step, failed_value, changes_so_far}} -> {:error, {step, failed_value, changes_so_far}} end end + defp update_block_cache(blocks) do + max_block = Enum.max_by(blocks, fn block -> block.number end) + min_block = Enum.min_by(blocks, fn block -> block.number end) + + BlockNumberCache.update(max_block.number) + BlockNumberCache.update(min_block.number) + end + def import( %__MODULE__{broadcast: broadcast, callback_module: callback_module} = state, options diff --git a/apps/indexer/lib/indexer/fetcher/staking_pools.ex b/apps/indexer/lib/indexer/fetcher/staking_pools.ex index 68794d9ee4..fe4ab84c28 100644 --- a/apps/indexer/lib/indexer/fetcher/staking_pools.ex +++ b/apps/indexer/lib/indexer/fetcher/staking_pools.ex @@ -125,6 +125,7 @@ defmodule Indexer.Fetcher.StakingPools do pool |> Map.delete(:staking_address) |> Map.put(:mining_address, mining_address) + |> Map.put(:is_pool, true) %{ name: "anonymous", diff --git a/apps/indexer/lib/indexer/supervisor.ex b/apps/indexer/lib/indexer/supervisor.ex index 21dc3637ec..b2c3d19664 100644 --- a/apps/indexer/lib/indexer/supervisor.ex +++ b/apps/indexer/lib/indexer/supervisor.ex @@ -24,6 +24,7 @@ defmodule Indexer.Supervisor do } alias Indexer.Temporary.{ + BlocksTransactionsMismatch, UncatalogedTokenTransfers, UnclesWithoutIndex } @@ -124,6 +125,8 @@ defmodule Indexer.Supervisor do # Temporary workers {UncatalogedTokenTransfers.Supervisor, [[]]}, {UnclesWithoutIndex.Supervisor, + [[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]}, + {BlocksTransactionsMismatch.Supervisor, [[json_rpc_named_arguments: json_rpc_named_arguments, memory_monitor: memory_monitor]]} ], strategy: :one_for_one diff --git a/apps/indexer/lib/indexer/temporary/blocks_transactions_mismatch.ex b/apps/indexer/lib/indexer/temporary/blocks_transactions_mismatch.ex new file mode 100644 index 0000000000..76f4322379 --- /dev/null +++ b/apps/indexer/lib/indexer/temporary/blocks_transactions_mismatch.ex @@ -0,0 +1,115 @@ +defmodule Indexer.Temporary.BlocksTransactionsMismatch do + @moduledoc """ + Fetches `consensus` `t:Explorer.Chain.Block.t/0` and compares their transaction + number against a node, to revoke `consensus` on mismatch. + + This is meant to fix incorrectly strored transactions that happened as a result + of a race condition due to the asynchronicity of indexer's components. + """ + + use Indexer.Fetcher + + require Logger + + import Ecto.Query + + alias Ecto.Multi + alias EthereumJSONRPC.Blocks + alias Explorer.Chain.Block + alias Explorer.Repo + alias Indexer.BufferedTask + + @behaviour BufferedTask + + @defaults [ + flush_interval: :timer.seconds(3), + max_batch_size: 10, + max_concurrency: 4, + task_supervisor: Indexer.Temporary.BlocksTransactionsMismatch.TaskSupervisor, + metadata: [fetcher: :blocks_transactions_mismatch] + ] + + @doc false + def child_spec([init_options, gen_server_options]) when is_list(init_options) do + {state, mergeable_init_options} = Keyword.pop(init_options, :json_rpc_named_arguments) + + unless state do + raise ArgumentError, + ":json_rpc_named_arguments must be provided to `#{__MODULE__}.child_spec " <> + "to allow for json_rpc calls when running." + end + + merged_init_options = + @defaults + |> Keyword.merge(mergeable_init_options) + |> Keyword.put(:state, state) + + Supervisor.child_spec({BufferedTask, [{__MODULE__, merged_init_options}, gen_server_options]}, id: __MODULE__) + end + + @impl BufferedTask + def init(initial, reducer, _) do + query = + from(block in Block, + join: transactions in assoc(block, :transactions), + where: block.consensus and block.refetch_needed, + group_by: block.hash, + select: {block, count(transactions.hash)} + ) + + {:ok, final} = Repo.stream_reduce(query, initial, &reducer.(&1, &2)) + + final + end + + @impl BufferedTask + def run(blocks_data, json_rpc_named_arguments) do + hashes = Enum.map(blocks_data, fn {block, _trans_num} -> block.hash end) + + Logger.debug("fetching") + + case EthereumJSONRPC.fetch_blocks_by_hash(hashes, json_rpc_named_arguments) do + {:ok, blocks} -> + run_blocks(blocks, blocks_data) + + {:error, reason} -> + Logger.error(fn -> ["failed to fetch: ", inspect(reason)] end) + {:retry, blocks_data} + end + end + + defp run_blocks(%Blocks{blocks_params: []}, blocks_data), do: {:retry, blocks_data} + + defp run_blocks( + %Blocks{transactions_params: transactions_params}, + blocks_data + ) do + found_blocks_map = + transactions_params + |> Enum.group_by(&Map.fetch!(&1, :block_hash)) + |> Map.new(fn {block_hash, trans_lst} -> {block_hash, Enum.count(trans_lst)} end) + + {found_blocks_data, missing_blocks_data} = + Enum.split_with(blocks_data, fn {block, _trans_num} -> + Map.has_key?(found_blocks_map, to_string(block.hash)) + end) + + {:ok, _} = + found_blocks_data + |> Enum.reduce(Multi.new(), fn {block, trans_num}, multi -> + changes = %{ + refetch_needed: false, + consensus: found_blocks_map[to_string(block.hash)] == trans_num + } + + Multi.update(multi, block.hash, Block.changeset(block, changes)) + end) + |> Repo.transaction() + + if Enum.empty?(missing_blocks_data) do + :ok + else + {:retry, missing_blocks_data} + end + end +end diff --git a/apps/indexer/test/indexer/fetcher/staking_pools_test.exs b/apps/indexer/test/indexer/fetcher/staking_pools_test.exs index 8f985537bf..13e2c0d7ee 100644 --- a/apps/indexer/test/indexer/fetcher/staking_pools_test.exs +++ b/apps/indexer/test/indexer/fetcher/staking_pools_test.exs @@ -129,6 +129,25 @@ defmodule Indexer.Fetcher.StakingPoolsTest do result: "0x0000000000000000000000000000000000000000000000000000000000000000" } + # stakeAmountMinusOrderedWithdraw + %{ + id: id, + jsonrpc: "2.0", + method: "eth_call", + params: [ + %{ + data: + "0x58daab6a0000000000000000000000000b2f5e2f3cbd864eaa2c642e3769c1582361caf60000000000000000000000000b2f5e2f3cbd864eaa2c642e3769c1582361caf6", + to: _ + }, + "latest" + ] + } -> + %{ + id: id, + result: "0x0000000000000000000000000000000000000000000000000000000000000000" + } + # isValidator %{ id: id, From ad333aac7f421d450068c0c5e7943898086a7bb0 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Wed, 22 May 2019 12:53:25 +0300 Subject: [PATCH 44/73] load data async on addresses page --- .../controllers/address_controller.ex | 68 +++++++++---------- .../templates/address/index.html.eex | 21 +++--- .../lib/block_scout_web/views/address_view.ex | 2 - 3 files changed, 44 insertions(+), 47 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex index 3d43ed7054..88ff531f70 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex @@ -3,11 +3,13 @@ defmodule BlockScoutWeb.AddressController do import BlockScoutWeb.Chain, only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1] + alias BlockScoutWeb.AddressView alias Explorer.{Chain, Market} alias Explorer.Chain.Address alias Explorer.ExchangeRates.Token + alias Phoenix.View - def index(conn, params) do + def index(conn, %{"type" => "JSON"} = params) do addresses = params |> paging_options() @@ -15,41 +17,49 @@ defmodule BlockScoutWeb.AddressController do {addresses_page, next_page} = split_list_by_page(addresses) - cur_page_number = - cond do - !params["prev_page_number"] -> 1 - params["next_page"] -> String.to_integer(params["prev_page_number"]) + 1 - params["prev_page"] -> String.to_integer(params["prev_page_number"]) - 1 - end - next_page_path = case next_page_params(next_page, addresses_page, params) do nil -> nil next_page_params -> - next_params = - next_page_params - |> Map.put("prev_page_path", cur_page_path(conn, params)) - |> Map.put("next_page", true) - |> Map.put("prev_page_number", cur_page_number) - address_path( conn, :index, - next_params + next_page_params ) end + exchange_rate = Market.get_exchange_rate(Explorer.coin()) || Token.null() + total_supply = Chain.total_supply() + + json( + conn, + %{ + items: + addresses_page + |> Enum.with_index(1) + |> Enum.map(fn {{address, tx_count}, index} -> + View.render_to_string( + AddressView, + "_tile.html", + address: address, + index: index, + exchange_rate: exchange_rate, + total_supply: total_supply, + tx_count: tx_count, + validation_count: validation_count(address) + ) + end), + next_page_path: next_page_path + } + ) + end + + def index(conn, _params) do render(conn, "index.html", - address_tx_count_pairs: addresses_page, - page_address_count: Enum.count(addresses_page), - address_count: Chain.count_addresses_with_balance_from_cache(), - exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(), - total_supply: Chain.total_supply(), - next_page_path: next_page_path, - prev_page_path: params["prev_page_path"], - cur_page_number: cur_page_number + current_path: current_path(conn), + address_count: Chain.count_addresses_with_balance_from_cache() ) end @@ -64,16 +74,4 @@ defmodule BlockScoutWeb.AddressController do def validation_count(%Address{} = address) do Chain.address_to_validation_count(address) end - - defp cur_page_path(conn, %{"hash" => _hash, "fetched_coin_balance" => _balance} = params) do - new_params = Map.put(params, "next_page", false) - - address_path( - conn, - :index, - new_params - ) - end - - defp cur_page_path(conn, _), do: address_path(conn, :index) end diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/index.html.eex index 91697de0c9..dd57df4273 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/index.html.eex @@ -1,20 +1,21 @@
-
+

<%= gettext "Addresses" %>

- <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", prev_page_path: @prev_page_path, next_page_path: @next_page_path, cur_page_number: @cur_page_number, show_pagination_limit: true %> + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true %> -
- <%= for {{address, tx_count}, index} <- Enum.with_index(@address_tx_count_pairs, 1) do %> - <%= render "_tile.html", - address: address, index: index, exchange_rate: @exchange_rate, - total_supply: @total_supply, tx_count: tx_count, - validation_count: validation_count(address) %> - <% end %> +
+ + - <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", prev_page_path: @prev_page_path, next_page_path: @next_page_path, cur_page_number: @cur_page_number, show_pagination_limit: true %> + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true %>
diff --git a/apps/block_scout_web/lib/block_scout_web/views/address_view.ex b/apps/block_scout_web/lib/block_scout_web/views/address_view.ex index 7a6005d526..3102401144 100644 --- a/apps/block_scout_web/lib/block_scout_web/views/address_view.ex +++ b/apps/block_scout_web/lib/block_scout_web/views/address_view.ex @@ -3,8 +3,6 @@ defmodule BlockScoutWeb.AddressView do require Logger - import BlockScoutWeb.AddressController, only: [validation_count: 1] - alias BlockScoutWeb.LayoutView alias Explorer.Chain alias Explorer.Chain.{Address, Hash, InternalTransaction, SmartContract, Token, TokenTransfer, Transaction, Wei} From 124f5d8434fc3910bb81cf8c75b9f5912b2f5356 Mon Sep 17 00:00:00 2001 From: Michael Ira Krufky Date: Tue, 7 May 2019 14:45:07 -0400 Subject: [PATCH 45/73] add apps/block_scout_web/assets/static/images/ether1_logo.svg --- .../assets/static/images/ether1_logo.svg | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 apps/block_scout_web/assets/static/images/ether1_logo.svg diff --git a/apps/block_scout_web/assets/static/images/ether1_logo.svg b/apps/block_scout_web/assets/static/images/ether1_logo.svg new file mode 100644 index 0000000000..1ebd89e7df --- /dev/null +++ b/apps/block_scout_web/assets/static/images/ether1_logo.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 73277b41ebe4484409735f627d54a3c4309b0e37 Mon Sep 17 00:00:00 2001 From: Michael Ira Krufky Date: Tue, 7 May 2019 15:02:42 -0400 Subject: [PATCH 46/73] add apps/block_scout_web/assets/css/theme/_ether1_variables.scss --- .../assets/css/theme/_ether1_variables.scss | 49 +++++++++++++++++++ .../assets/css/theme/_variables.scss | 1 + 2 files changed, 50 insertions(+) create mode 100644 apps/block_scout_web/assets/css/theme/_ether1_variables.scss diff --git a/apps/block_scout_web/assets/css/theme/_ether1_variables.scss b/apps/block_scout_web/assets/css/theme/_ether1_variables.scss new file mode 100644 index 0000000000..323ceb22ac --- /dev/null +++ b/apps/block_scout_web/assets/css/theme/_ether1_variables.scss @@ -0,0 +1,49 @@ +// general +$primary: #840032; +$secondary: #343434; +$tertiary: #7f7f7f; +$additional-font: #ff95db; + +// footer +$footer-background-color: $primary; +$footer-title-color: #fff; +$footer-text-color: #fff; +$footer-item-disc-color: $secondary; + +// dashboard +$dashboard-line-color-price: $tertiary; // price left border + +$dashboard-banner-chart-legend-value-color: $additional-font; // chart labels + +$dashboard-stats-item-value-color: $additional-font; // stat values + +$dashboard-stats-item-border-color: $secondary; // stat border + +$dashboard-banner-gradient-start: $primary; // gradient begin + +$dashboard-banner-gradient-end: lighten($primary, 5); // gradient end + +$dashboard-banner-network-plain-container-background-color: #4b021e; // stats bg + + +// navigation +.navbar { box-shadow: 0px 0px 30px 0px rgba(75, 2, 30, 0.12); } // header shadow +$header-icon-border-color-hover: $tertiary; // top border on hover +$header-icon-color-hover: $tertiary; // nav icon on hover +.dropdown-item:hover, .dropdown-item:focus { background-color: $tertiary !important; } // dropdown item on hover + +// buttons +$btn-line-bg: #fff; // button bg +$btn-line-color: #4b021e; // button border and font color && hover bg color +$btn-copy-color: #4b021e; // btn copy +$btn-qr-color: #4b021e; // btn qr-code + +//links & tile +.tile a { color: $tertiary !important; } // links color for badges +.tile-type-block { + border-left: 4px solid #4b021e; +} // tab active bg + +// card +$card-background-1: $tertiary; +$card-tab-active: $tertiary; diff --git a/apps/block_scout_web/assets/css/theme/_variables.scss b/apps/block_scout_web/assets/css/theme/_variables.scss index 01bdc463d8..14517ac46f 100644 --- a/apps/block_scout_web/assets/css/theme/_variables.scss +++ b/apps/block_scout_web/assets/css/theme/_variables.scss @@ -3,6 +3,7 @@ // @import "dai_variables"; // @import "ethereum_classic_variables"; // @import "ethereum_variables"; +// @import "ether1_variables"; // @import "expanse_variables"; // @import "gochain_variables"; // @import "goerli_variables"; From b996e8b7eaeeddc458d7859c4e534f8aea59d463 Mon Sep 17 00:00:00 2001 From: Michael Ira Krufky Date: Wed, 22 May 2019 07:21:25 -0400 Subject: [PATCH 47/73] docker/Makefile: always set a container name --- docker/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docker/Makefile b/docker/Makefile index 1f49ff50b5..ac1c2677d6 100644 --- a/docker/Makefile +++ b/docker/Makefile @@ -1,6 +1,7 @@ SYSTEM = $(shell uname -s) HOST = host.docker.internal DOCKER_IMAGE = blockscout_prod +BS_CONTAINER_NAME = blockscout PG_CONTAINER_NAME = postgres PG_CONTAINER_IMAGE = postgres:10.4 THIS_FILE = $(lastword $(MAKEFILE_LIST)) @@ -87,7 +88,7 @@ endif start: build postgres @echo "==> Starting blockscout" - @docker run --rm \ + @docker run --rm --name $(BS_CONTAINER_NAME) \ $(BLOCKSCOUT_CONTAINNER_PARAMS) \ -p 4000:4000 \ $(DOCKER_IMAGE) /bin/sh -c "mix phx.server" From cb4354c6013b54ba8b4746b5e939cd5f3d38c6bf Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Wed, 22 May 2019 14:55:06 +0300 Subject: [PATCH 48/73] fix items --- .../controllers/address_controller.ex | 32 ++++++++++--------- .../templates/address/index.html.eex | 4 +-- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex index 88ff531f70..d8b1fbbf60 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex @@ -33,24 +33,26 @@ defmodule BlockScoutWeb.AddressController do exchange_rate = Market.get_exchange_rate(Explorer.coin()) || Token.null() total_supply = Chain.total_supply() + items = + addresses_page + |> Enum.with_index(1) + |> Enum.map(fn {{address, tx_count}, index} -> + View.render_to_string( + AddressView, + "_tile.html", + address: address, + index: index, + exchange_rate: exchange_rate, + total_supply: total_supply, + tx_count: tx_count, + validation_count: validation_count(address) + ) + end) + json( conn, %{ - items: - addresses_page - |> Enum.with_index(1) - |> Enum.map(fn {{address, tx_count}, index} -> - View.render_to_string( - AddressView, - "_tile.html", - address: address, - index: index, - exchange_rate: exchange_rate, - total_supply: total_supply, - tx_count: tx_count, - validation_count: validation_count(address) - ) - end), + items: items, next_page_path: next_page_path } ) diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/index.html.eex index dd57df4273..b81814ccbc 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/index.html.eex @@ -1,11 +1,11 @@
-
+

<%= gettext "Addresses" %>

<%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true %> -
+
-
- - - - - <%= gettext("Loading balances") %>... -
-
<%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> -
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/index.html.eex index ab3c9a7cea..3bc2e7fe9b 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_internal_transaction/index.html.eex @@ -65,24 +65,11 @@ <%= gettext "There are no internal transactions for this address." %> -
- - - - - <%= gettext("Loading") %>... -
+
<%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> - diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_token_transfer/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_token_transfer/index.html.eex index 00ebd0c5ca..c2ab502bf0 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_token_transfer/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_token_transfer/index.html.eex @@ -11,14 +11,6 @@ <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> -
- - - - - <%= gettext("Loading...") %> -
- @@ -33,13 +25,6 @@ <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> - diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex index d5379abd55..11b4de110c 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_transaction/index.html.eex @@ -63,25 +63,10 @@ -
- - - - - <%= gettext("Loading") %>... -
-
<%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> - diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address_validation/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address_validation/index.html.eex index d84af1a6d6..3f85f03aba 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address_validation/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address_validation/index.html.eex @@ -14,13 +14,6 @@ <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> -
- - - - - <%= gettext("Loading...") %> -
@@ -33,13 +26,6 @@ <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> -
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/block/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/block/index.html.eex index 542d1eb126..92b91ce287 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/block/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/block/index.html.eex @@ -11,13 +11,6 @@ <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> -
- - - - - <%= gettext("Loading") %>... -
<%= gettext "There are no blocks." %> @@ -25,14 +18,6 @@ <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> - -
diff --git a/apps/block_scout_web/lib/block_scout_web/templates/pending_transaction/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/pending_transaction/index.html.eex index 57855b2ab0..4c97be61e1 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/pending_transaction/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/pending_transaction/index.html.eex @@ -34,14 +34,6 @@ <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> - - diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/index.html.eex index fb9d5cb919..3b24912dd5 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/holder/index.html.eex @@ -27,24 +27,11 @@ -
- - - - - <%= gettext("Loading") %>... -
+
<%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> - diff --git a/apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/index.html.eex index f65e4209e8..980cc4b05a 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/tokens/transfer/index.html.eex @@ -26,25 +26,10 @@ -
- - - - - <%= gettext("Loading") %>... -
-
<%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> - diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/index.html.eex index 9049c3674c..18928915f2 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/index.html.eex @@ -28,24 +28,9 @@ -
- - - - - <%= gettext("Loading") %>... -
-
<%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %> - From 0ad849baab2d73da8e009f9c34f7de2d9eb8e5d0 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Thu, 23 May 2019 10:16:23 +0300 Subject: [PATCH 60/73] fix gettext --- apps/block_scout_web/priv/gettext/default.pot | 33 +++---------------- .../priv/gettext/en/LC_MESSAGES/default.po | 33 +++---------------- 2 files changed, 10 insertions(+), 56 deletions(-) diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot index 8d04e59dd8..32962f72e0 100644 --- a/apps/block_scout_web/priv/gettext/default.pot +++ b/apps/block_scout_web/priv/gettext/default.pot @@ -755,7 +755,7 @@ msgid "There are no logs for this transaction." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:23 +#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:15 msgid "There are no token transfers for this address." msgstr "" @@ -1089,9 +1089,6 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_read_contract/index.html.eex:14 -#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:19 -#: lib/block_scout_web/templates/address_validation/index.html.eex:22 -#: lib/block_scout_web/templates/address_validation/index.html.eex:41 #: lib/block_scout_web/templates/chain/show.html.eex:99 #: lib/block_scout_web/templates/chain/show.html.eex:125 #: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:21 @@ -1114,22 +1111,7 @@ msgid "GraphQL" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:60 -#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:73 -#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:84 -#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:41 -#: lib/block_scout_web/templates/address_transaction/index.html.eex:71 -#: lib/block_scout_web/templates/address_transaction/index.html.eex:83 -#: lib/block_scout_web/templates/block/index.html.eex:19 -#: lib/block_scout_web/templates/block/index.html.eex:33 #: lib/block_scout_web/templates/pending_transaction/index.html.eex:32 -#: lib/block_scout_web/templates/pending_transaction/index.html.eex:42 -#: lib/block_scout_web/templates/tokens/holder/index.html.eex:35 -#: lib/block_scout_web/templates/tokens/holder/index.html.eex:46 -#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:34 -#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:46 -#: lib/block_scout_web/templates/transaction/index.html.eex:36 -#: lib/block_scout_web/templates/transaction/index.html.eex:48 msgid "Loading" msgstr "" @@ -1300,9 +1282,9 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_coin_balance/index.html.eex:34 #: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:61 -#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:28 +#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:20 #: lib/block_scout_web/templates/address_transaction/index.html.eex:57 -#: lib/block_scout_web/templates/address_validation/index.html.eex:29 +#: lib/block_scout_web/templates/address_validation/index.html.eex:22 #: lib/block_scout_web/templates/chain/show.html.eex:91 #: lib/block_scout_web/templates/pending_transaction/index.html.eex:19 #: lib/block_scout_web/templates/tokens/holder/index.html.eex:21 @@ -1312,7 +1294,7 @@ msgid "Something went wrong, click to reload." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_validation/index.html.eex:25 +#: lib/block_scout_web/templates/address_validation/index.html.eex:18 msgid "There are no blocks validated by this address." msgstr "" @@ -1337,11 +1319,6 @@ msgstr "" msgid "Coin Balance History" msgstr "" -#, elixir-format -#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:48 -msgid "Loading balances" -msgstr "" - #, elixir-format #: lib/block_scout_web/templates/address_coin_balance/index.html.eex:22 #: lib/block_scout_web/templates/chain/show.html.eex:13 @@ -1365,7 +1342,7 @@ msgid "There are no pending transactions." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/block/index.html.eex:23 +#: lib/block_scout_web/templates/block/index.html.eex:16 msgid "There are no blocks." 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 da66541590..6e7023f8fc 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 @@ -755,7 +755,7 @@ msgid "There are no logs for this transaction." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:23 +#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:15 msgid "There are no token transfers for this address." msgstr "" @@ -1089,9 +1089,6 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_read_contract/index.html.eex:14 -#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:19 -#: lib/block_scout_web/templates/address_validation/index.html.eex:22 -#: lib/block_scout_web/templates/address_validation/index.html.eex:41 #: lib/block_scout_web/templates/chain/show.html.eex:99 #: lib/block_scout_web/templates/chain/show.html.eex:125 #: lib/block_scout_web/templates/tokens/read_contract/index.html.eex:21 @@ -1114,22 +1111,7 @@ msgid "GraphQL" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:60 -#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:73 -#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:84 -#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:41 -#: lib/block_scout_web/templates/address_transaction/index.html.eex:71 -#: lib/block_scout_web/templates/address_transaction/index.html.eex:83 -#: lib/block_scout_web/templates/block/index.html.eex:19 -#: lib/block_scout_web/templates/block/index.html.eex:33 #: lib/block_scout_web/templates/pending_transaction/index.html.eex:32 -#: lib/block_scout_web/templates/pending_transaction/index.html.eex:42 -#: lib/block_scout_web/templates/tokens/holder/index.html.eex:35 -#: lib/block_scout_web/templates/tokens/holder/index.html.eex:46 -#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:34 -#: lib/block_scout_web/templates/tokens/transfer/index.html.eex:46 -#: lib/block_scout_web/templates/transaction/index.html.eex:36 -#: lib/block_scout_web/templates/transaction/index.html.eex:48 msgid "Loading" msgstr "" @@ -1300,9 +1282,9 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_coin_balance/index.html.eex:34 #: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:61 -#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:28 +#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:20 #: lib/block_scout_web/templates/address_transaction/index.html.eex:57 -#: lib/block_scout_web/templates/address_validation/index.html.eex:29 +#: lib/block_scout_web/templates/address_validation/index.html.eex:22 #: lib/block_scout_web/templates/chain/show.html.eex:91 #: lib/block_scout_web/templates/pending_transaction/index.html.eex:19 #: lib/block_scout_web/templates/tokens/holder/index.html.eex:21 @@ -1312,7 +1294,7 @@ msgid "Something went wrong, click to reload." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/address_validation/index.html.eex:25 +#: lib/block_scout_web/templates/address_validation/index.html.eex:18 msgid "There are no blocks validated by this address." msgstr "" @@ -1337,11 +1319,6 @@ msgstr "" msgid "Coin Balance History" msgstr "" -#, elixir-format -#: lib/block_scout_web/templates/address_coin_balance/index.html.eex:48 -msgid "Loading balances" -msgstr "" - #, elixir-format #: lib/block_scout_web/templates/address_coin_balance/index.html.eex:22 #: lib/block_scout_web/templates/chain/show.html.eex:13 @@ -1365,7 +1342,7 @@ msgid "There are no pending transactions." msgstr "" #, elixir-format -#: lib/block_scout_web/templates/block/index.html.eex:23 +#: lib/block_scout_web/templates/block/index.html.eex:16 msgid "There are no blocks." msgstr "" From f7ec7206d8d17fc610598aa2009185320a7d53af Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Thu, 23 May 2019 10:29:06 +0300 Subject: [PATCH 61/73] fix gettext --- .../lib/block_scout_web/controllers/address_controller.ex | 1 - .../lib/block_scout_web/templates/address/index.html.eex | 8 -------- 2 files changed, 9 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex index 88c6e53288..d8b1fbbf60 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex @@ -2,7 +2,6 @@ defmodule BlockScoutWeb.AddressController do use BlockScoutWeb, :controller import BlockScoutWeb.Chain, only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1] - import BlockScoutWeb.PaginationHelpers alias BlockScoutWeb.AddressView alias Explorer.{Chain, Market} diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/index.html.eex index b81814ccbc..e3140a0efc 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/index.html.eex @@ -7,14 +7,6 @@
- - <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true %> From 1323a360c2e36e4740ac23d3e148ee2672ef2310 Mon Sep 17 00:00:00 2001 From: maxgrapps <50101080+maxgrapps@users.noreply.github.com> Date: Thu, 23 May 2019 11:17:14 +0300 Subject: [PATCH 62/73] Update default.pot --- apps/block_scout_web/priv/gettext/default.pot | 9 --------- 1 file changed, 9 deletions(-) diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot index f55bc54589..bcaa5614b5 100644 --- a/apps/block_scout_web/priv/gettext/default.pot +++ b/apps/block_scout_web/priv/gettext/default.pot @@ -1706,13 +1706,8 @@ msgid "of" msgstr "" #, elixir-format -<<<<<<< HEAD -<<<<<<< HEAD #: lib/block_scout_web/templates/block_transaction/404.html.eex:7 msgid "Block Details" -======= -======= ->>>>>>> 59d33a42ddf629ec042ae03fa17818f4c6eb8211 #: lib/block_scout_web/templates/address_contract/index.html.eex:83 msgid "Contract Byte Code" msgstr "" @@ -1740,8 +1735,4 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/address_logs/index.html.eex:77 msgid "There are no logs for this address." -<<<<<<< HEAD ->>>>>>> 59d33a42ddf629ec042ae03fa17818f4c6eb8211 -======= ->>>>>>> 59d33a42ddf629ec042ae03fa17818f4c6eb8211 msgstr "" From ef7ac3e01e282e6b898a443a5b9a93062ab36ed7 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Thu, 23 May 2019 11:43:36 +0300 Subject: [PATCH 63/73] add new function clause for xDai network --- .../lib/ethereum_jsonrpc/parity/fetched_beneficiaries.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/parity/fetched_beneficiaries.ex b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/parity/fetched_beneficiaries.ex index d56a85cd9f..98174be69a 100644 --- a/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/parity/fetched_beneficiaries.ex +++ b/apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/parity/fetched_beneficiaries.ex @@ -173,6 +173,7 @@ defmodule EthereumJSONRPC.Parity.FetchedBeneficiaries do defp get_address_type(reward_type, index) when reward_type == "external" and index == 1, do: :emission_funds defp get_address_type(reward_type, index) when reward_type == "external" and index == 2, do: :validator defp get_address_type(reward_type, index) when reward_type == "external" and index == 3, do: :validator + defp get_address_type(reward_type, index) when reward_type == "external" and index == 4, do: :validator defp get_address_type(reward_type, _index) when reward_type == "block", do: :validator defp get_address_type(reward_type, _index) when reward_type == "uncle", do: :uncle end From ab92f1f36112ba517005af17fa838443ae91f41a Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Thu, 23 May 2019 11:49:56 +0300 Subject: [PATCH 64/73] add CHANGELOG entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c9add09bb..bda41f10f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,7 @@ - [#1917](https://github.com/poanetwork/blockscout/pull/1917) - Force block refetch if transaction is re-collated in a different block - [#1992](https://github.com/poanetwork/blockscout/pull/1992) - fix: support https for wobserver polling - [#1966](https://github.com/poanetwork/blockscout/pull/1966) - fix: add fields for contract filter performance +- [#2008](https://github.com/poanetwork/blockscout/pull/2008) - add new function clause for xDai network beneficiaries ### Chore From e3971517eabd123948943ff095e2944c584ba603 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Thu, 23 May 2019 12:11:11 +0300 Subject: [PATCH 65/73] addresses page improvements --- .../lib/block_scout_web/controllers/address_controller.ex | 2 +- .../lib/block_scout_web/templates/address/index.html.eex | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex b/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex index d8b1fbbf60..6ce33a2773 100644 --- a/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex +++ b/apps/block_scout_web/lib/block_scout_web/controllers/address_controller.ex @@ -26,7 +26,7 @@ defmodule BlockScoutWeb.AddressController do address_path( conn, :index, - next_page_params + Map.delete(next_page_params, "type") ) end diff --git a/apps/block_scout_web/lib/block_scout_web/templates/address/index.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/address/index.html.eex index e3140a0efc..47d7f2457b 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/address/index.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/address/index.html.eex @@ -3,11 +3,11 @@

<%= gettext "Addresses" %>

- <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true %> + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "top", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %>
- <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true %> + <%= render BlockScoutWeb.CommonComponentsView, "_pagination_container.html", position: "bottom", cur_page_number: "1", show_pagination_limit: true, data_next_page_button: true, data_prev_page_button: true %>
From 236a56f1a9df23b9a48721b7f85966483a56513f Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Thu, 23 May 2019 12:33:08 +0300 Subject: [PATCH 66/73] hide navigation button if a page has no items --- .../assets/js/lib/async_listing_load.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/apps/block_scout_web/assets/js/lib/async_listing_load.js b/apps/block_scout_web/assets/js/lib/async_listing_load.js index 0d7f427fd3..dd1b8e4335 100644 --- a/apps/block_scout_web/assets/js/lib/async_listing_load.js +++ b/apps/block_scout_web/assets/js/lib/async_listing_load.js @@ -48,6 +48,8 @@ export const asyncInitialState = { loading: false, /* if there was an error fetching items */ requestError: false, + /* if response has no items */ + emptyResponse: false, /* if it is loading the first page */ loadingFirstPage: true, /* link to the next page */ @@ -90,6 +92,7 @@ export function asyncReducer (state = asyncInitialState, action) { return Object.assign({}, state, { requestError: false, + emptyResponse: action.items.length === 0, items: action.items, nextPagePath: action.nextPagePath, prevPagePath: prevPagePath @@ -169,6 +172,10 @@ export const elements = { }, '[data-async-listing] [data-next-page-button]': { render ($el, state) { + if (state.emptyResponse) { + return $el.hide() + } + if (state.requestError || !state.nextPagePath || state.loading) { return $el.attr('disabled', 'disabled') } @@ -179,6 +186,10 @@ export const elements = { }, '[data-async-listing] [data-prev-page-button]': { render ($el, state) { + if (state.emptyResponse) { + return $el.hide() + } + if (state.requestError || !state.prevPagePath || state.loading) { return $el.attr('disabled', 'disabled') } @@ -189,6 +200,10 @@ export const elements = { }, '[data-async-listing] [data-page-number]': { render ($el, state) { + if (state.emptyResponse) { + return $el.hide() + } + if (state.pagesStack.length === 0) { return $el.text('Page 1') } From 23645570b630b0e2ec0d4cd2c9547e105ef1e45f Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Thu, 23 May 2019 12:36:57 +0300 Subject: [PATCH 67/73] fix eslint --- apps/block_scout_web/assets/js/lib/async_listing_load.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/block_scout_web/assets/js/lib/async_listing_load.js b/apps/block_scout_web/assets/js/lib/async_listing_load.js index dd1b8e4335..e22f6e399d 100644 --- a/apps/block_scout_web/assets/js/lib/async_listing_load.js +++ b/apps/block_scout_web/assets/js/lib/async_listing_load.js @@ -173,7 +173,7 @@ export const elements = { '[data-async-listing] [data-next-page-button]': { render ($el, state) { if (state.emptyResponse) { - return $el.hide() + return $el.hide() } if (state.requestError || !state.nextPagePath || state.loading) { @@ -201,7 +201,7 @@ export const elements = { '[data-async-listing] [data-page-number]': { render ($el, state) { if (state.emptyResponse) { - return $el.hide() + return $el.hide() } if (state.pagesStack.length === 0) { From 5c2a22782aeafc665954051fa2683dafea4f59c6 Mon Sep 17 00:00:00 2001 From: Ayrat Badykov Date: Thu, 23 May 2019 12:38:44 +0300 Subject: [PATCH 68/73] add CHANGELOG entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c9add09bb..04e77bcb09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,7 @@ - [#1917](https://github.com/poanetwork/blockscout/pull/1917) - Force block refetch if transaction is re-collated in a different block - [#1992](https://github.com/poanetwork/blockscout/pull/1992) - fix: support https for wobserver polling - [#1966](https://github.com/poanetwork/blockscout/pull/1966) - fix: add fields for contract filter performance +- [#2009](https://github.com/poanetwork/blockscout/pull/2009) - addresses page improvements ### Chore From 65f937ebf22037d5b46254a0034adcd81f490971 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 23 May 2019 13:35:39 +0300 Subject: [PATCH 69/73] gettext fixes --- .../templates/transaction/not_found.html.eex | 12 +-- apps/block_scout_web/priv/gettext/default.pot | 33 +++++++ .../priv/gettext/en/LC_MESSAGES/default.po | 86 +++++++++---------- 3 files changed, 78 insertions(+), 53 deletions(-) diff --git a/apps/block_scout_web/lib/block_scout_web/templates/transaction/not_found.html.eex b/apps/block_scout_web/lib/block_scout_web/templates/transaction/not_found.html.eex index c51de04db8..c34d84824a 100644 --- a/apps/block_scout_web/lib/block_scout_web/templates/transaction/not_found.html.eex +++ b/apps/block_scout_web/lib/block_scout_web/templates/transaction/not_found.html.eex @@ -4,30 +4,30 @@ Block Not Found
\ No newline at end of file diff --git a/apps/block_scout_web/priv/gettext/default.pot b/apps/block_scout_web/priv/gettext/default.pot index bcaa5614b5..7680919044 100644 --- a/apps/block_scout_web/priv/gettext/default.pot +++ b/apps/block_scout_web/priv/gettext/default.pot @@ -1708,6 +1708,9 @@ msgstr "" #, elixir-format #: lib/block_scout_web/templates/block_transaction/404.html.eex:7 msgid "Block Details" +msgstr "" + +#, elixir-format #: lib/block_scout_web/templates/address_contract/index.html.eex:83 msgid "Contract Byte Code" msgstr "" @@ -1736,3 +1739,33 @@ msgstr "" #: lib/block_scout_web/templates/address_logs/index.html.eex:77 msgid "There are no logs for this address." msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/transaction/not_found.html.eex:7 +msgid "Sorry, We are unable to locate this transaction Hash" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/transaction/not_found.html.eex:12 +msgid "If you have just submitted this transaction please wait for at least 30 seconds before refreshing this page." +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/transaction/not_found.html.eex:30 +msgid "Back Home" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/transaction/not_found.html.eex:22 +msgid "During times when the network is busy (i.e during ICOs) it can take a while for your transaction to propagate through the network and for us to index it." +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/transaction/not_found.html.eex:26 +msgid "If it still does not show up after 1 hour, please check with your sender/exchange/wallet/transaction provider for additional information." +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/transaction/not_found.html.eex:16 +msgid "It could still be in the TX Pool of a different node, waiting to be broadcasted." +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 1dd2465781..512d9aad7e 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 @@ -307,13 +307,11 @@ msgid "Copy Address" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/not_found.html.eex:10 #: lib/block_scout_web/templates/transaction/overview.html.eex:16 msgid "Copy Transaction Hash" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/not_found.html.eex:14 #: lib/block_scout_web/templates/transaction/overview.html.eex:20 msgid "Copy Txn Hash" msgstr "" @@ -910,7 +908,6 @@ msgid "Transaction %{transaction}, %{subnetwork} %{transaction}" msgstr "" #, elixir-format -#: lib/block_scout_web/templates/transaction/not_found.html.eex:22 #: lib/block_scout_web/templates/transaction/overview.html.eex:11 msgid "Transaction Details" msgstr "" @@ -1164,11 +1161,6 @@ msgstr "" msgid "Raw Input" msgstr "" -#, elixir-format -#: lib/block_scout_web/templates/block_transaction/404.html.eex:7 -msgid "Block Details" -msgstr "" - #, elixir-format #: lib/block_scout_web/views/block_transaction_view.ex:15 msgid "Block not found, please try again later." @@ -1189,41 +1181,6 @@ msgstr "" msgid "Invalid Transaction Hash" msgstr "" -#, elixir-format -#: lib/block_scout_web/templates/transaction/not_found.html.eex:33 -msgid "Once we have the transaction's data this page will refresh automatically" -msgstr "" - -#, elixir-format -#: lib/block_scout_web/templates/transaction/not_found.html.eex:38 -msgid "Some transactions may take a while longer to be indexed depending on the load on the network" -msgstr "" - -#, elixir-format -#: lib/block_scout_web/templates/transaction/not_found.html.eex:34 -msgid "The possible reasons for this transaction not being processed include the following:" -msgstr "" - -#, elixir-format -#: lib/block_scout_web/templates/transaction/not_found.html.eex:29 -msgid "The transaction %{bold_hash} was not processed yet" -msgstr "" - -#, elixir-format -#: lib/block_scout_web/templates/transaction/not_found.html.eex:37 -msgid "The transaction may be in the pool of a node that didn't broadcast it yet" -msgstr "" - -#, elixir-format -#: lib/block_scout_web/templates/transaction/not_found.html.eex:39 -msgid "The transaction still does not exist" -msgstr "" - -#, elixir-format -#: lib/block_scout_web/templates/transaction/not_found.html.eex:36 -msgid "The transaction was made a few seconds ago" -msgstr "" - #, elixir-format #: lib/block_scout_web/templates/transaction/invalid.html.eex:8 msgid "is not a valid transaction hash" @@ -1748,12 +1705,17 @@ msgstr "" msgid "of" msgstr "" -#, elixir-format, fuzzy +#, elixir-format +#: lib/block_scout_web/templates/block_transaction/404.html.eex:7 +msgid "Block Details" +msgstr "" + +#, elixir-format #: lib/block_scout_web/templates/address_contract/index.html.eex:83 msgid "Contract Byte Code" msgstr "" -#, elixir-format, fuzzy +#, elixir-format #: lib/block_scout_web/templates/address_contract/index.html.eex:69 msgid "Contract Creation Code" msgstr "" @@ -1763,7 +1725,7 @@ msgstr "" msgid "Contracts that self destruct in their constructors have no contract code published and cannot be verified." msgstr "" -#, elixir-format, fuzzy +#, elixir-format #: lib/block_scout_web/templates/address_contract/index.html.eex:85 msgid "Copy Contract Byte Code" msgstr "" @@ -1773,7 +1735,37 @@ msgstr "" msgid "Displaying the init data provided of the creating transaction." msgstr "" -#, elixir-format, fuzzy +#, elixir-format #: lib/block_scout_web/templates/address_logs/index.html.eex:77 msgid "There are no logs for this address." msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/transaction/not_found.html.eex:7 +msgid "Sorry, We are unable to locate this transaction Hash" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/transaction/not_found.html.eex:12 +msgid "If you have just submitted this transaction please wait for at least 30 seconds before refreshing this page." +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/transaction/not_found.html.eex:30 +msgid "Back Home" +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/transaction/not_found.html.eex:22 +msgid "During times when the network is busy (i.e during ICOs) it can take a while for your transaction to propagate through the network and for us to index it." +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/transaction/not_found.html.eex:26 +msgid "If it still does not show up after 1 hour, please check with your sender/exchange/wallet/transaction provider for additional information." +msgstr "" + +#, elixir-format +#: lib/block_scout_web/templates/transaction/not_found.html.eex:16 +msgid "It could still be in the TX Pool of a different node, waiting to be broadcasted." +msgstr "" From 60f08244b77cf3f9e14596447a9aa719107a10c2 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 23 May 2019 14:01:43 +0300 Subject: [PATCH 70/73] tests update --- CHANGELOG.md | 4 ++-- .../controllers/api/rpc/address_controller_test.exs | 2 +- .../controllers/block_transaction_controller_test.exs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de38e9a542..b37063521e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,7 @@ ## Current -- [#2000](https://github.com/poanetwork/blockscout/pull/2000) - docker/Makefile: always set a container name ### Features -- [#2005](https://github.com/poanetwork/blockscout/pull/2005) - added "block not found" and "tx not found pages" +- [#2010](https://github.com/poanetwork/blockscout/pull/2010) - added "block not found" and "tx not found pages" - [#1963](https://github.com/poanetwork/blockscout/pull/1963) - added rinkeby theme and rinkeby logo - [#1959](https://github.com/poanetwork/blockscout/pull/1959) - added goerli theme and goerli logo - [#1928](https://github.com/poanetwork/blockscout/pull/1928) - pagination styles were updated @@ -69,6 +68,7 @@ - [#1958](https://github.com/poanetwork/blockscout/pull/1958) - Default value for release link env var - [#1975](https://github.com/poanetwork/blockscout/pull/1975) - add log index to transaction view - [#1988](https://github.com/poanetwork/blockscout/pull/1988) - Fix wrong parity tasks names in Circle CI +- [#2000](https://github.com/poanetwork/blockscout/pull/2000) - docker/Makefile: always set a container name ## 1.3.10-beta diff --git a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs index 993b25fcec..e6a0e7e702 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs @@ -5,7 +5,7 @@ defmodule BlockScoutWeb.API.RPC.AddressControllerTest do alias BlockScoutWeb.API.RPC.AddressController alias Explorer.Chain - alias Explorer.Chain.{BlockNumberCache, Events.Subscriber, Transaction, Wei} + alias Explorer.Chain.{Events.Subscriber, Transaction, Wei} alias Explorer.Counters.{AddressesWithBalanceCounter, AverageBlockTime} alias Indexer.Fetcher.CoinBalanceOnDemand alias Explorer.Repo diff --git a/apps/block_scout_web/test/block_scout_web/controllers/block_transaction_controller_test.exs b/apps/block_scout_web/test/block_scout_web/controllers/block_transaction_controller_test.exs index 6a4e2ebf32..b8f6233e49 100644 --- a/apps/block_scout_web/test/block_scout_web/controllers/block_transaction_controller_test.exs +++ b/apps/block_scout_web/test/block_scout_web/controllers/block_transaction_controller_test.exs @@ -165,7 +165,7 @@ defmodule BlockScoutWeb.BlockTransactionControllerTest do defp assert_block_above_tip(conn) do assert conn |> html_response(404) - |> Floki.find(~S|[data-selector="block-not-found-message"|) + |> Floki.find(~S|.error-descr|) |> Floki.text() |> String.trim() == "Easy Cowboy! This block does not exist yet!" end From 6fb255eece183f7c2373d27fa54b9713f4e55a24 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 23 May 2019 14:50:06 +0300 Subject: [PATCH 71/73] Explicitly add evm version to tests --- apps/explorer/test/explorer/smart_contract/verifier_test.exs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/explorer/test/explorer/smart_contract/verifier_test.exs b/apps/explorer/test/explorer/smart_contract/verifier_test.exs index ef635c2573..916453fafc 100644 --- a/apps/explorer/test/explorer/smart_contract/verifier_test.exs +++ b/apps/explorer/test/explorer/smart_contract/verifier_test.exs @@ -21,6 +21,7 @@ defmodule Explorer.SmartContract.VerifierTest do "contract_source_code" => contract_code_info.source_code, "compiler_version" => contract_code_info.version, "name" => contract_code_info.name, + "evm_version" => "petersburg", "optimization" => contract_code_info.optimized } @@ -68,6 +69,7 @@ defmodule Explorer.SmartContract.VerifierTest do "compiler_version" => contract_code_info.version, "name" => contract_code_info.name, "optimization" => contract_code_info.optimized, + "evm_version" => "petersburg", "constructor_arguments" => constructor_arguments } From e39da64647ad718ec19c9d50e59f8e434a1de8c4 Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Thu, 23 May 2019 15:46:41 +0300 Subject: [PATCH 72/73] temporarily remove evm versions tests --- .../solidity/code_compiler_test.exs | 42 +++++++++---------- .../explorer/smart_contract/verifier_test.exs | 2 - 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs b/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs index 0f5ac79cad..be68a03a0e 100644 --- a/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs +++ b/apps/explorer/test/explorer/smart_contract/solidity/code_compiler_test.exs @@ -310,33 +310,33 @@ defmodule Explorer.SmartContract.Solidity.CodeCompilerTest do end end - describe "allowed_evm_versions/0" do - test "returns allowed evm versions defined by ALLOWED_EVM_VERSIONS env var" do - Application.put_env(:explorer, :allowed_evm_versions, "CustomEVM1,CustomEVM2,CustomEVM3") - response = CodeCompiler.allowed_evm_versions() + # describe "allowed_evm_versions/0" do + # test "returns allowed evm versions defined by ALLOWED_EVM_VERSIONS env var" do + # Application.put_env(:explorer, :allowed_evm_versions, "CustomEVM1,CustomEVM2,CustomEVM3") + # response = CodeCompiler.allowed_evm_versions() - assert ["CustomEVM1", "CustomEVM2", "CustomEVM3"] = response - end + # assert ["CustomEVM1", "CustomEVM2", "CustomEVM3"] = response + # end - test "returns allowed evm versions defined by not trimmed ALLOWED_EVM_VERSIONS env var" do - Application.put_env(:explorer, :allowed_evm_versions, "CustomEVM1, CustomEVM2, CustomEVM3") - response = CodeCompiler.allowed_evm_versions() + # test "returns allowed evm versions defined by not trimmed ALLOWED_EVM_VERSIONS env var" do + # Application.put_env(:explorer, :allowed_evm_versions, "CustomEVM1, CustomEVM2, CustomEVM3") + # response = CodeCompiler.allowed_evm_versions() - assert ["CustomEVM1", "CustomEVM2", "CustomEVM3"] = response - end + # assert ["CustomEVM1", "CustomEVM2", "CustomEVM3"] = response + # end - test "returns default_allowed_evm_versions" do - Application.put_env( - :explorer, - :allowed_evm_versions, - "homestead,tangerineWhistle,spuriousDragon,byzantium,constantinople,petersburg" - ) + # test "returns default_allowed_evm_versions" do + # Application.put_env( + # :explorer, + # :allowed_evm_versions, + # "homestead,tangerineWhistle,spuriousDragon,byzantium,constantinople,petersburg" + # ) - response = CodeCompiler.allowed_evm_versions() + # response = CodeCompiler.allowed_evm_versions() - assert ["homestead", "tangerineWhistle", "spuriousDragon", "byzantium", "constantinople", "petersburg"] = response - end - end + # assert ["homestead", "tangerineWhistle", "spuriousDragon", "byzantium", "constantinople", "petersburg"] = response + # end + # end defp remove_init_data_and_whisper_data(code) do {res, _} = diff --git a/apps/explorer/test/explorer/smart_contract/verifier_test.exs b/apps/explorer/test/explorer/smart_contract/verifier_test.exs index 916453fafc..ef635c2573 100644 --- a/apps/explorer/test/explorer/smart_contract/verifier_test.exs +++ b/apps/explorer/test/explorer/smart_contract/verifier_test.exs @@ -21,7 +21,6 @@ defmodule Explorer.SmartContract.VerifierTest do "contract_source_code" => contract_code_info.source_code, "compiler_version" => contract_code_info.version, "name" => contract_code_info.name, - "evm_version" => "petersburg", "optimization" => contract_code_info.optimized } @@ -69,7 +68,6 @@ defmodule Explorer.SmartContract.VerifierTest do "compiler_version" => contract_code_info.version, "name" => contract_code_info.name, "optimization" => contract_code_info.optimized, - "evm_version" => "petersburg", "constructor_arguments" => constructor_arguments } From 1567d9cc48af0549e7bbfcbd55096184a9d36e3c Mon Sep 17 00:00:00 2001 From: Victor Baranov Date: Fri, 24 May 2019 10:17:23 +0300 Subject: [PATCH 73/73] Update CHANGELOG.md Add 1.3.11 - 1.3.14 releases --- CHANGELOG.md | 85 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae7741dcea..5daf1b344d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,81 +1,108 @@ ## Current -- [#2000](https://github.com/poanetwork/blockscout/pull/2000) - docker/Makefile: always set a container name ### Features -- [#1963](https://github.com/poanetwork/blockscout/pull/1963) - added rinkeby theme and rinkeby logo -- [#1959](https://github.com/poanetwork/blockscout/pull/1959) - added goerli theme and goerli logo +- [#1963](https://github.com/poanetwork/blockscout/pull/1963), [#1959](https://github.com/poanetwork/blockscout/pull/1959), [#1948](https://github.com/poanetwork/blockscout/pull/1948), [#1936](https://github.com/poanetwork/blockscout/pull/1936), [#1925](https://github.com/poanetwork/blockscout/pull/1925), [#1922](https://github.com/poanetwork/blockscout/pull/1922), [#1903](https://github.com/poanetwork/blockscout/pull/1903), [#1874](https://github.com/poanetwork/blockscout/pull/1874), [#1895](https://github.com/poanetwork/blockscout/pull/1895) - added new themes and logos for poa, eth, rinkeby, goerli, ropsten, kovan, sokol, xdai, etc, rsk - [#1928](https://github.com/poanetwork/blockscout/pull/1928) - pagination styles were updated -- [#1948](https://github.com/poanetwork/blockscout/pull/1948) - added ropsten theme and ropsten logo - [#1940](https://github.com/poanetwork/blockscout/pull/1940) - qr modal button and background issue -- [#1936](https://github.com/poanetwork/blockscout/pull/1936) - added kovan, sokol themes and logos -- [#1925](https://github.com/poanetwork/blockscout/pull/1925) - added dai theme and logo -- [#1922](https://github.com/poanetwork/blockscout/pull/1922) - added ethereum classic theme and logo - [#1907](https://github.com/poanetwork/blockscout/pull/1907) - dropdown color bug fix (lukso theme) and tooltip color bug fix -- [#1903](https://github.com/poanetwork/blockscout/pull/1903) - added rsk theme and rsk logo -- [#1895](https://github.com/poanetwork/blockscout/pull/1895) - add changes to poa theme and poa logo -- [#1812](https://github.com/poanetwork/blockscout/pull/1812) - add pagination to addresses page -- [#1874](https://github.com/poanetwork/blockscout/pull/1874) - add changes to ethereum theme and ethereum logo -- [#1815](https://github.com/poanetwork/blockscout/pull/1815) - able to search without prefix "0x" -- [#1813](https://github.com/poanetwork/blockscout/pull/1813) - add total blocks counter to the main page -- [#1806](https://github.com/poanetwork/blockscout/pull/1806) - verify contracts with a post request - [#1857](https://github.com/poanetwork/blockscout/pull/1857) - Re-implement Geth JS internal transaction tracer in Elixir - [#1859](https://github.com/poanetwork/blockscout/pull/1859) - feat: show raw transaction traces -- [#1920](https://github.com/poanetwork/blockscout/pull/1920) - fix: remove source code fields from list endpoint -- [#1876](https://github.com/poanetwork/blockscout/pull/1876) - async calculate a count of blocks - [#1941](https://github.com/poanetwork/blockscout/pull/1941) - feat: add on demand fetching and stale attr to rpc - [#1957](https://github.com/poanetwork/blockscout/pull/1957) - Calculate stakes ratio before insert pools - [#1956](https://github.com/poanetwork/blockscout/pull/1956) - add logs tab to address -- [#1933](https://github.com/poanetwork/blockscout/pull/1933) - add eth_BlockNumber json rpc method - [#1952](https://github.com/poanetwork/blockscout/pull/1952) - feat: exclude empty contracts by default - [#1989](https://github.com/poanetwork/blockscout/pull/1989) - fix: consolidate address w/ balance one at a time - [#1954](https://github.com/poanetwork/blockscout/pull/1954) - feat: use creation init on self destruct - [#1974](https://github.com/poanetwork/blockscout/pull/1974) - feat: previous page button logic - [#1999](https://github.com/poanetwork/blockscout/pull/1999) - load data async on addresses page - [#2002](https://github.com/poanetwork/blockscout/pull/2002) - Get estimated count of blocks when cache is empty +- [#1807](https://github.com/poanetwork/blockscout/pull/1807) - New theming capabilites. ### Fixes - [#1944](https://github.com/poanetwork/blockscout/pull/1944) - fixed styles for token's dropdown. - [#1926](https://github.com/poanetwork/blockscout/pull/1926) - status label alignment -- [#1829](https://github.com/poanetwork/blockscout/pull/1829) - Handle nil quantities in block decoding routine -- [#1830](https://github.com/poanetwork/blockscout/pull/1830) - Make block size field nullable -- [#1840](https://github.com/poanetwork/blockscout/pull/1840) - Handle case when total supply is nil -- [#1838](https://github.com/poanetwork/blockscout/pull/1838) - Block counter calculates only consensus blocks - [#1849](https://github.com/poanetwork/blockscout/pull/1849) - Improve chains menu - [#1869](https://github.com/poanetwork/blockscout/pull/1869) - Fix output and gas extraction in JS tracer for Geth - [#1868](https://github.com/poanetwork/blockscout/pull/1868) - fix: logs list endpoint performance - [#1822](https://github.com/poanetwork/blockscout/pull/1822) - Fix style breaks in decompiled contract code view - [#1885](https://github.com/poanetwork/blockscout/pull/1885) - highlight reserved words in decompiled code - [#1896](https://github.com/poanetwork/blockscout/pull/1896) - re-query tokens in top nav automplete -- [#1881](https://github.com/poanetwork/blockscout/pull/1881) - fix: store solc versions locally for performance - [#1905](https://github.com/poanetwork/blockscout/pull/1905) - fix reorgs, uncles pagination -- [#1875](https://github.com/poanetwork/blockscout/pull/1875) - fix: resolve false positive constructor arguments - [#1904](https://github.com/poanetwork/blockscout/pull/1904) - fix `BLOCK_COUNT_CACHE_TTL` env var type -- [#1898](https://github.com/poanetwork/blockscout/pull/1898) - check if the constructor has arguments before verifying constructor arguments - [#1915](https://github.com/poanetwork/blockscout/pull/1915) - fallback to 2 latest evm versions - [#1937](https://github.com/poanetwork/blockscout/pull/1937) - Check the presence of overlap[i] object before retrieving properties from it - [#1960](https://github.com/poanetwork/blockscout/pull/1960) - do not remove bold text in decompiled contacts -- [#1917](https://github.com/poanetwork/blockscout/pull/1917) - Force block refetch if transaction is re-collated in a different block - [#1992](https://github.com/poanetwork/blockscout/pull/1992) - fix: support https for wobserver polling - [#1966](https://github.com/poanetwork/blockscout/pull/1966) - fix: add fields for contract filter performance - [#2009](https://github.com/poanetwork/blockscout/pull/2009) - addresses page improvements ### Chore -- [#1814](https://github.com/poanetwork/blockscout/pull/1814) - Clear build artefacts script -- [#1837](https://github.com/poanetwork/blockscout/pull/1837) - Add -f flag to clear_build.sh script delete static folder - [#1900](https://github.com/poanetwork/blockscout/pull/1900) - SUPPORTED_CHAINS ENV var -- [#1892](https://github.com/poanetwork/blockscout/pull/1892) - Remove temporary worker modules - [#1958](https://github.com/poanetwork/blockscout/pull/1958) - Default value for release link env var - [#1964](https://github.com/poanetwork/blockscout/pull/1964) - ALLOWED_EVM_VERSIONS env var - [#1975](https://github.com/poanetwork/blockscout/pull/1975) - add log index to transaction view - [#1988](https://github.com/poanetwork/blockscout/pull/1988) - Fix wrong parity tasks names in Circle CI +- [#2000](https://github.com/poanetwork/blockscout/pull/2000) - docker/Makefile: always set a container name + +## 1.3.14-beta + +### Features + +- [#1812](https://github.com/poanetwork/blockscout/pull/1812) - add pagination to addresses page +- [#1920](https://github.com/poanetwork/blockscout/pull/1920) - fix: remove source code fields from list endpoint +- [#1876](https://github.com/poanetwork/blockscout/pull/1876) - async calculate a count of blocks + +### Fixes + +- [#1917](https://github.com/poanetwork/blockscout/pull/1917) - Force block refetch if transaction is re-collated in a different block + +### Chore + +- [#1892](https://github.com/poanetwork/blockscout/pull/1892) - Remove temporary worker modules + +## 1.3.13-beta + +### Features + +- [#1933](https://github.com/poanetwork/blockscout/pull/1933) - add eth_BlockNumber json rpc method + +### Fixes + +- [#1875](https://github.com/poanetwork/blockscout/pull/1875) - fix: resolve false positive constructor arguments +- [#1881](https://github.com/poanetwork/blockscout/pull/1881) - fix: store solc versions locally for performance +- [#1898](https://github.com/poanetwork/blockscout/pull/1898) - check if the constructor has arguments before verifying constructor arguments + +## 1.3.12-beta + +Reverting of synchronous block counter, implemented in #1848 + +## 1.3.11-beta + +### Features + +- [#1815](https://github.com/poanetwork/blockscout/pull/1815) - Be able to search without prefix "0x" +- [#1813](https://github.com/poanetwork/blockscout/pull/1813) - Add total blocks counter to the main page +- [#1806](https://github.com/poanetwork/blockscout/pull/1806) - Verify contracts with a post request +- [#1848](https://github.com/poanetwork/blockscout/pull/1848) - Add cache for block counter + +### Fixes + +- [#1829](https://github.com/poanetwork/blockscout/pull/1829) - Handle nil quantities in block decoding routine +- [#1830](https://github.com/poanetwork/blockscout/pull/1830) - Make block size field nullable +- [#1840](https://github.com/poanetwork/blockscout/pull/1840) - Handle case when total supply is nil +- [#1838](https://github.com/poanetwork/blockscout/pull/1838) - Block counter calculates only consensus blocks + +### Chore + +- [#1814](https://github.com/poanetwork/blockscout/pull/1814) - Clear build artefacts script +- [#1837](https://github.com/poanetwork/blockscout/pull/1837) - Add -f flag to clear_build.sh script delete static folder ## 1.3.10-beta ### Features -- [#1807](https://github.com/poanetwork/blockscout/pull/1807) - New theming capabilites. - [#1739](https://github.com/poanetwork/blockscout/pull/1739) - highlight decompiled source code - [#1696](https://github.com/poanetwork/blockscout/pull/1696) - full-text search by tokens - [#1742](https://github.com/poanetwork/blockscout/pull/1742) - Support RSK