Merge pull request #2862 from poanetwork/vb-coin-total-supply-from-db-api

Coin total supply from DB API endpoint
ab-do-not-remove-token-transfers
Victor Baranov 5 years ago committed by GitHub
commit 29faeff3f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 13
      apps/block_scout_web/lib/block_scout_web/controllers/api/rpc/stats_controller.ex
  3. 35
      apps/block_scout_web/lib/block_scout_web/etherscan.ex
  4. 4
      apps/block_scout_web/lib/block_scout_web/views/api/rpc/stats_view.ex
  5. 29
      apps/block_scout_web/test/block_scout_web/controllers/api/rpc/stats_controller_test.exs
  6. 14
      apps/explorer/config/config.exs
  7. 2
      apps/explorer/lib/explorer/application.ex
  8. 11
      apps/explorer/lib/explorer/chain.ex
  9. 53
      apps/explorer/lib/explorer/chain/cache/address_sum.ex
  10. 56
      apps/explorer/test/explorer/chain/cache/address_sum_test.exs
  11. 2
      apps/explorer/test/explorer/chain/cache/block_count_test.exs
  12. 14
      apps/explorer/test/explorer/chain_test.exs

@ -1,6 +1,7 @@
## Current ## Current
### Features ### Features
- [#2862](https://github.com/poanetwork/blockscout/pull/2862) - Coin total supply from DB API endpoint
- [#2825](https://github.com/poanetwork/blockscout/pull/2825) - separate token transfers and transactions - [#2825](https://github.com/poanetwork/blockscout/pull/2825) - separate token transfers and transactions
- [#2787](https://github.com/poanetwork/blockscout/pull/2787) - async fetching of address counters - [#2787](https://github.com/poanetwork/blockscout/pull/2787) - async fetching of address counters
- [#2791](https://github.com/poanetwork/blockscout/pull/2791) - add ipc client - [#2791](https://github.com/poanetwork/blockscout/pull/2791) - add ipc client

@ -1,7 +1,10 @@
defmodule BlockScoutWeb.API.RPC.StatsController do defmodule BlockScoutWeb.API.RPC.StatsController do
use BlockScoutWeb, :controller use BlockScoutWeb, :controller
use Explorer.Schema
alias Explorer.{Chain, ExchangeRates} alias Explorer.{Chain, ExchangeRates}
alias Explorer.Chain.Cache.AddressSum
alias Explorer.Chain.Wei alias Explorer.Chain.Wei
def tokensupply(conn, params) do def tokensupply(conn, params) do
@ -21,7 +24,7 @@ defmodule BlockScoutWeb.API.RPC.StatsController do
end end
end end
def ethsupply(conn, _params) do def ethsupplyexchange(conn, _params) do
wei_total_supply = wei_total_supply =
Chain.total_supply() Chain.total_supply()
|> Decimal.new() |> Decimal.new()
@ -29,7 +32,13 @@ defmodule BlockScoutWeb.API.RPC.StatsController do
|> Wei.to(:wei) |> Wei.to(:wei)
|> Decimal.to_string() |> Decimal.to_string()
render(conn, "ethsupply.json", total_supply: wei_total_supply) render(conn, "ethsupplyexchange.json", total_supply: wei_total_supply)
end
def ethsupply(conn, _params) do
cached_wei_total_supply = AddressSum.get_sum()
render(conn, "ethsupply.json", total_supply: cached_wei_total_supply)
end end
def ethprice(conn, _params) do def ethprice(conn, _params) do

@ -261,6 +261,12 @@ defmodule BlockScoutWeb.Etherscan do
"result" => "21265524714464" "result" => "21265524714464"
} }
@stats_ethsupplyexchange_example_value %{
"status" => "1",
"message" => "OK",
"result" => "101959776311500000000000000"
}
@stats_ethsupply_example_value %{ @stats_ethsupply_example_value %{
"status" => "1", "status" => "1",
"message" => "OK", "message" => "OK",
@ -1772,9 +1778,35 @@ defmodule BlockScoutWeb.Etherscan do
] ]
} }
@stats_ethsupplyexchange_action %{
name: "ethsupplyexchange",
description: "Get total supply in Wei from exchange.",
required_params: [],
optional_params: [],
responses: [
%{
code: "200",
description: "successful operation",
example_value: Jason.encode!(@stats_ethsupplyexchange_example_value),
model: %{
name: "Result",
fields: %{
status: @status_type,
message: @message_type,
result: %{
type: "integer",
description: "The total supply.",
example: ~s("101959776311500000000000000")
}
}
}
}
]
}
@stats_ethsupply_action %{ @stats_ethsupply_action %{
name: "ethsupply", name: "ethsupply",
description: "Get total supply in Wei.", description: "Get total supply in Wei from DB.",
required_params: [], required_params: [],
optional_params: [], optional_params: [],
responses: [ responses: [
@ -2302,6 +2334,7 @@ defmodule BlockScoutWeb.Etherscan do
name: "stats", name: "stats",
actions: [ actions: [
@stats_tokensupply_action, @stats_tokensupply_action,
@stats_ethsupplyexchange_action,
@stats_ethsupply_action, @stats_ethsupply_action,
@stats_ethprice_action @stats_ethprice_action
] ]

@ -7,6 +7,10 @@ defmodule BlockScoutWeb.API.RPC.StatsView do
RPCView.render("show.json", data: Decimal.to_string(token_supply)) RPCView.render("show.json", data: Decimal.to_string(token_supply))
end end
def render("ethsupplyexchange.json", %{total_supply: total_supply}) do
RPCView.render("show.json", data: total_supply)
end
def render("ethsupply.json", %{total_supply: total_supply}) do def render("ethsupply.json", %{total_supply: total_supply}) do
RPCView.render("show.json", data: total_supply) RPCView.render("show.json", data: total_supply)
end end

@ -85,8 +85,27 @@ defmodule BlockScoutWeb.API.RPC.StatsControllerTest do
end end
end end
describe "ethsupplyexchange" do
test "returns total supply from exchange", %{conn: conn} do
params = %{
"module" => "stats",
"action" => "ethsupplyexchange"
}
assert response =
conn
|> get("/api", params)
|> json_response(200)
assert response["result"] == "252460800000000000000000000"
assert response["status"] == "1"
assert response["message"] == "OK"
assert :ok = ExJsonSchema.Validator.validate(ethsupplyexchange_schema(), response)
end
end
describe "ethsupply" do describe "ethsupply" do
test "returns total supply", %{conn: conn} do test "returns total supply from DB", %{conn: conn} do
params = %{ params = %{
"module" => "stats", "module" => "stats",
"action" => "ethsupply" "action" => "ethsupply"
@ -97,7 +116,7 @@ defmodule BlockScoutWeb.API.RPC.StatsControllerTest do
|> get("/api", params) |> get("/api", params)
|> json_response(200) |> json_response(200)
assert response["result"] == "252460800000000000000000000" assert response["result"] == "6"
assert response["status"] == "1" assert response["status"] == "1"
assert response["message"] == "OK" assert response["message"] == "OK"
assert :ok = ExJsonSchema.Validator.validate(ethsupply_schema(), response) assert :ok = ExJsonSchema.Validator.validate(ethsupply_schema(), response)
@ -179,6 +198,12 @@ defmodule BlockScoutWeb.API.RPC.StatsControllerTest do
}) })
end end
defp ethsupplyexchange_schema do
resolve_schema(%{
"type" => ["string", "null"]
})
end
defp ethprice_schema do defp ethprice_schema do
resolve_schema(%{ resolve_schema(%{
"type" => "object", "type" => "object",

@ -52,6 +52,20 @@ config :explorer, Explorer.Chain.Cache.BlockNumber,
ttl_check_interval: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(1), else: false), ttl_check_interval: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(1), else: false),
global_ttl: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(5)) global_ttl: if(System.get_env("DISABLE_INDEXER") == "true", do: :timer.seconds(5))
address_sum_global_ttl =
"ADDRESS_SUM_CACHE_PERIOD"
|> System.get_env("")
|> Integer.parse()
|> case do
{integer, ""} -> :timer.seconds(integer)
_ -> :timer.minutes(60)
end
config :explorer, Explorer.Chain.Cache.AddressSum,
enabled: true,
ttl_check_interval: :timer.seconds(1),
global_ttl: address_sum_global_ttl
balances_update_interval = balances_update_interval =
if System.get_env("ADDRESS_WITH_BALANCES_UPDATE_INTERVAL") do if System.get_env("ADDRESS_WITH_BALANCES_UPDATE_INTERVAL") do
case Integer.parse(System.get_env("ADDRESS_WITH_BALANCES_UPDATE_INTERVAL")) do case Integer.parse(System.get_env("ADDRESS_WITH_BALANCES_UPDATE_INTERVAL")) do

@ -9,6 +9,7 @@ defmodule Explorer.Application do
alias Explorer.Chain.Cache.{ alias Explorer.Chain.Cache.{
Accounts, Accounts,
AddressSum,
BlockCount, BlockCount,
BlockNumber, BlockNumber,
Blocks, Blocks,
@ -46,6 +47,7 @@ defmodule Explorer.Application do
{Registry, keys: :duplicate, name: Registry.ChainEvents, id: Registry.ChainEvents}, {Registry, keys: :duplicate, name: Registry.ChainEvents, id: Registry.ChainEvents},
{Admin.Recovery, [[], [name: Admin.Recovery]]}, {Admin.Recovery, [[], [name: Admin.Recovery]]},
TransactionCount, TransactionCount,
AddressSum,
BlockCount, BlockCount,
Blocks, Blocks,
NetVersion, NetVersion,

@ -1322,6 +1322,17 @@ defmodule Explorer.Chain do
Repo.one!(query) Repo.one!(query)
end end
@spec fetch_sum_coin_total_supply() :: non_neg_integer
def fetch_sum_coin_total_supply do
query =
from(
a0 in Address,
select: fragment("SUM(a0.fetched_coin_balance)")
)
Repo.one!(query) || 0
end
@doc """ @doc """
The number of `t:Explorer.Chain.InternalTransaction.t/0`. The number of `t:Explorer.Chain.InternalTransaction.t/0`.

@ -0,0 +1,53 @@
defmodule Explorer.Chain.Cache.AddressSum do
@moduledoc """
Cache for address sum.
"""
require Logger
use Explorer.Chain.MapCache,
name: :address_sum,
key: :sum,
key: :async_task,
ttl_check_interval: Application.get_env(:explorer, __MODULE__)[:ttl_check_interval],
global_ttl: Application.get_env(:explorer, __MODULE__)[:global_ttl],
callback: &async_task_on_deletion(&1)
alias Explorer.Chain
defp handle_fallback(:sum) do
# This will get the task PID if one exists and launch a new task if not
# See next `handle_fallback` definition
get_async_task()
{:return, nil}
end
defp handle_fallback(:async_task) do
# If this gets called it means an async task was requested, but none exists
# so a new one needs to be launched
{:ok, task} =
Task.start(fn ->
try do
result = Chain.fetch_sum_coin_total_supply()
set_sum(result)
rescue
e ->
Logger.debug([
"Coudn't update address sum test #{inspect(e)}"
])
end
set_async_task(nil)
end)
{:update, task}
end
# By setting this as a `callback` an async task will be started each time the
# `sum` expires (unless there is one already running)
defp async_task_on_deletion({:delete, _, :sum}), do: get_async_task()
defp async_task_on_deletion(_data), do: nil
end

@ -0,0 +1,56 @@
defmodule Explorer.Chain.Cache.AddressSumTest do
use Explorer.DataCase
alias Explorer.Chain.Cache.AddressSum
setup do
Supervisor.terminate_child(Explorer.Supervisor, AddressSum.child_id())
Supervisor.restart_child(Explorer.Supervisor, AddressSum.child_id())
:ok
end
test "returns default address sum" do
result = AddressSum.get_sum()
assert is_nil(result)
end
test "updates cache if initial value is zero" do
insert(:address, fetched_coin_balance: 1)
insert(:address, fetched_coin_balance: 2)
insert(:address, fetched_coin_balance: 3)
_result = AddressSum.get_sum()
Process.sleep(1000)
updated_value = Decimal.to_integer(AddressSum.get_sum())
assert updated_value == 6
end
test "does not update cache if cache period did not pass" do
insert(:address, fetched_coin_balance: 1)
insert(:address, fetched_coin_balance: 2)
insert(:address, fetched_coin_balance: 3)
_result = AddressSum.get_sum()
Process.sleep(1000)
updated_value = Decimal.to_integer(AddressSum.get_sum())
assert updated_value == 6
insert(:address, fetched_coin_balance: 4)
insert(:address, fetched_coin_balance: 5)
_updated_value = AddressSum.get_sum()
Process.sleep(1000)
updated_value = Decimal.to_integer(AddressSum.get_sum())
assert updated_value == 6
end
end

@ -9,7 +9,7 @@ defmodule Explorer.Chain.Cache.BlockCountTest do
:ok :ok
end end
test "returns default transaction count" do test "returns default block count" do
result = BlockCount.get_count() result = BlockCount.get_count()
assert is_nil(result) assert is_nil(result)

@ -1121,6 +1121,20 @@ defmodule Explorer.ChainTest do
end end
end end
describe "fetch_sum_coin_total_supply/0" do
test "fetches coin total supply" do
for index <- 0..4 do
insert(:address, fetched_coin_balance: index)
end
assert "10" = Decimal.to_string(Chain.fetch_sum_coin_total_supply())
end
test "fetches coin total supply when there are no blocks" do
assert 0 = Chain.fetch_sum_coin_total_supply()
end
end
describe "address_hash_to_token_transfers/2" do describe "address_hash_to_token_transfers/2" do
test "returns just the token transfers related to the given contract address" do test "returns just the token transfers related to the given contract address" do
contract_address = contract_address =

Loading…
Cancel
Save