Merge branch 'master' into ab-remove-burn-address-from-tests

pull/1744/head
Ayrat Badykov 6 years ago committed by GitHub
commit 3d4584cbf7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      CHANGELOG.md
  2. 2
      README.md
  3. 2
      apps/block_scout_web/config/config.exs
  4. 50
      apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/rpc_translator.ex
  5. 6
      apps/block_scout_web/lib/block_scout_web/templates/address/_tabs.html.eex
  6. 2
      apps/block_scout_web/lib/block_scout_web/templates/address_decompiled_contract/index.html.eex
  7. 2
      apps/block_scout_web/lib/block_scout_web/templates/layout/_footer.html.eex
  8. 18
      apps/block_scout_web/priv/gettext/default.pot
  9. 22
      apps/block_scout_web/priv/gettext/en/LC_MESSAGES/default.po
  10. 16
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/rpc_translator_test.exs
  11. 8
      apps/ethereum_jsonrpc/config/config.exs
  12. 9
      apps/ethereum_jsonrpc/config/test.exs
  13. 19
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc.ex
  14. 30
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/application.ex
  15. 3
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex
  16. 4
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/receipt.ex
  17. 91
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/request_coordinator.ex
  18. 12
      apps/ethereum_jsonrpc/lib/rsk.ex
  19. 34
      apps/ethereum_jsonrpc/test/ethereum_jsonrpc/request_coordinator_test.exs
  20. 25
      apps/explorer/config/dev/rsk.exs
  21. 25
      apps/explorer/config/prod/rsk.exs
  22. 14
      apps/explorer/config/test/rsk.exs
  23. 27
      apps/indexer/config/dev/rsk.exs
  24. 27
      apps/indexer/config/prod/rsk.exs
  25. 4
      apps/indexer/lib/indexer/supervisor.ex

