Merge branch 'master' into ag-README-update

pull/1967/head
Victor Baranov 6 years ago committed by GitHub
commit 876852ca9c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .dialyzer-ignore
  2. 4
      CHANGELOG.md
  3. 2
      README.md
  4. 53
      apps/block_scout_web/assets/css/theme/_rinkeby_variables.scss
  5. 37
      apps/block_scout_web/assets/static/images/rinkeby_logo.svg
  6. 8
      apps/block_scout_web/lib/block_scout_web/chain.ex
  7. 46
      apps/block_scout_web/lib/block_scout_web/controllers/address_logs_controller.ex
  8. 8
      apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/block_controller.ex
  9. 52
      apps/block_scout_web/lib/block_scout_web/etherscan.ex
  10. 7
      apps/block_scout_web/lib/block_scout_web/router.ex
  11. 7
      apps/block_scout_web/lib/block_scout_web/templates/address/_tabs.html.eex
  12. 82
      apps/block_scout_web/lib/block_scout_web/templates/address_logs/index.html.eex
  13. 3
      apps/block_scout_web/lib/block_scout_web/views/address_logs_view.ex
  14. 1
      apps/block_scout_web/lib/block_scout_web/views/address_view.ex
  15. 28
      apps/block_scout_web/lib/block_scout_web/views/api/rpc/block_view.ex
  16. 33
      apps/block_scout_web/lib/block_scout_web/views/api/rpc/eth_rpc_view.ex
  17. 18
      apps/block_scout_web/lib/block_scout_web/views/layout_view.ex
  18. 19
      apps/block_scout_web/priv/gettext/default.pot
  19. 23
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  20. 1
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/address_controller_test.exs
  21. 28
      apps/block_scout_web/test/block_scout_web/views/layout_view_test.exs
  22. 2
      apps/explorer/config/config.exs
  23. 2
      apps/explorer/config/test.exs
  24. 32
      apps/explorer/lib/explorer/chain.ex
  25. 57
      apps/explorer/lib/explorer/chain/block_number_cache.ex
  26. 64
      apps/explorer/test/explorer/chain/block_number_cache_test.exs
  27. 45
      apps/explorer/test/explorer/chain_test.exs
  28. 2
      apps/explorer/test/support/data_case.ex
  29. 14
      apps/indexer/lib/indexer/block/fetcher.ex

