commit
1c3adb1b5b
@ -1,20 +0,0 @@ |
||||
# Upgrading Guide |
||||
|
||||
### Migration scripts |
||||
|
||||
There is in the project a `scripts` folder that contains `SQL` files responsible to migrate data from the database. |
||||
|
||||
This script should be used if you already have an indexed database with a large amount of data. |
||||
|
||||
#### `address_current_token_balances_in_batches.sql` |
||||
|
||||
Is responsible to populate a new table using the `token_balances` table information. |
||||
|
||||
#### `internal_transaction_update_in_batches.sql` |
||||
|
||||
Is responsible to migrate data from the `transactions` table to the `internal_transactions` one in order to improve the application listing performance; |
||||
|
||||
#### `transaction_update_in_baches.sql` |
||||
|
||||
Parity call traces contain the input, but it was not put in the internal_transactions_params. |
||||
Enforce input and call_type being non-NULL for calls in new constraints on internal_transactions. |
@ -0,0 +1,9 @@ |
||||
import $ from 'jquery' |
||||
|
||||
$(document).click(function (event) { |
||||
var clickover = $(event.target) |
||||
var _opened = $('.navbar-collapse').hasClass('show') |
||||
if (_opened === true && $('.navbar').find(clickover).length < 1) { |
||||
$('.navbar-toggler').click() |
||||
} |
||||
}) |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
@ -0,0 +1,34 @@ |
||||
<section class="container"> |
||||
<div class="card"> |
||||
<div class="card-body"> |
||||
<h1 class="card-title margin-bottom-sm"><%= gettext("ETH RPC API Documentation") %></h2> |
||||
<p class="api-text-monospace" data-endpoint-url="<%= BlockScoutWeb.Endpoint.url() %>/api/eth_rpc">[ <%= gettext "Base URL:" %> <%= @conn.host %>/api/eth_rpc ]</p> |
||||
<p class="card-subtitle margin-bottom-0"> |
||||
<%= gettext "This API is provided to support some rpc methods in the exact format specified for ethereum nodes, which can be found " %> |
||||
|
||||
<a href="https://github.com/ethereum/wiki/wiki/JSON-RPC"><%= gettext "here." %></a> |
||||
<%= gettext "This is useful to allow sending requests to blockscout without having to change anything about the request." %> |
||||
<%= gettext "However, in general, the" %> <%= link( |
||||
gettext("custom RPC"), |
||||
to: api_docs_path(@conn, :index) |
||||
) %> <%= gettext " is recommended." %> |
||||
<%= gettext "Anything not in this list is not supported. Click on the method to be taken to the documentation for that method, and check the notes section for any potential differences." %> |
||||
</p> |
||||
</div> |
||||
</div> |
||||
<div class="card"> |
||||
<div class="card-body"> |
||||
<table class="table"> |
||||
<tr> |
||||
<th>Supported Method</th> |
||||
<th>Notes</th> |
||||
</tr> |
||||
<%= for {method, info} <- Map.to_list(@documentation) do %> |
||||
<tr> |
||||
<td> <a href="https://github.com/ethereum/wiki/wiki/JSON-RPC#<%= method %>"> <%= method %> </a> </td> |
||||
<td> <%= Map.get(info, :notes, "N/A") %> </td> |
||||
</tr> |
||||
<% end %> |
||||
</table> |
||||
</div> |
||||
</section> |
@ -0,0 +1,103 @@ |
||||
defmodule Explorer.Chain.Supply.RSK do |
||||
@moduledoc """ |
||||
Defines the supply API for calculating supply for coins from RSK. |
||||
""" |
||||
|
||||
use Explorer.Chain.Supply |
||||
|
||||
import Ecto.Query, only: [from: 2] |
||||
|
||||
alias Explorer.Chain.Address.CoinBalance |
||||
alias Explorer.Chain.{Block, Wei} |
||||
alias Explorer.ExchangeRates.Token |
||||
alias Explorer.{Market, Repo} |
||||
|
||||
def market_cap(exchange_rate) do |
||||
circulating() * exchange_rate.usd_value |
||||
end |
||||
|
||||
@doc "Equivalent to getting the circulating value " |
||||
def supply_for_days(days) do |
||||
now = Timex.now() |
||||
|
||||
balances_query = |
||||
from(balance in CoinBalance, |
||||
join: block in Block, |
||||
on: block.number == balance.block_number, |
||||
where: block.consensus == true, |
||||
where: balance.address_hash == ^"0x0000000000000000000000000000000001000006", |
||||
where: block.timestamp > ^Timex.shift(now, days: -days), |
||||
distinct: fragment("date_trunc('day', ?)", block.timestamp), |
||||
select: {block.timestamp, balance.value} |
||||
) |
||||
|
||||
balance_before_query = |
||||
from(balance in CoinBalance, |
||||
join: block in Block, |
||||
on: block.number == balance.block_number, |
||||
where: block.consensus == true, |
||||
where: balance.address_hash == ^"0x0000000000000000000000000000000001000006", |
||||
where: block.timestamp <= ^Timex.shift(Timex.now(), days: -days), |
||||
order_by: [desc: block.timestamp], |
||||
limit: 1, |
||||
select: balance.value |
||||
) |
||||
|
||||
by_day = |
||||
balances_query |
||||
|> Repo.all() |
||||
|> Enum.into(%{}, fn {timestamp, value} -> |
||||
{Timex.to_date(timestamp), value} |
||||
end) |
||||
|
||||
starting = Repo.one(balance_before_query) || wei!(0) |
||||
|
||||
result = |
||||
-days..0 |
||||
|> Enum.reduce({%{}, starting.value}, fn i, {days, last} -> |
||||
date = |
||||
now |
||||
|> Timex.shift(days: i) |
||||
|> Timex.to_date() |
||||
|
||||
case Map.get(by_day, date) do |
||||
nil -> |
||||
{Map.put(days, date, last), last} |
||||
|
||||
value -> |
||||
{Map.put(days, date, value.value), value.value} |
||||
end |
||||
end) |
||||
|> elem(0) |
||||
|
||||
{:ok, result} |
||||
end |
||||
|
||||
def circulating do |
||||
query = |
||||
from(balance in CoinBalance, |
||||
join: block in Block, |
||||
on: block.number == balance.block_number, |
||||
where: block.consensus == true, |
||||
where: balance.address_hash == ^"0x0000000000000000000000000000000001000006", |
||||
order_by: [desc: block.timestamp], |
||||
limit: 1, |
||||
select: balance.value |
||||
) |
||||
|
||||
Repo.one(query) || wei!(0) |
||||
end |
||||
|
||||
defp wei!(value) do |
||||
{:ok, wei} = Wei.cast(value) |
||||
wei |
||||
end |
||||
|
||||
def total do |
||||
21_000_000 |
||||
end |
||||
|
||||
def exchange_rate do |
||||
Market.get_exchange_rate(Explorer.coin()) || Token.null() |
||||
end |
||||
end |
@ -0,0 +1,79 @@ |
||||
defmodule Explorer.Market.MarketHistoryCache do |
||||
@moduledoc """ |
||||
Caches recent market history. |
||||
""" |
||||
|
||||
import Ecto.Query, only: [from: 2] |
||||
|
||||
alias Explorer.Market.MarketHistory |
||||
alias Explorer.Repo |
||||
|
||||
@cache_name :market_history |
||||
@last_update_key :last_update |
||||
@history_key :history |
||||
# 6 hours |
||||
@cache_period 1_000 * 60 * 60 * 6 |
||||
@recent_days 30 |
||||
|
||||
def fetch do |
||||
if cache_expired?() do |
||||
update_cache() |
||||
else |
||||
fetch_from_cache(@history_key) |
||||
end |
||||
end |
||||
|
||||
def cache_name, do: @cache_name |
||||
|
||||
def data_key, do: @history_key |
||||
|
||||
def updated_at_key, do: @last_update_key |
||||
|
||||
def recent_days_count, do: @recent_days |
||||
|
||||
defp cache_expired? do |
||||
updated_at = fetch_from_cache(@last_update_key) |
||||
|
||||
cond do |
||||
is_nil(updated_at) -> true |
||||
current_time() - updated_at > @cache_period -> true |
||||
true -> false |
||||
end |
||||
end |
||||
|
||||
defp update_cache do |
||||
new_data = fetch_from_db() |
||||
|
||||
put_into_cache(@last_update_key, current_time()) |
||||
put_into_cache(@history_key, new_data) |
||||
|
||||
new_data |
||||
end |
||||
|
||||
defp fetch_from_db do |
||||
day_diff = @recent_days * -1 |
||||
|
||||
query = |
||||
from( |
||||
mh in MarketHistory, |
||||
where: mh.date > date_add(^Date.utc_today(), ^day_diff, "day"), |
||||
order_by: [desc: mh.date] |
||||
) |
||||
|
||||
Repo.all(query) |
||||
end |
||||
|
||||
defp fetch_from_cache(key) do |
||||
ConCache.get(@cache_name, key) |
||||
end |
||||
|
||||
defp put_into_cache(key, value) do |
||||
ConCache.put(@cache_name, key, value) |
||||
end |
||||
|
||||
defp current_time do |
||||
utc_now = DateTime.utc_now() |
||||
|
||||
DateTime.to_unix(utc_now, :millisecond) |
||||
end |
||||
end |
@ -0,0 +1,139 @@ |
||||
defmodule Explorer.Chain.Supply.RSKTest do |
||||
use Explorer.DataCase |
||||
|
||||
alias Explorer.Chain.Supply.RSK |
||||
alias Explorer.Chain.Wei |
||||
|
||||
@coin_address "0x0000000000000000000000000000000001000006" |
||||
|
||||
defp wei!(value) do |
||||
{:ok, wei} = Wei.cast(value) |
||||
wei |
||||
end |
||||
|
||||
test "total is 21_000_000" do |
||||
assert RSK.total() == 21_000_000 |
||||
end |
||||
|
||||
describe "circulating/0" do |
||||
test "with no balance" do |
||||
assert RSK.circulating() == wei!(0) |
||||
end |
||||
|
||||
test "with a balance" do |
||||
address = insert(:address, hash: @coin_address) |
||||
insert(:block, number: 0) |
||||
|
||||
insert(:fetched_balance, value: 10, address_hash: address.hash, block_number: 0) |
||||
|
||||
assert RSK.circulating() == wei!(10) |
||||
end |
||||
end |
||||
|
||||
defp date(now, shift \\ []) do |
||||
now |
||||
|> Timex.shift(shift) |
||||
|> Timex.to_date() |
||||
end |
||||
|
||||
defp dec(number) do |
||||
Decimal.new(number) |
||||
end |
||||
|
||||
describe "supply_for_days/1" do |
||||
test "when there is no balance" do |
||||
now = Timex.now() |
||||
|
||||
assert RSK.supply_for_days(2) == |
||||
{:ok, |
||||
%{ |
||||
date(now, days: -2) => dec(0), |
||||
date(now, days: -1) => dec(0), |
||||
date(now) => dec(0) |
||||
}} |
||||
end |
||||
|
||||
test "when there is a single balance before the days, that balance is used" do |
||||
address = insert(:address, hash: @coin_address) |
||||
now = Timex.now() |
||||
|
||||
insert(:block, number: 0, timestamp: Timex.shift(now, days: -10)) |
||||
|
||||
insert(:fetched_balance, value: 10, address_hash: address.hash, block_number: 0) |
||||
|
||||
assert RSK.supply_for_days(2) == |
||||
{:ok, |
||||
%{ |
||||
date(now, days: -2) => dec(10), |
||||
date(now, days: -1) => dec(10), |
||||
date(now) => dec(10) |
||||
}} |
||||
end |
||||
|
||||
test "when there is a balance for one of the days, days after it use that balance" do |
||||
address = insert(:address, hash: @coin_address) |
||||
now = Timex.now() |
||||
|
||||
insert(:block, number: 0, timestamp: Timex.shift(now, days: -10)) |
||||
insert(:block, number: 1, timestamp: Timex.shift(now, days: -1)) |
||||
|
||||
insert(:fetched_balance, value: 10, address_hash: address.hash, block_number: 0) |
||||
|
||||
insert(:fetched_balance, value: 20, address_hash: address.hash, block_number: 1) |
||||
|
||||
assert RSK.supply_for_days(2) == |
||||
{:ok, |
||||
%{ |
||||
date(now, days: -2) => dec(10), |
||||
date(now, days: -1) => dec(20), |
||||
date(now) => dec(20) |
||||
}} |
||||
end |
||||
|
||||
test "when there is a balance for the first day, that balance is used" do |
||||
address = insert(:address, hash: @coin_address) |
||||
now = Timex.now() |
||||
|
||||
insert(:block, number: 0, timestamp: Timex.shift(now, days: -10)) |
||||
insert(:block, number: 1, timestamp: Timex.shift(now, days: -2)) |
||||
insert(:block, number: 2, timestamp: Timex.shift(now, days: -1)) |
||||
|
||||
insert(:fetched_balance, value: 5, address_hash: address.hash, block_number: 0) |
||||
|
||||
insert(:fetched_balance, value: 10, address_hash: address.hash, block_number: 1) |
||||
|
||||
insert(:fetched_balance, value: 20, address_hash: address.hash, block_number: 2) |
||||
|
||||
assert RSK.supply_for_days(2) == |
||||
{:ok, |
||||
%{ |
||||
date(now, days: -2) => dec(10), |
||||
date(now, days: -1) => dec(20), |
||||
date(now) => dec(20) |
||||
}} |
||||
end |
||||
|
||||
test "when there is a balance for all days, they are each used correctly" do |
||||
address = insert(:address, hash: @coin_address) |
||||
now = Timex.now() |
||||
|
||||
insert(:block, number: 0, timestamp: Timex.shift(now, days: -10)) |
||||
insert(:block, number: 1, timestamp: Timex.shift(now, days: -2)) |
||||
insert(:block, number: 2, timestamp: Timex.shift(now, days: -1)) |
||||
insert(:block, number: 3, timestamp: now) |
||||
|
||||
insert(:fetched_balance, value: 5, address_hash: address.hash, block_number: 0) |
||||
insert(:fetched_balance, value: 10, address_hash: address.hash, block_number: 1) |
||||
insert(:fetched_balance, value: 20, address_hash: address.hash, block_number: 2) |
||||
insert(:fetched_balance, value: 30, address_hash: address.hash, block_number: 3) |
||||
|
||||
assert RSK.supply_for_days(2) == |
||||
{:ok, |
||||
%{ |
||||
date(now, days: -2) => dec(10), |
||||
date(now, days: -1) => dec(20), |
||||
date(now) => dec(30) |
||||
}} |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,90 @@ |
||||
defmodule Explorer.Market.MarketHistoryCacheTest do |
||||
use Explorer.DataCase |
||||
|
||||
alias Explorer.Market |
||||
alias Explorer.Market.MarketHistoryCache |
||||
|
||||
setup do |
||||
Supervisor.terminate_child(Explorer.Supervisor, {ConCache, MarketHistoryCache.cache_name()}) |
||||
Supervisor.restart_child(Explorer.Supervisor, {ConCache, MarketHistoryCache.cache_name()}) |
||||
|
||||
on_exit(fn -> |
||||
Supervisor.terminate_child(Explorer.Supervisor, {ConCache, Explorer.Chain.BlocksCache.cache_name()}) |
||||
Supervisor.restart_child(Explorer.Supervisor, {ConCache, Explorer.Chain.BlocksCache.cache_name()}) |
||||
end) |
||||
|
||||
:ok |
||||
end |
||||
|
||||
describe "fetch/1" do |
||||
test "caches data on the first call" do |
||||
today = Date.utc_today() |
||||
|
||||
records = |
||||
for i <- 0..29 do |
||||
%{ |
||||
date: Timex.shift(today, days: i * -1), |
||||
closing_price: Decimal.new(1), |
||||
opening_price: Decimal.new(1) |
||||
} |
||||
end |
||||
|
||||
Market.bulk_insert_history(records) |
||||
|
||||
refute fetch_data() |
||||
|
||||
assert Enum.count(MarketHistoryCache.fetch()) == 30 |
||||
|
||||
assert fetch_data() == records |
||||
end |
||||
|
||||
test "updates cache if cache is stale" do |
||||
today = Date.utc_today() |
||||
|
||||
stale_records = |
||||
for i <- 0..29 do |
||||
%{ |
||||
date: Timex.shift(today, days: i * -1), |
||||
closing_price: Decimal.new(1), |
||||
opening_price: Decimal.new(1) |
||||
} |
||||
end |
||||
|
||||
Market.bulk_insert_history(stale_records) |
||||
|
||||
MarketHistoryCache.fetch() |
||||
|
||||
stale_updated_at = fetch_updated_at() |
||||
|
||||
assert fetch_data() == stale_records |
||||
|
||||
ConCache.put(MarketHistoryCache.cache_name(), MarketHistoryCache.updated_at_key(), 1) |
||||
|
||||
fetch_data() |
||||
|
||||
assert stale_updated_at != fetch_updated_at() |
||||
end |
||||
end |
||||
|
||||
defp fetch_updated_at do |
||||
ConCache.get(MarketHistoryCache.cache_name(), MarketHistoryCache.updated_at_key()) |
||||
end |
||||
|
||||
defp fetch_data do |
||||
MarketHistoryCache.cache_name() |
||||
|> ConCache.get(MarketHistoryCache.data_key()) |
||||
|> case do |
||||
nil -> |
||||
nil |
||||
|
||||
records -> |
||||
Enum.map(records, fn record -> |
||||
%{ |
||||
date: record.date, |
||||
closing_price: record.closing_price, |
||||
opening_price: record.opening_price |
||||
} |
||||
end) |
||||
end |
||||
end |
||||
end |
@ -1,3 +1,3 @@ |
||||
<!-- faq.md --> |
||||
|
||||
_Coming Soon_ |
||||
FAQs are located in the [BlockScout forum](https://forum.poa.network/c/blockscout/wiki). |
@ -0,0 +1,3 @@ |
||||
## Upgrading Guide |
||||
|
||||
**Upgrade instructions are in progress. If you need assistance with an upgrade, please contact us through the [forum](https://forum.poa.network/c/blockscout) or [gitter](https://gitter.im/poanetwork/blockscout) channel.** |
Before Width: | Height: | Size: 683 KiB |
Before Width: | Height: | Size: 840 KiB |
Loading…
Reference in new issue