@ -3,6 +3,7 @@
### Features
- [#1739](https://github.com/poanetwork/blockscout/pull/1739) - highlight decompiled source code
- [#1742](https://github.com/poanetwork/blockscout/pull/1742) - Support RSK
### Fixes
@ -13,7 +14,9 @@
### Chore
- [#1749](https://github.com/poanetwork/blockscout/pull/1749) - Replace the link in the footer with the official POA announcements tg channel link
- [#1718](https://github.com/poanetwork/blockscout/pull/1718) - Flatten indexer module hierarchy and supervisor tree
- [#1753](https://github.com/poanetwork/blockscout/pull/1753) - Add a check mark to decompiled contract tab
## 1.3.9-beta

@ -119,7 +119,7 @@ The [development stack page](https://github.com/poanetwork/blockscout/wiki/Devel
`cd apps/explorer && npm install; cd -`
7. Update your JSON RPC Variant in `apps/explorer/config/dev.exs` and `apps/indexer/config/dev.exs`.
For `variant`, enter `ganache`, `geth`, or `parity`
For `variant`, enter `ganache`, `geth`, `parity`, or `rsk`
8. Update your JSON RPC Endpoint in `apps/explorer/config/dev/` and `apps/indexer/config/dev/`
For the `variant` chosen in step 7, enter the correct information for the corresponding JSON RPC Endpoint in `parity.exs`, `geth.exs`, or `ganache.exs`

@ -93,7 +93,7 @@ config :block_scout_web, BlockScoutWeb.Gettext, locales: ~w(en), default_locale:
config :block_scout_web, BlockScoutWeb.SocialMedia,
twitter: "PoaNetwork",
telegram: "oraclesnetwork",
telegram: "poa_network",
facebook: "PoaNetwork",
instagram: "PoaNetwork"

@ -13,6 +13,8 @@ defmodule BlockScoutWeb.API.RPC.RPCTranslator do
"""
require Logger
import Plug.Conn
import Phoenix.Controller, only: [put_view: 2]
@ -28,12 +30,28 @@ defmodule BlockScoutWeb.API.RPC.RPCTranslator do
{:ok, conn} <- call_controller(conn, controller, action) do
conn
else
_ ->
{:error, :no_action} ->
conn
|> put_status(400)
|> put_view(RPCView)
|> Controller.render(:error, error: "Unknown action")
|> halt()
{:error, error} ->
Logger.error(fn -> ["Error while calling RPC action", inspect(error)] end)
conn
|> put_status(500)
|> put_view(RPCView)
|> Controller.render(:error, error: "Something went wrong.")
|> halt()
_ ->
conn
|> put_status(500)
|> put_view(RPCView)
|> Controller.render(:error, error: "Something went wrong.")
|> halt()
end
end
@ -46,27 +64,35 @@ defmodule BlockScoutWeb.API.RPC.RPCTranslator do
end
@doc false
@spec translate_module(map(), String.t()) :: {:ok, module()} | :error
def translate_module(translations, module) do
@spec translate_module(map(), String.t()) :: {:ok, module()} | {:error, :no_action}
defp translate_module(translations, module) do
module_lowercase = String.downcase(module)
Map.fetch(translations, module_lowercase)
case Map.fetch(translations, module_lowercase) do
{:ok, module} -> {:ok, module}
_ -> {:error, :no_action}
end
end
@doc false
@spec translate_action(String.t()) :: {:ok, atom()} | :error
def translate_action(action) do
@spec translate_action(String.t()) :: {:ok, atom()} | {:error, :no_action}
defp translate_action(action) do
action_lowercase = String.downcase(action)
{:ok, String.to_existing_atom(action_lowercase)}
rescue
ArgumentError -> :error
ArgumentError -> {:error, :no_action}
end
@doc false
@spec call_controller(Conn.t(), module(), atom()) :: {:ok, Conn.t()} | :error
def call_controller(conn, controller, action) do
{:ok, controller.call(conn, action)}
@spec call_controller(Conn.t(), module(), atom()) :: {:ok, Conn.t()} | {:error, :no_action} | {:error, Exception.t()}
defp call_controller(conn, controller, action) do
if :erlang.function_exported(controller, action, 2) do
{:ok, controller.call(conn, action)}
else
{:error, :no_action}
end
rescue
Conn.WrapperError ->
:error
e ->
{:error, Exception.format(:error, e, __STACKTRACE__)}
end
end

@ -64,8 +64,9 @@
<%= link(
to: address_decompiled_contract_path(@conn, :index, @address.hash),
class: "nav-link #{tab_status("decompiled_contracts", @conn.request_path)}") do %>
<%= gettext("Decompiled code") %>
<% end %>
<%= gettext("Decompiled code") %>
<i class="far fa-check-circle text-success"></i>
<% end %>
</li>
<% end %>
@ -131,6 +132,7 @@
to: address_decompiled_contract_path(@conn, :index, @address.hash),
class: "dropdown-item #{tab_status("contracts", @conn.request_path)}") do %>
<%= gettext("Decompiled code") %>
<i class="far fa-check-circle text-success"></i>
<% end %>
<% end %>
<% end %>

@ -10,7 +10,7 @@
<div class="card-body">
<h3><%= gettext "Decompiler version" %></h3>
<div class="tile tile-muted">
<pre class="pre-wrap pre-scrollable"><code class="nohighlight"><%= contract.decompiler_version %></code></pre>
<pre class="pre-wrap"><code class="nohighlight"><%= contract.decompiler_version %></code></pre>
</div>
<br>
<section>

@ -17,7 +17,7 @@
<a href="https://www.twitter.com/PoaNetwork/" rel="noreferrer" target="_blank" class="icon-link" data-toggle="tooltip" data-placement="top" title="<%= gettext("Twitter") %>">
<i class="fab fa-twitter"></i>
</a>
<a href="http://t.me/oraclesnetwork" rel="noreferrer" target="_blank" class="icon-link" data-toggle="tooltip" data-placement="top" title="<%= gettext("Telegram") %>">
<a href="https://t.me/poa_network" rel="noreferrer" target="_blank" class="icon-link" data-toggle="tooltip" data-placement="top" title="<%= gettext("Telegram") %>">
<i class="fab fa-telegram-plane"></i>
</a>
</div>

@ -188,7 +188,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:40
#: lib/block_scout_web/templates/address/_tabs.html.eex:113
#: lib/block_scout_web/templates/address/_tabs.html.eex:114
#: lib/block_scout_web/templates/address/overview.html.eex:59
#: lib/block_scout_web/templates/address_validation/index.html.eex:30
#: lib/block_scout_web/templates/address_validation/index.html.eex:57
@ -218,7 +218,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:53
#: lib/block_scout_web/templates/address/_tabs.html.eex:123
#: lib/block_scout_web/templates/address/_tabs.html.eex:124
#: lib/block_scout_web/templates/address_validation/index.html.eex:39
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:119
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:141
@ -488,7 +488,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:21
#: lib/block_scout_web/templates/address/_tabs.html.eex:100
#: lib/block_scout_web/templates/address/_tabs.html.eex:101
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:58
#: lib/block_scout_web/templates/address_validation/index.html.eex:24
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:14
@ -698,8 +698,8 @@ msgid "Query"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:75
#: lib/block_scout_web/templates/address/_tabs.html.eex:140
#: lib/block_scout_web/templates/address/_tabs.html.eex:76
#: lib/block_scout_web/templates/address/_tabs.html.eex:142
#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:33
#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:75
#: lib/block_scout_web/views/address_view.ex:298
@ -891,7 +891,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:13
#: lib/block_scout_web/templates/address/_tabs.html.eex:95
#: lib/block_scout_web/templates/address/_tabs.html.eex:96
#: lib/block_scout_web/templates/address_token/index.html.eex:11
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:12
#: lib/block_scout_web/templates/address_validation/index.html.eex:11
@ -948,7 +948,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:5
#: lib/block_scout_web/templates/address/_tabs.html.eex:90
#: lib/block_scout_web/templates/address/_tabs.html.eex:91
#: lib/block_scout_web/templates/address_transaction/index.html.eex:53
#: lib/block_scout_web/templates/address_validation/index.html.eex:14
#: lib/block_scout_web/templates/block_transaction/index.html.eex:13
@ -1399,7 +1399,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:30
#: lib/block_scout_web/templates/address/_tabs.html.eex:106
#: lib/block_scout_web/templates/address/_tabs.html.eex:107
#: lib/block_scout_web/views/address_view.ex:299
msgid "Coin Balance History"
msgstr ""
@ -1714,7 +1714,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:67
#: lib/block_scout_web/templates/address/_tabs.html.eex:133
#: lib/block_scout_web/templates/address/_tabs.html.eex:134
msgid "Decompiled code"
msgstr ""

@ -188,7 +188,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:40
#: lib/block_scout_web/templates/address/_tabs.html.eex:113
#: lib/block_scout_web/templates/address/_tabs.html.eex:114
#: lib/block_scout_web/templates/address/overview.html.eex:59
#: lib/block_scout_web/templates/address_validation/index.html.eex:30
#: lib/block_scout_web/templates/address_validation/index.html.eex:57
@ -218,7 +218,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:53
#: lib/block_scout_web/templates/address/_tabs.html.eex:123
#: lib/block_scout_web/templates/address/_tabs.html.eex:124
#: lib/block_scout_web/templates/address_validation/index.html.eex:39
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:119
#: lib/block_scout_web/templates/api_docs/_action_tile.html.eex:141
@ -488,7 +488,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:21
#: lib/block_scout_web/templates/address/_tabs.html.eex:100
#: lib/block_scout_web/templates/address/_tabs.html.eex:101
#: lib/block_scout_web/templates/address_internal_transaction/index.html.eex:58
#: lib/block_scout_web/templates/address_validation/index.html.eex:24
#: lib/block_scout_web/templates/transaction/_tabs.html.eex:14
@ -698,8 +698,8 @@ msgid "Query"
msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:75
#: lib/block_scout_web/templates/address/_tabs.html.eex:140
#: lib/block_scout_web/templates/address/_tabs.html.eex:76
#: lib/block_scout_web/templates/address/_tabs.html.eex:142
#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:33
#: lib/block_scout_web/templates/tokens/overview/_tabs.html.eex:75
#: lib/block_scout_web/views/address_view.ex:298
@ -891,7 +891,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:13
#: lib/block_scout_web/templates/address/_tabs.html.eex:95
#: lib/block_scout_web/templates/address/_tabs.html.eex:96
#: lib/block_scout_web/templates/address_token/index.html.eex:11
#: lib/block_scout_web/templates/address_token_transfer/index.html.eex:12
#: lib/block_scout_web/templates/address_validation/index.html.eex:11
@ -948,7 +948,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:5
#: lib/block_scout_web/templates/address/_tabs.html.eex:90
#: lib/block_scout_web/templates/address/_tabs.html.eex:91
#: lib/block_scout_web/templates/address_transaction/index.html.eex:53
#: lib/block_scout_web/templates/address_validation/index.html.eex:14
#: lib/block_scout_web/templates/block_transaction/index.html.eex:13
@ -1399,7 +1399,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:30
#: lib/block_scout_web/templates/address/_tabs.html.eex:106
#: lib/block_scout_web/templates/address/_tabs.html.eex:107
#: lib/block_scout_web/views/address_view.ex:299
msgid "Coin Balance History"
msgstr ""
@ -1714,7 +1714,7 @@ msgstr ""
#, elixir-format
#: lib/block_scout_web/templates/address/_tabs.html.eex:67
#: lib/block_scout_web/templates/address/_tabs.html.eex:133
#: lib/block_scout_web/templates/address/_tabs.html.eex:134
msgid "Decompiled code"
msgstr ""
@ -1723,12 +1723,12 @@ msgstr ""
msgid "Decompiled contract code"
msgstr ""
#, elixir-format, fuzzy
#, elixir-format
#: lib/block_scout_web/templates/address_decompiled_contract/index.html.eex:11
msgid "Decompiler version"
msgstr ""
#, elixir-format, fuzzy
#, elixir-format
#: lib/block_scout_web/templates/address_contract_verification/new.html.eex:52
msgid "Optimization runs"
msgstr ""

@ -75,20 +75,4 @@ defmodule BlockScoutWeb.API.RPC.RPCTranslatorTest do
assert json_response(result, 200) == %{}
end
end
test "translate_module/2" do
assert RPCTranslator.translate_module(%{"test" => __MODULE__}, "tesT") == {:ok, __MODULE__}
assert RPCTranslator.translate_module(%{}, "test") == :error
end
test "translate_action/1" do
expected = :test_atom
assert RPCTranslator.translate_action("test_atoM") == {:ok, expected}
assert RPCTranslator.translate_action("some_atom_that_should_not_exist") == :error
end
test "call_controller/3", %{conn: conn} do
assert RPCTranslator.call_controller(conn, TestController, :bad_action) == :error
assert {:ok, %Plug.Conn{}} = RPCTranslator.call_controller(conn, TestController, :test_action)
end
end

@ -9,6 +9,14 @@ config :ethereum_jsonrpc, EthereumJSONRPC.RequestCoordinator,
wait_per_timeout: :timer.seconds(20),
max_jitter: :timer.seconds(2)
# Add this configuration to add global RPC request throttling.
# throttle_rate_limit: 250,
# throttle_rolling_window_opts: [
# window_count: 4,
# duration: :timer.seconds(15),
# table: EthereumJSONRPC.RequestCoordinator.ThrottleCounter
# ]
config :ethereum_jsonrpc, EthereumJSONRPC.Tracer,
service: :ethereum_jsonrpc,
adapter: SpandexDatadog.Adapter,

@ -7,7 +7,14 @@ config :ethereum_jsonrpc, EthereumJSONRPC.RequestCoordinator,
table: EthereumJSONRPC.RequestCoordinator.TimeoutCounter
],
wait_per_timeout: 2,
max_jitter: 1
max_jitter: 1,
# This should not actually limit anything in tests, but it is here to enable the relevant code for testing
throttle_rate_limit: 10_000,
throttle_rolling_window_opts: [
window_count: 4,
duration: :timer.seconds(1),
table: EthereumJSONRPC.RequestCoordinator.ThrottleCounter
]
config :ethereum_jsonrpc, EthereumJSONRPC.Tracer, disabled?: false

@ -326,11 +326,18 @@ defmodule EthereumJSONRPC do
@doc """
Converts `t:quantity/0` to `t:non_neg_integer/0`.
"""
@spec quantity_to_integer(quantity) :: non_neg_integer()
@spec quantity_to_integer(quantity) :: non_neg_integer() | :error
def quantity_to_integer("0x" <> hexadecimal_digits) do
String.to_integer(hexadecimal_digits, 16)
end
def quantity_to_integer(string) do
case Integer.parse(string) do
{integer, ""} -> integer
_ -> :error
end
end
@doc """
Converts `t:non_neg_integer/0` to `t:quantity/0`
"""
@ -398,9 +405,13 @@ defmodule EthereumJSONRPC do
Converts `t:timestamp/0` to `t:DateTime.t/0`
"""
def timestamp_to_datetime(timestamp) do
timestamp
|> quantity_to_integer()
|> Timex.from_unix()
case quantity_to_integer(timestamp) do
:error ->
nil
quantity ->
Timex.from_unix(quantity)
end
end
defp fetch_blocks_by_params(params, request, json_rpc_named_arguments)

@ -9,16 +9,32 @@ defmodule EthereumJSONRPC.Application do
@impl Application
def start(_type, _args) do
rolling_window_opts =
:ethereum_jsonrpc
|> Application.fetch_env!(RequestCoordinator)
|> Keyword.fetch!(:rolling_window_opts)
config = Application.fetch_env!(:ethereum_jsonrpc, RequestCoordinator)
children = [
rolling_window_opts = Keyword.fetch!(config, :rolling_window_opts)
[
:hackney_pool.child_spec(:ethereum_jsonrpc, recv_timeout: 60_000, timeout: 60_000, max_connections: 1000),
{RollingWindow, [rolling_window_opts]}
Supervisor.child_spec({RollingWindow, [rolling_window_opts]}, id: RollingWindow.ErrorThrottle)
]
|> add_throttle_rolling_window(config)
|> Supervisor.start_link(strategy: :one_for_one, name: EthereumJSONRPC.Supervisor)
end
defp add_throttle_rolling_window(children, config) do
if config[:throttle_rate_limit] do
case Keyword.fetch(config, :throttle_rolling_window_opts) do
{:ok, throttle_rolling_window_opts} ->
child =
Supervisor.child_spec({RollingWindow, [throttle_rolling_window_opts]}, id: RollingWindow.ThrottleRateLimit)
[child | children]
Supervisor.start_link(children, strategy: :one_for_one, name: EthereumJSONRPC.Supervisor)
:error ->
raise "If you have configured `:throttle_rate_limit` you must also configure `:throttle_rolling_window_opts`"
end
else
children
end
end
end

@ -431,7 +431,8 @@ defmodule EthereumJSONRPC.Block do
Enum.into(block, %{}, &entry_to_elixir/1)
end
defp entry_to_elixir({key, quantity}) when key in ~w(difficulty gasLimit gasUsed number size totalDifficulty) do
defp entry_to_elixir({key, quantity})
when key in ~w(difficulty gasLimit gasUsed minimumGasPrice number size totalDifficulty) do
{key, quantity_to_integer(quantity)}
end

@ -274,10 +274,10 @@ defmodule EthereumJSONRPC.Receipt do
defp entry_to_elixir({"status" = key, status}) do
case status do
"0x0" ->
zero when zero in ["0x0", "0x00"] ->
{:ok, {key, :error}}
"0x1" ->
one when one in ["0x1", "0x01"] ->
{:ok, {key, :ok}}
# pre-Byzantium / Ethereum Classic on Parity

@ -18,9 +18,14 @@ defmodule EthereumJSONRPC.RequestCoordinator do
the tracked window
* `:max_jitter` - Maximimum amount of time in milliseconds to be added to each
wait before multiplied by timeout count
* `:throttle_rolling_window_opts` - Options for the process tracking all requests
* `:window_count` - Number of windows
* `:duration` - Total amount of time to coumt events in milliseconds
* `:table` - name of the ets table to store the data in
* `:throttle_rate_limit` - The total number of requests allowed in the all windows.
See the docs for `EthereumJSONRPC.RollingWindow` for more documentation for
`:rolling_window_opts`.
`:rolling_window_opts` and `:throttle_rolling_window_opts`.
This is how the wait time for each request is calculated:
@ -33,13 +38,20 @@ defmodule EthereumJSONRPC.RequestCoordinator do
config :ethereum_jsonrpc, EthereumJSONRPC.RequestCoordinator,
rolling_window_opts: [
window_count: 6,
duration: :timer.seconds(10),
duration: :timer.minutes(1),
table: EthereumJSONRPC.RequestCoordinator.TimeoutCounter
],
wait_per_timeout: :timer.seconds(10),
max_jitter: :timer.seconds(1)
throttle_rate_limit: 60,
throttle_rolling_window_opts: [
window_count: 3,
duration: :timer.seconds(10),
table: EthereumJSONRPC.RequestCoordinator.RequestCounter
]
With this configuration, timeouts are tracked for 6 windows of 10 seconds for a total of 1 minute.
Requests are tracked for 3 windows of 10 seconds, for a total of 30 seconds, and
"""
require EthereumJSONRPC.Tracer
@ -47,6 +59,7 @@ defmodule EthereumJSONRPC.RequestCoordinator do
alias EthereumJSONRPC.{RollingWindow, Tracer, Transport}
@error_key :throttleable_error_count
@throttle_key :throttle_requests_count
@doc """
Performs a JSON RPC request and adds necessary backoff.
@ -64,12 +77,19 @@ defmodule EthereumJSONRPC.RequestCoordinator do
if sleep_time <= throttle_timeout do
:timer.sleep(sleep_time)
trace_request(request, fn ->
request
|> transport.json_rpc(transport_options)
|> handle_transport_response()
end)
remaining_wait_time = throttle_timeout - sleep_time
case throttle_request(remaining_wait_time) do
:ok ->
trace_request(request, fn ->
request
|> transport.json_rpc(transport_options)
|> handle_transport_response()
end)
:error ->
{:error, :timeout}
end
else
:timer.sleep(throttle_timeout)
@ -91,33 +111,78 @@ defmodule EthereumJSONRPC.RequestCoordinator do
defp handle_transport_response({:error, {:bad_gateway, _}} = error) do
RollingWindow.inc(table(), @error_key)
inc_throttle_table()
error
end
defp handle_transport_response({:error, :timeout} = error) do
RollingWindow.inc(table(), @error_key)
inc_throttle_table()
error
end
defp handle_transport_response(response), do: response
defp handle_transport_response(response) do
inc_throttle_table()
response
end
defp inc_throttle_table do
if config(:throttle_rolling_window_opts) do
RollingWindow.inc(throttle_table(), @throttle_key)
end
end
defp throttle_request(
remaining_time,
rate_limit \\ config(:throttle_rate_limit),
opts \\ config(:throttle_rolling_window_opts)
) do
if opts[:throttle_rate_limit] && RollingWindow.count(throttle_table(), @throttle_key) >= rate_limit do
if opts[:duration] >= remaining_time do
:timer.sleep(remaining_time)
:error
else
new_remaining_time = remaining_time - opts[:duration]
:timer.sleep(opts[:duration])
throttle_request(new_remaining_time, rate_limit, opts)
end
else
:ok
end
end
defp sleep_time do
wait_coefficient = RollingWindow.count(table(), @error_key)
jitter = :rand.uniform(config(:max_jitter))
wait_per_timeout = config(:wait_per_timeout)
jitter = :rand.uniform(config!(:max_jitter))
wait_per_timeout = config!(:wait_per_timeout)
wait_coefficient * (wait_per_timeout + jitter)
end
defp table do
:rolling_window_opts
|> config()
|> config!()
|> Keyword.fetch!(:table)
end
defp config(key) do
defp throttle_table do
case config(:throttle_rolling_window_opts) do
nil -> :ignore
keyword -> Keyword.fetch!(keyword, :table)
end
end
defp config!(key) do
:ethereum_jsonrpc
|> Application.get_env(__MODULE__)
|> Keyword.fetch!(key)
end
defp config(key) do
:ethereum_jsonrpc
|> Application.get_env(__MODULE__)
|> Keyword.get(key)
end
end

@ -0,0 +1,12 @@
defmodule EthereumJSONRPC.RSK do
@moduledoc """
Ethereum JSONRPC methods that are/are not supported by [RSK](https://www.rsk.co/).
"""
@behaviour EthereumJSONRPC.Variant
def fetch_internal_transactions(_, _), do: :ignore
def fetch_pending_transactions(_), do: :ignore
def fetch_block_internal_transactions(_block_numbers, _json_rpc_named_arguments), do: :ignore
def fetch_beneficiaries(_, _), do: :ignore
end

@ -11,38 +11,50 @@ defmodule EthereumJSONRPC.RequestCoordinatorTest do
setup :verify_on_exit!
setup do
table = Application.get_env(:ethereum_jsonrpc, EthereumJSONRPC.RequestCoordinator)[:rolling_window_opts][:table]
timeout_table =
Application.get_env(:ethereum_jsonrpc, EthereumJSONRPC.RequestCoordinator)[:rolling_window_opts][:table]
:ets.delete_all_objects(table)
throttle_table =
Application.get_env(:ethereum_jsonrpc, EthereumJSONRPC.RequestCoordinator)[:throttle_rolling_window_opts][:table]
%{table: table}
:ets.delete_all_objects(timeout_table)
:ets.delete_all_objects(throttle_table)
%{timeout_table: timeout_table, throttle_table: throttle_table}
end
describe "perform/4" do
test "forwards result whenever a request doesn't timeout", %{table: table} do
test "forwards result whenever a request doesn't timeout", %{timeout_table: timeout_table} do
expect(EthereumJSONRPC.Mox, :json_rpc, fn _, _ -> {:ok, %{}} end)
assert RollingWindow.count(table, :throttleable_error_count) == 0
assert RollingWindow.count(timeout_table, :throttleable_error_count) == 0
assert {:ok, %{}} == RequestCoordinator.perform(%{}, EthereumJSONRPC.Mox, [], :timer.minutes(60))
assert RollingWindow.count(table, :throttleable_error_count) == 0
assert RollingWindow.count(timeout_table, :throttleable_error_count) == 0
end
test "increments counter on certain errors", %{table: table} do
test "increments counter on certain errors", %{timeout_table: timeout_table} do
expect(EthereumJSONRPC.Mox, :json_rpc, fn :timeout, _ -> {:error, :timeout} end)
expect(EthereumJSONRPC.Mox, :json_rpc, fn :bad_gateway, _ -> {:error, {:bad_gateway, "message"}} end)
assert {:error, :timeout} == RequestCoordinator.perform(:timeout, EthereumJSONRPC.Mox, [], :timer.minutes(60))
assert RollingWindow.count(table, :throttleable_error_count) == 1
assert RollingWindow.count(timeout_table, :throttleable_error_count) == 1
assert {:error, {:bad_gateway, "message"}} ==
RequestCoordinator.perform(:bad_gateway, EthereumJSONRPC.Mox, [], :timer.minutes(60))
assert RollingWindow.count(table, :throttleable_error_count) == 2
assert RollingWindow.count(timeout_table, :throttleable_error_count) == 2
end
test "returns timeout error if sleep time will exceed max timeout", %{table: table} do
test "returns timeout error if sleep time will exceed max timeout", %{timeout_table: timeout_table} do
expect(EthereumJSONRPC.Mox, :json_rpc, 0, fn _, _ -> :ok end)
RollingWindow.inc(table, :throttleable_error_count)
RollingWindow.inc(timeout_table, :throttleable_error_count)
assert {:error, :timeout} == RequestCoordinator.perform(%{}, EthereumJSONRPC.Mox, [], 1)
end
test "increments throttle_table even when not an error", %{throttle_table: throttle_table} do
expect(EthereumJSONRPC.Mox, :json_rpc, fn _, _ -> {:ok, %{}} end)
assert RollingWindow.count(throttle_table, :throttle_requests_count) == 0
assert {:ok, %{}} == RequestCoordinator.perform(%{}, EthereumJSONRPC.Mox, [], :timer.minutes(60))
assert RollingWindow.count(throttle_table, :throttle_requests_count) == 1
end
end
end

@ -0,0 +1,25 @@
use Mix.Config
config :explorer,
json_rpc_named_arguments: [
transport: EthereumJSONRPC.HTTP,
transport_options: [
http: EthereumJSONRPC.HTTP.HTTPoison,
url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "http://localhost:8545",
method_to_url: [
eth_call: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545",
eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545",
trace_replayTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545"
],
http_options: [recv_timeout: :timer.minutes(1), timeout: :timer.minutes(1), hackney: [pool: :ethereum_jsonrpc]]
],
variant: EthereumJSONRPC.RSK
],
subscribe_named_arguments: [
transport: EthereumJSONRPC.WebSocket,
transport_options: [
web_socket: EthereumJSONRPC.WebSocket.WebSocketClient,
url: System.get_env("ETHEREUM_JSONRPC_WS_URL") || "ws://localhost:8546"
],
variant: EthereumJSONRPC.RSK
]

@ -0,0 +1,25 @@
use Mix.Config
config :explorer,
json_rpc_named_arguments: [
transport: EthereumJSONRPC.HTTP,
transport_options: [
http: EthereumJSONRPC.HTTP.HTTPoison,
url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL"),
method_to_url: [
eth_call: System.get_env("ETHEREUM_JSONRPC_TRACE_URL"),
eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL"),
trace_replayTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL")
],
http_options: [recv_timeout: :timer.minutes(1), timeout: :timer.minutes(1), hackney: [pool: :ethereum_jsonrpc]]
],
variant: EthereumJSONRPC.RSK
],
subscribe_named_arguments: [
transport: EthereumJSONRPC.WebSocket,
transport_options: [
web_socket: EthereumJSONRPC.WebSocket.WebSocketClient,
url: System.get_env("ETHEREUM_JSONRPC_WS_URL")
],
variant: EthereumJSONRPC.RSK
]

@ -0,0 +1,14 @@
use Mix.Config
config :explorer,
transport: EthereumJSONRPC.HTTP,
json_rpc_named_arguments: [
transport: EthereumJSONRPC.Mox,
transport_options: [],
variant: EthereumJSONRPC.RSK
],
subscribe_named_arguments: [
transport: EthereumJSONRPC.Mox,
transport_options: [],
variant: EthereumJSONRPC.RSK
]

@ -0,0 +1,27 @@
use Mix.Config
config :indexer,
block_interval: :timer.seconds(5),
blocks_concurrency: 1,
receipts_concurrency: 1,
json_rpc_named_arguments: [
transport: EthereumJSONRPC.HTTP,
transport_options: [
http: EthereumJSONRPC.HTTP.HTTPoison,
url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL") || "http://localhost:8545",
method_to_url: [
eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545",
trace_block: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545",
trace_replayBlockTransactions: System.get_env("ETHEREUM_JSONRPC_TRACE_URL") || "http://localhost:8545"
],
http_options: [recv_timeout: :timer.minutes(10), timeout: :timer.minutes(10), hackney: [pool: :ethereum_jsonrpc]]
],
variant: EthereumJSONRPC.RSK
],
subscribe_named_arguments: [
transport: EthereumJSONRPC.WebSocket,
transport_options: [
web_socket: EthereumJSONRPC.WebSocket.WebSocketClient,
url: System.get_env("ETHEREUM_JSONRPC_WS_URL") || "ws://localhost:8546"
]
]

@ -0,0 +1,27 @@
use Mix.Config
config :indexer,
block_interval: :timer.seconds(5),
blocks_concurrency: 1,
receipts_concurrency: 1,
json_rpc_named_arguments: [
transport: EthereumJSONRPC.HTTP,
transport_options: [
http: EthereumJSONRPC.HTTP.HTTPoison,
url: System.get_env("ETHEREUM_JSONRPC_HTTP_URL"),
method_to_url: [
eth_getBalance: System.get_env("ETHEREUM_JSONRPC_TRACE_URL"),
trace_block: System.get_env("ETHEREUM_JSONRPC_TRACE_URL"),
trace_replayTransaction: System.get_env("ETHEREUM_JSONRPC_TRACE_URL")
],
http_options: [recv_timeout: :timer.minutes(10), timeout: :timer.minutes(10), hackney: [pool: :ethereum_jsonrpc]]
],
variant: EthereumJSONRPC.RSK
],
subscribe_named_arguments: [
transport: EthereumJSONRPC.WebSocket,
transport_options: [
web_socket: EthereumJSONRPC.WebSocket.WebSocketClient,
url: System.get_env("ETHEREUM_JSONRPC_WS_URL")
]
]

@ -75,7 +75,7 @@ defmodule Indexer.Supervisor do
block_fetcher =
named_arguments
|> Map.drop(~w(block_interval memory_monitor subscribe_named_arguments realtime_overrides)a)
|> Map.drop(~w(block_interval blocks_concurrency memory_monitor subscribe_named_arguments realtime_overrides)a)
|> Block.Fetcher.new()
fixing_realtime_fetcher = %Block.Fetcher{
@ -86,7 +86,7 @@ defmodule Indexer.Supervisor do
realtime_block_fetcher =
named_arguments
|> Map.drop(~w(block_interval memory_monitor subscribe_named_arguments realtime_overrides)a)
|> Map.drop(~w(block_interval blocks_concurrency memory_monitor subscribe_named_arguments realtime_overrides)a)
|> Map.merge(Enum.into(realtime_overrides, %{}))
|> Block.Fetcher.new()

Loading…
Cancel
Save