@ -4,4 +4,4 @@
apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex:400: Function timestamp_to_datetime/1 has no local return
apps/explorer/lib/explorer/repo/prometheus_logger.ex:8: Function microseconds_time/1 has no local return
apps/explorer/lib/explorer/repo/prometheus_logger.ex:8: The call 'Elixir.System':convert_time_unit(__@1::any(),'native','microseconds') breaks the contract (integer(),time_unit() | 'native',time_unit() | 'native') -> integer()
apps/block_scout_web/lib/block_scout_web/views/layout_view.ex:162: The call 'Elixir.Poison.Parser':'parse!'(any(),#{'keys':='atoms!'}) will never return since the success typing is (binary() | maybe_improper_list(binary() | maybe_improper_list(any(),binary() | []) | byte(),binary() | []),[{atom(),_}]) -> 'false' | 'nil' | 'true' | binary() | ['false' | 'nil' | 'true' | binary() | [any()] | number() | map()] | number() | map() and the contract is (iodata(),'Elixir.Keyword':t()) -> t()
apps/block_scout_web/lib/block_scout_web/views/layout_view.ex:174: The call 'Elixir.Poison.Parser':'parse!'(any(),#{'keys':='atoms!'}) will never return since the success typing is (binary() | maybe_improper_list(binary() | maybe_improper_list(any(),binary() | []) | byte(),binary() | []),[{atom(),_}]) -> 'false' | 'nil' | 'true' | binary() | ['false' | 'nil' | 'true' | binary() | [any()] | number() | map()] | number() | map() and the contract is (iodata(),'Elixir.Keyword':t()) -> t()

@ -1,6 +1,7 @@
## Current
### 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
- [#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
@ -21,6 +22,8 @@
- [#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
- [#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
### Fixes
@ -53,6 +56,7 @@
- [#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
## 1.3.10-beta

@ -49,7 +49,7 @@ Currently available block explorers (i.e. Etherscan and Etherchain) are closed s
| [Callisto](https://blockscout.com/callisto/mainnet) | [Kovan Testnet](https://blockscout.com/eth/kovan) | [Ether-1](https://blocks.ether1.wattpool.net/) |
| [Ethereum Classic](https://blockscout.com/etc/mainnet) | [POA Sokol Testnet](https://blockscout.com/poa/sokol) | [Fuse Network](https://explorer.fuse.io/) |
| [Ethereum Mainnet](https://blockscout.com/eth/mainnet) | [Rinkeby Testnet](https://blockscout.com/eth/rinkeby) | [Oasis Labs](https://blockexplorer.oasiscloud.io/) |
| [POA Core Network](https://blockscout.com/poa/core) | [Ropsten Testnet](https://blockscout.com/eth/ropsten) | [Petrichor](https://explorer.petrichor-dev.com/) |
| [POA Core Network](https://blockscout.com/poa/core) | [Ropsten Testnet](https://blockscout.com/eth/ropsten) | [Petrichor](https://explorer.petrachor.com/) |
| [RSK](https://blockscout.com/rsk/mainnet) | | [PIRL](http://pirl.es/) |
| [xDai Chain](https://blockscout.com/poa/dai) | | [SafeChain](https://explorer.safechain.io) |
| | | [SpringChain](https://explorer.springrole.com/) |

@ -1,3 +1,50 @@
$primary: #2a3f54;
$secondary: #39c4a9;
$tertiary: #39c4a9;
// general
$primary: #193653;
$secondary: #49a2ee;
$tertiary: #41c3a9;
$additional-font: #a3ceff;
// footer
$footer-background-color: $primary;
$footer-title-color: #fff;
$footer-text-color: $additional-font;
$footer-item-disc-color: $additional-font;
.footer-logo { filter: brightness(0) invert(1); }
// 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: $tertiary; // 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: #244468; // stats bg
// navigation
.navbar { box-shadow: 0px 0px 30px 0px rgba(21, 53, 80, 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: $tertiary; // button border and font color && hover bg color
$btn-copy-color: $tertiary; // btn copy
$btn-qr-color: $tertiary; // btn qr-code
//links & tile
.tile a { color: $tertiary !important; } // links color for badges
.tile-type-block {
border-left: 4px solid $tertiary;
} // tab active bg
// card
$card-background-1: $secondary;
$card-tab-active: $secondary;

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 12 KiB

@ -205,8 +205,12 @@ defmodule BlockScoutWeb.Chain do
%{"block_number" => block_number, "transaction_index" => transaction_index, "index" => index}
end
defp paging_params(%Log{index: index}) do
%{"index" => index}
defp paging_params(%Log{index: index} = log) do
if Ecto.assoc_loaded?(log.transaction) do
%{"block_number" => log.transaction.block_number, "transaction_index" => log.transaction.index, "index" => index}
else
%{"index" => index}
end
end
defp paging_params(%Transaction{block_number: nil, inserted_at: inserted_at, hash: hash}) do

@ -0,0 +1,46 @@
defmodule BlockScoutWeb.AddressLogsController do
@moduledoc """
Manages events logs tab.
"""
import BlockScoutWeb.AddressController, only: [transaction_count: 1, validation_count: 1]
import BlockScoutWeb.Chain, only: [paging_options: 1, next_page_params: 3, split_list_by_page: 1]
alias Explorer.{Chain, Market}
alias Explorer.ExchangeRates.Token
alias Indexer.Fetcher.CoinBalanceOnDemand
use BlockScoutWeb, :controller
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
logs_plus_one = Chain.address_to_logs(address, paging_options(params))
{results, next_page} = split_list_by_page(logs_plus_one)
next_page_url =
case next_page_params(next_page, results, params) do
nil ->
nil
next_page_params ->
address_logs_path(conn, :index, address, next_page_params)
end
render(
conn,
"index.html",
address: address,
logs: results,
coin_balance_status: CoinBalanceOnDemand.trigger_fetch(address),
exchange_rate: Market.get_exchange_rate(Explorer.coin()) || Token.null(),
transaction_count: transaction_count(address),
validation_count: validation_count(address),
next_page_url: next_page_url
)
else
_ ->
not_found(conn)
end
end
end

@ -3,6 +3,7 @@ defmodule BlockScoutWeb.API.RPC.BlockController do
alias BlockScoutWeb.Chain, as: ChainWeb
alias Explorer.Chain
alias Explorer.Chain.BlockNumberCache
def getblockreward(conn, params) do
with {:block_param, {:ok, unsafe_block_number}} <- {:block_param, Map.fetch(params, "blockno")},
@ -23,4 +24,11 @@ defmodule BlockScoutWeb.API.RPC.BlockController do
render(conn, :error, error: "Block does not exist")
end
end
def eth_block_number(conn, params) do
id = Map.get(params, "id", 1)
max_block_number = BlockNumberCache.max_number()
render(conn, :eth_block_number, number: max_block_number, id: id)
end
end

@ -279,6 +279,12 @@ defmodule BlockScoutWeb.Etherscan do
"result" => nil
}
@block_eth_block_number_example_value %{
"jsonrpc" => "2.0",
"result" => "767969",
"id" => 1
}
@contract_listcontracts_example_value %{
"status" => "1",
"message" => "OK",
@ -476,11 +482,26 @@ defmodule BlockScoutWeb.Etherscan do
enum_interpretation: %{"0" => "error", "1" => "ok"}
}
@jsonrpc_version_type %{
type: "string",
example: ~s("2.0")
}
@message_type %{
type: "string",
example: ~s("OK")
}
@hex_number_type %{
type: "string",
example: ~s("767969")
}
@id_type %{
type: "string",
example: ~s("1")
}
@wei_type %{
type: "wei",
definition: &__MODULE__.wei_type_definition/1,
@ -1737,6 +1758,35 @@ defmodule BlockScoutWeb.Etherscan do
]
}
@block_eth_block_number_action %{
name: "eth_block_number",
description: "Mimics Ethereum JSON RPC's eth_blockNumber. Returns the lastest block number",
required_params: [],
optional_params: [
%{
key: "id",
placeholder: "request id",
type: "integer",
description: "A nonnegative integer that represents the json rpc request id."
}
],
responses: [
%{
code: "200",
description: "successful request",
example_value: Jason.encode!(@block_eth_block_number_example_value),
model: %{
name: "Result",
fields: %{
jsonrpc: @jsonrpc_version_type,
id: @id_type,
result: @hex_number_type
}
}
}
]
}
@block_getblockreward_action %{
name: "getblockreward",
description: "Get block reward by block number.",
@ -2171,7 +2221,7 @@ defmodule BlockScoutWeb.Etherscan do
@block_module %{
name: "block",
actions: [@block_getblockreward_action]
actions: [@block_getblockreward_action, @block_eth_block_number_action]
}
@contract_module %{

@ -147,6 +147,13 @@ defmodule BlockScoutWeb.Router do
as: :decompiled_contract
)
resources(
"/logs",
AddressLogsController,
only: [:index],
as: :logs
)
resources(
"/contract_verifications",
AddressContractVerificationController,

@ -22,6 +22,11 @@
"data-test": "coin_balance_tab_link",
to: address_coin_balance_path(@conn, :index, @address.hash)
) %>
<%= link(
gettext("Logs"),
class: "card-tab #{tab_status("logs", @conn.request_path)}",
to: address_logs_path(@conn, :index, @address.hash)
) %>
<%= if BlockScoutWeb.AddressView.validator?(@validation_count) do %>
<%= link(
gettext("Blocks Validated"),
@ -55,4 +60,4 @@
class: "card-tab #{tab_status("read_contract", @conn.request_path)}")
%>
<% end %>
</div>
</div>

@ -0,0 +1,82 @@
<section class="container">
<%= render BlockScoutWeb.AddressView, "overview.html", assigns %>
<div class="card">
<%= render BlockScoutWeb.AddressView, "_tabs.html", assigns %>
<div class="card-body">
<h2 class="card-title"><%= gettext "Logs" %></h2>
<%= 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 %>
<div data-test="transaction_log" class="tile tile-muted">
<dl class="row">
<dt class="col-md-2"> <%= gettext "Transaction" %> </dt>
<dd class="col-md-10">
<h3 class="">
<%= link(
log.transaction,
to: transaction_path(@conn, :show, log.transaction),
"data-test": "log_address_link",
"data-address-hash": log.transaction
) %>
</h3>
</dd>
<dt class="col-md-2"><%= gettext "Topics" %></dt>
<dd class="col-md-10">
<div class="raw-transaction-log-topics">
<%= unless is_nil(log.first_topic) do %>
<div class="text-dark">
<span class="text-dark">[0]</span>
<%= log.first_topic %>
</div>
<% end %>
<%= unless is_nil(log.second_topic) do %>
<div class="text-dark">
<span class="">[1] </span>
<%= log.second_topic %>
</div>
<% end %>
<%= unless is_nil(log.third_topic) do %>
<div class="text-dark">
<span>[2]</span>
<%= log.third_topic %>
</div>
<% end %>
<%= unless is_nil(log.fourth_topic) do %>
<div class="text-dark">
<span>[3]</span>
<%= log.fourth_topic %>
</div>
<% end %>
</div>
</dd>
<dt class="col-md-2">
<%= gettext "Data" %>
</dt>
<dd class="col-md-10">
<%= unless is_nil(log.data) do %>
<div class="text-dark raw-transaction-log-data">
<%= log.data %>
</div>
<% end %>
</dd>
</dl>
</div>
<% end %>
<% else %>
<div class="tile tile-muted text-center">
<span><%= gettext "There are no logs for this address." %></span>
</div>
<% end %>
</div>
</div>
</section>

@ -0,0 +1,3 @@
defmodule BlockScoutWeb.AddressLogsView do
use BlockScoutWeb, :view
end

@ -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
<<

@ -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

@ -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

@ -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, "<a href=\"#{release_link}\" class=\"footer-link\" target=\"_blank\">#{version}</a>"})
end

@ -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
@ -334,6 +334,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 +506,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 ""
@ -682,7 +686,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 +878,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 +899,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 ""
@ -1661,7 +1667,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 +1751,8 @@ msgstr ""
#: lib/block_scout_web/templates/common_components/_pagination_container.html.eex:37
msgid "of"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address_logs/index.html.eex:77
msgid "There are no logs for this address."
msgstr ""

@ -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
@ -334,6 +334,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 +506,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 ""
@ -682,7 +686,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 +878,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 +899,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 ""
@ -1357,7 +1363,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 +1374,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 ""
@ -1661,7 +1667,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 +1751,8 @@ 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_logs/index.html.eex:77
msgid "There are no logs for this address."
msgstr ""

@ -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)

@ -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(<a href="https://github.com/poanetwork/blockscout/releases/tag/v1.3.4-beta" class="footer-link" target="_blank">v1.3.4-beta</a>)}
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(<a href="https://github.com/poanetwork/blockscout/releases/tag/v1.3.4-beta" class="footer-link" target="_blank">v1.3.4-beta</a>)}
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(<a href="https://github.com/poanetwork/blockscout/releases/tag/v1.3.4-beta" class="footer-link" target="_blank">1.3.4</a>)}
~s(<a href="https://github.com/poanetwork/blockscout/releases/tag/v1.3.4-beta" class="footer-link" target="_blank">v1.3.4-beta</a>)}
end
end

@ -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")

@ -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

@ -279,6 +279,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.

@ -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

@ -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

@ -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)

@ -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

@ -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

Loading…
Cancel
Save