Skip to content

Commit

Permalink
Added Beacon Explorer (#76)
Browse files Browse the repository at this point in the history
* Create interface to fetch the transactions from the BeaconChain (chain where all the transaction are listed) and to split by days using pages

* Added UTC to `format_date` helper
  • Loading branch information
imnik11 committed Sep 21, 2021
1 parent 39e466d commit 3bd600d
Show file tree
Hide file tree
Showing 9 changed files with 242 additions and 9 deletions.
23 changes: 21 additions & 2 deletions lib/archethic/beacon_chain/summary_timer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ defmodule ArchEthic.BeaconChain.SummaryTimer do
"""

use GenServer

alias Crontab.CronExpression.Parser, as: CronParser
alias Crontab.DateChecker
alias Crontab.Scheduler, as: CronScheduler
alias ArchEthic.PubSub
alias ArchEthic.Utils

@doc """
Create a new summary timer
Expand Down Expand Up @@ -78,7 +79,7 @@ defmodule ArchEthic.BeaconChain.SummaryTimer do
interval = Keyword.get(opts, :interval)
:ets.new(:archethic_summary_timer, [:named_table, :public, read_concurrency: true])
:ets.insert(:archethic_summary_timer, {:interval, interval})

schedule_next_summary_time(interval)
{:ok, %{interval: interval}, :hibernate}
end

Expand All @@ -98,6 +99,24 @@ defmodule ArchEthic.BeaconChain.SummaryTimer do
end
end

def handle_info(
:next_summary_time,
state = %{
interval: interval
}
) do
timer = schedule_next_summary_time(interval)

slot_time = DateTime.utc_now() |> Utils.truncate_datetime()

PubSub.notify_next_summary_time(next_summary(slot_time))
{:noreply, Map.put(state, :timer, timer), :hibernate}
end

defp schedule_next_summary_time(interval) do
Process.send_after(self(), :next_summary_time, Utils.time_offset(interval) * 1000)
end

def config_change(nil), do: :ok

def config_change(conf) do
Expand Down
15 changes: 15 additions & 0 deletions lib/archethic/pub_sub.ex
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ defmodule ArchEthic.PubSub do
dispatch(:new_oracle_data, {:new_oracle_data, data})
end

@doc """
Notify next summary time beacon chain to the subscribers
"""
def notify_next_summary_time(date = %DateTime{}) do
dispatch(:next_summary_time, {:next_summary_time, date})
end

@doc """
Register a process to a new transaction publication by type
"""
Expand Down Expand Up @@ -131,6 +138,14 @@ defmodule ArchEthic.PubSub do
Registry.register(PubSubRegistry, :new_transaction_number, [])
end

@doc """
Register a process to sent next summary time of beacon summary
"""
@spec register_to_next_summary_time :: {:ok, pid()}
def register_to_next_summary_time do
Registry.register(PubSubRegistry, :next_summary_time, [])
end

@doc """
Register to a new oracle data
"""
Expand Down
6 changes: 3 additions & 3 deletions lib/archethic/self_repair/sync/beacon_summary_handler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ defmodule ArchEthic.SelfRepair.Sync.BeaconSummaryHandler do
|> Stream.map(fn {:ok, {:ok, summary}} -> summary end)
end

defp download_summary(beacon_address, nodes, patch, prev_result \\ nil)
def download_summary(beacon_address, nodes, patch, prev_result \\ nil)

defp download_summary(_beacon_address, [], _, %NotFound{}), do: {:ok, %NotFound{}}
def download_summary(_beacon_address, [], _, %NotFound{}), do: {:ok, %NotFound{}}

defp download_summary(beacon_address, nodes, patch, prev_result) do
def download_summary(beacon_address, nodes, patch, prev_result) do
case P2P.reply_first(nodes, %GetBeaconSummary{address: beacon_address},
patch: patch,
node_ack?: true
Expand Down
138 changes: 138 additions & 0 deletions lib/archethic_web/live/chains/beacon_live.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
defmodule ArchEthicWeb.BeaconChainLive do
@moduledoc false
use ArchEthicWeb, :live_view

alias ArchEthic.BeaconChain
alias ArchEthic.BeaconChain.Summary, as: BeaconSummary
alias ArchEthic.BeaconChain.SummaryTimer
alias ArchEthic.Crypto
alias ArchEthic.Election
alias ArchEthic.P2P
alias ArchEthic.P2P.Node
alias ArchEthic.PubSub
alias ArchEthic.SelfRepair.Sync.BeaconSummaryHandler
alias ArchEthicWeb.ExplorerView
alias Phoenix.View

defp list_transaction_by_date(date = %DateTime{}) do
Enum.map(BeaconChain.list_subsets(), fn subset ->
b_address = Crypto.derive_beacon_chain_address(subset, date, true)
node_list = P2P.authorized_nodes()
nodes = Election.beacon_storage_nodes(subset, date, node_list)
%Node{network_patch: patch} = P2P.get_node_info()

{b_address, nodes, patch}
end)
|> Task.async_stream(
fn {address, nodes, patch} ->
BeaconSummaryHandler.download_summary(address, nodes, patch)
end,
on_timeout: :kill_task,
max_concurrency: 256
)
|> Stream.filter(&match?({:ok, {:ok, %BeaconSummary{}}}, &1))
|> Stream.flat_map(fn {:ok,
{:ok, %BeaconSummary{transaction_summaries: transaction_summaries}}} ->
transaction_summaries
end)
end

defp list_transaction_by_date(nil), do: []

def mount(_params, _session, socket) do
next_summary_time = BeaconChain.next_summary_date(DateTime.utc_now())

if connected?(socket) do
PubSub.register_to_next_summary_time()
end

beacon_dates = get_beacon_dates()

new_assign =
socket
|> assign(:next_summary_time, next_summary_time)
|> assign(:dates, beacon_dates)
|> assign(:current_date_page, 1)
|> assign(
:transactions,
list_transaction_by_date(Enum.at(beacon_dates, 0))
)

{:ok, new_assign}
end

def render(assigns) do
View.render(ExplorerView, "beacon_chain_index.html", assigns)
end

def handle_params(%{"page" => page}, _uri, socket = %{assigns: %{dates: dates}}) do
case Integer.parse(page) do
{number, ""} when number > 0 and is_list(dates) ->
if number > length(dates) do
{:noreply,
push_redirect(socket, to: Routes.live_path(socket, __MODULE__, %{"page" => 1}))}
else
transactions =
dates
|> Enum.at(number - 1)
|> list_transaction_by_date()

new_assign =
socket
|> assign(:current_date_page, number)
|> assign(:transactions, transactions)

{:noreply, new_assign}
end

_ ->
{:noreply, socket}
end
end

def handle_params(%{}, _, socket) do
{:noreply, socket}
end

@spec handle_event(<<_::32>>, map, Phoenix.LiveView.Socket.t()) ::
{:noreply, Phoenix.LiveView.Socket.t()}
def handle_event("goto", %{"page" => page}, socket) do
{:noreply, push_redirect(socket, to: Routes.live_path(socket, __MODULE__, %{"page" => page}))}
end

def handle_info(
{:next_summary_time, next_summary_date},
socket = %{assigns: %{current_date_page: page, dates: dates}}
) do
new_dates = [next_summary_date | dates]

transactions =
new_dates
|> Enum.at(page - 1)
|> list_transaction_by_date()

new_next_summary =
if :gt == DateTime.compare(next_summary_date, DateTime.utc_now()) do
next_summary_date
else
BeaconChain.next_summary_date(DateTime.utc_now())
end

new_assign =
socket
|> assign(:transactions, transactions)
|> assign(:dates, new_dates)
|> assign(:next_summary_time, new_next_summary)

{:noreply, new_assign}
end

defp get_beacon_dates do
%Node{enrollment_date: enrollment_date} =
P2P.list_nodes() |> Enum.sort_by(& &1.enrollment_date, {:asc, DateTime}) |> Enum.at(0)

enrollment_date
|> SummaryTimer.previous_summaries()
|> Enum.sort({:desc, DateTime})
end
end
1 change: 1 addition & 0 deletions lib/archethic_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ defmodule ArchEthicWeb.Router do

get("/chain", ExplorerController, :chain)
live("/chain/oracle", OracleChainLive)
live("/chain/beacon", BeaconChainLive)

live("/nodes", NodeListLive)
live("/node/:public_key", NodeDetailsLive)
Expand Down
57 changes: 57 additions & 0 deletions lib/archethic_web/templates/explorer/beacon_chain_index.html.leex
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<h1 class="subtitle is-size-4 heading has-text-white">Beacon chain</h2>

<p class="heading is-size-7 has-text-white">Next Beacon Summary Time <span><%= format_date(@next_summary_time) %></span></p>

<div class="columns">
<div class="column">
<nav class="pagination is-right" role="navigation" aria-label="pagination">
<%= if @current_date_page > 1 do %>
<a class="pagination-previous is-outlined has-text-white" phx-value-page="<%= @current_date_page - 1 %>" phx-click="goto">Previous</a>
<% end %>

<%= if @current_date_page + 1 <= Enum.count(@dates) do %>
<a class="pagination-next is-outlined has-text-white" phx-value-page="<%= @current_date_page + 1 %>" phx-click="goto">Next page</a>
<% end %>
<%= if Enum.count(@dates)!= 0 do %>
<p class="pagination-list has-text-white">
Page <%= @current_date_page %> on <%= Enum.count(@dates) %>
</p>
<% end %>
</nav>
</div>
</div>


<div class="columns">
<div class="column">
<div class="box">
<%= if Enum.count(@dates)!= 0 do %>
<p class="heading is-size-6">Transaction chain for <%= format_date(Enum.at(@dates, @current_date_page - 1)) %></p>
<%else %>
<p class="heading is-size-6">Transaction chain for <%= format_date(@next_summary_time) %></p>

<p> There is no transaction yet</p>
<% end %>
<div class="columns mt-6">
<div class="column">
<%= for tx <- @transactions do %>
<div class="columns">
<div class="column is-5-desktop">
<%= link to: Routes.live_path(@socket, ArchEthicWeb.TransactionDetailsLive, Base.encode16(tx.address)) do%>
<span class="text_wrap"><%= Base.encode16(tx.address) %></span>
<% end %>
</div>
<div class="column is-2-desktop">
<%= format_date(tx.timestamp) %>
</div>
<div class="column is-1-desktop">
<span class="tag is-light is-info"><%= tx.type %></span>
</div>
</div>
<% end %>
</div>
</div>
</div>
</div>
</div>
</div>
7 changes: 5 additions & 2 deletions lib/archethic_web/templates/layout/root.html.eex
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

<div id="navbar" class="navbar-menu">
<div class="navbar-end">
<!--
<!--
TODO: implement the listing of transactions through BeaconChain
<a class="navbar-item" href="<%= Routes.live_path(@conn, ArchEthicWeb.TransactionListLive) %>">
Expand All @@ -38,6 +38,9 @@
<a class="navbar-item" href="<%= Routes.live_path(@conn, ArchEthicWeb.OracleChainLive) %>">
Oracle
</a>
<a class="navbar-item" href="<%= Routes.live_path(@conn, ArchEthicWeb.BeaconChainLive) %>">
Beacon
</a>
</div>
</div>

Expand All @@ -58,7 +61,7 @@
</a>
</div>
</div>

<a class="navbar-item" href="https://archethic-foundation.github.io/archethic-docs/" target="_blank">
Docs
</a>
Expand Down
2 changes: 1 addition & 1 deletion lib/archethic_web/views/layout_helpers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ defmodule ArchEthicWeb.LayoutHelpers do
minute: minute,
second: second
}) do
"#{year}/#{zero_pad(month)}/#{zero_pad(day)} #{zero_pad(hour)}:#{zero_pad(minute)}:#{zero_pad(second)}"
"#{year}/#{zero_pad(month)}/#{zero_pad(day)} #{zero_pad(hour)}:#{zero_pad(minute)}:#{zero_pad(second)} UTC"
end

def format_date(nil), do: ""
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule ArchEthic.MixProject do
def project do
[
app: :archethic,
version: "0.11.7",
version: "0.12.0",
build_path: "_build",
config_path: "config/config.exs",
deps_path: "deps",
Expand Down

0 comments on commit 3bd600d

Please sign in to comment.