Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Add node shared secrets chain explorer#411 #581

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions lib/archethic/db/embedded_impl/chain_index.ex
Original file line number Diff line number Diff line change
Expand Up @@ -370,9 +370,15 @@ defmodule Archethic.DB.EmbeddedImpl.ChainIndex do

filename = chain_addresses_path(db_path, genesis_address)

:ok = File.write!(filename, encoded_data, [:binary, :append])
true = :ets.insert(:archethic_db_last_index, {genesis_address, new_address, unix_time})
:ok
case :ets.lookup(:archethic_db_last_index, genesis_address) do
[{_, ^new_address, _}] ->
:ok

_ ->
:ok = File.write!(filename, encoded_data, [:binary, :append])
true = :ets.insert(:archethic_db_last_index, {genesis_address, new_address, unix_time})
:ok
end
end

@doc """
Expand Down
191 changes: 191 additions & 0 deletions lib/archethic_web/live/chains/node_shared_secrets_live.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
defmodule ArchethicWeb.NodeSharedSecretsChainLive do
@moduledoc false

use ArchethicWeb, :live_view

alias Archethic.{
TransactionChain,
TransactionChain.Transaction,
TransactionChain.TransactionData,
TransactionChain.TransactionData.Ownership,
PubSub,
SharedSecrets
}

alias ArchethicWeb.{ExplorerView, WebUtils}
alias Phoenix.{LiveView, View}

@display_limit 10
@txn_type :node_shared_secrets

@spec mount(_parameters :: map(), _session :: map(), socket :: LiveView.Socket.t()) ::
{:ok, LiveView.Socket.t()}
def mount(_parameters, _session, socket) do
if connected?(socket) do
PubSub.register_to_new_transaction_by_type(@txn_type)
end

tx_count = TransactionChain.count_transactions_by_type(@txn_type)

socket =
case SharedSecrets.genesis_address(@txn_type) do
nil ->
socket
|> assign(:tx_count, 0)
|> assign(:nb_pages, 0)
|> assign(:nb_authorized_nodes, 0)
|> assign(:current_page, 1)
|> assign(:transactions, [])

address when is_binary(address) ->
nb_authorized_nodes =
address
|> TransactionChain.get_last_address()
|> elem(0)
|> nb_of_authorized_keys()

socket
|> assign(:tx_count, tx_count)
|> assign(:nb_pages, WebUtils.total_pages(tx_count))
|> assign(:nb_authorized_nodes, nb_authorized_nodes)
|> assign(:current_page, 1)
|> assign(:transactions, transactions_from_page(1, tx_count))
end

{:ok, socket}
end

@spec render(assigns :: LiveView.Socket.assigns()) :: LiveView.Rendered.t()
def render(assigns) do
View.render(ExplorerView, "node_shared_secrets_chain_index.html", assigns)
end

@spec handle_params(_params :: map(), _uri :: binary(), socket :: LiveView.Socket.t()) ::
{:noreply, LiveView.Socket.t()}
def handle_params(
_params = %{"page" => page},
_uri,
socket = %{assigns: %{nb_pages: nb_pages, tx_count: tx_count}}
) do
case Integer.parse(page) do
{number, ""} when number < 1 and number > nb_pages ->
{:noreply, push_patch(socket, to: Routes.live_path(socket, __MODULE__, %{"page" => 1}))}

{number, ""} when number >= 1 and number <= nb_pages ->
socket =
socket
|> assign(:current_page, number)
|> assign(:transactions, transactions_from_page(number, tx_count))

{:noreply, socket}

_ ->
{:noreply, socket}
end
end

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

@spec handle_event(_event :: binary(), _params :: map(), socket :: LiveView.Socket.t()) ::
{:noreply, LiveView.Socket.t()}
def handle_event(_event = "next_page", _params = %{"page" => page}, socket) do
{:noreply, push_patch(socket, to: Routes.live_path(socket, __MODULE__, %{"page" => page}))}
end

def handle_event(_event = "prev_page", _params = %{"page" => page}, socket) do
{:noreply, push_patch(socket, to: Routes.live_path(socket, __MODULE__, %{"page" => page}))}
end

@spec handle_info(
_msg ::
{:new_transaction, address :: binary(), :node_shared_secrets, DateTime.t()},
socket :: LiveView.Socket.t()
) ::
{:noreply, LiveView.Socket.t()}
def handle_info(
_msg = {:new_transaction, address, :node_shared_secrets, timestamp},
socket = %{assigns: %{current_page: current_page, tx_count: tx_count}}
) do
updated_socket =
case current_page do
1 ->
nb_auth_nodes = nb_of_authorized_keys(address)

socket
|> update(
:transactions,
fn tx_list ->
[display_data(address, nb_auth_nodes, timestamp) | tx_list]
|> Enum.take(@display_limit)
end
)
|> assign(:tx_count, tx_count + 1)
|> assign(:nb_authorized_nodes, nb_auth_nodes)
|> assign(:current_page, 1)
|> assign(:nb_pages, WebUtils.total_pages(tx_count + 1))

_ ->
socket
end

{:noreply, updated_socket}
end

@spec transactions_from_page(current_page :: non_neg_integer(), tx_count :: non_neg_integer()) ::
list(map())
def transactions_from_page(current_page, tx_count) do
nb_drops = tx_count - current_page * @display_limit

{nb_drops, display_limit} =
if nb_drops < 0, do: {0, @display_limit + nb_drops}, else: {nb_drops, @display_limit}

case SharedSecrets.genesis_address(@txn_type) do
address when is_binary(address) ->
address
|> TransactionChain.list_chain_addresses()
|> Stream.drop(nb_drops)
|> Stream.take(display_limit)
|> Stream.map(fn {addr, timestamp} ->
nb_authorized_nodes = nb_of_authorized_keys(addr)

display_data(
addr,
nb_authorized_nodes,
DateTime.from_unix(timestamp, :millisecond) |> elem(1)
)
end)
|> Enum.reverse()

_ ->
[]
end
end

@spec nb_of_authorized_keys(address :: binary()) :: non_neg_integer()
defp nb_of_authorized_keys(address) do
Neylix marked this conversation as resolved.
Show resolved Hide resolved
with {:ok, %Transaction{data: %TransactionData{ownerships: ownerships}}} <-
TransactionChain.get_transaction(address, data: [:ownerships]),
%Ownership{authorized_keys: authorized_keys} <- Enum.at(ownerships, 0) do
Enum.count(authorized_keys)
else
_ -> 1
end
end

@spec display_data(
address :: binary(),
nb_authorized_nodes :: non_neg_integer(),
timestamp :: DateTime.t()
) ::
map()
defp display_data(address, nb_authorized_nodes, timestamp) do
%{
address: address,
type: @txn_type,
timestamp: timestamp,
nb_authorized_nodes: nb_authorized_nodes
}
end
end
82 changes: 32 additions & 50 deletions lib/archethic_web/live/chains/reward_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ defmodule ArchethicWeb.RewardChainLive do

use ArchethicWeb, :live_view

alias ArchethicWeb.{ExplorerView}
alias ArchethicWeb.{ExplorerView, WebUtils}
alias Phoenix.{View}

@display_limit 10
Expand All @@ -29,7 +29,7 @@ defmodule ArchethicWeb.RewardChainLive do
socket =
socket
|> assign(:tx_count, tx_count)
|> assign(:nb_pages, total_pages(tx_count))
|> assign(:nb_pages, WebUtils.total_pages(tx_count))
|> assign(:current_page, 1)
|> assign(:transactions, transactions_from_page(1, tx_count))

Expand All @@ -49,11 +49,10 @@ defmodule ArchethicWeb.RewardChainLive do
socket = %{assigns: %{nb_pages: nb_pages, tx_count: tx_count}}
) do
case Integer.parse(page) do
{number, ""} when number > 0 and number > nb_pages ->
{:noreply,
push_redirect(socket, to: Routes.live_path(socket, __MODULE__, %{"page" => 1}))}
{number, ""} when number < 1 and number > nb_pages ->
{:noreply, push_patch(socket, to: Routes.live_path(socket, __MODULE__, %{"page" => 1}))}

{number, ""} when number > 0 and number <= nb_pages ->
{number, ""} when number >= 1 and number <= nb_pages ->
socket =
socket
|> assign(:current_page, number)
Expand All @@ -73,11 +72,11 @@ defmodule ArchethicWeb.RewardChainLive do
@spec handle_event(binary(), map(), Phoenix.LiveView.Socket.t()) ::
{:noreply, Phoenix.LiveView.Socket.t()}
def handle_event(_event = "prev_page", %{"page" => page}, socket) do
{:noreply, push_redirect(socket, to: Routes.live_path(socket, __MODULE__, %{"page" => page}))}
{:noreply, push_patch(socket, to: Routes.live_path(socket, __MODULE__, %{"page" => page}))}
end

def handle_event(_event = "next_page", %{"page" => page}, socket) do
{:noreply, push_redirect(socket, to: Routes.live_path(socket, __MODULE__, %{"page" => page}))}
{:noreply, push_patch(socket, to: Routes.live_path(socket, __MODULE__, %{"page" => page}))}
end

@spec handle_info(
Expand All @@ -92,25 +91,24 @@ defmodule ArchethicWeb.RewardChainLive do
{:noreply, handle_new_transaction({address, type, timestamp}, socket)}
end

@spec handle_new_transaction(
{address :: binary(), type :: :mint_rewards | :node_rewards, timestamp :: DateTime.t()},
socket :: Phoenix.LiveView.Socket.t()
) :: Phoenix.LiveView.Socket.t()
def handle_new_transaction(
{address, type, timestamp},
socket = %{assigns: %{current_page: current_page, transactions: txs, tx_count: tx_count}}
socket = %{assigns: %{current_page: current_page, tx_count: tx_count}}
) do
display_txs = Enum.count(txs)

case current_page do
1 when display_txs < @display_limit ->
socket
|> assign(:tx_count, tx_count + 1)
|> assign(:current_page, 1)
|> update(:transactions, &[%{address: address, type: type, timestamp: timestamp} | &1])

1 when display_txs >= @display_limit ->
1 ->
socket
|> update(:transactions, fn tx_list ->
[display_data(address, type, timestamp) | tx_list]
|> Enum.take(@display_limit)
end)
|> assign(:tx_count, tx_count + 1)
|> assign(:nb_pages, total_pages(tx_count + 1))
|> assign(:current_page, 1)
|> assign(:transactions, [%{address: address, type: type, timestamp: timestamp}])
|> assign(:nb_pages, WebUtils.total_pages(tx_count + 1))

_ ->
socket
Expand All @@ -131,11 +129,11 @@ defmodule ArchethicWeb.RewardChainLive do
|> Stream.drop(nb_drops)
|> Stream.take(display_limit)
|> Stream.map(fn {addr, timestamp} ->
%{
address: addr,
type: (TransactionChain.get_transaction(addr, [:type]) |> elem(1)).type,
timestamp: DateTime.from_unix(timestamp, :millisecond) |> elem(1)
}
display_data(
addr,
(TransactionChain.get_transaction(addr, [:type]) |> elem(1)).type,
DateTime.from_unix(timestamp, :millisecond) |> elem(1)
)
end)
|> Enum.reverse()

Expand All @@ -144,29 +142,13 @@ defmodule ArchethicWeb.RewardChainLive do
end
end

@doc """
Nb of pages required to display all the transactions.

## Examples
iex> total_pages(45)
5
iex> total_pages(40)
4
iex> total_pages(1)
1
iex> total_pages(10)
1
iex> total_pages(11)
2
iex> total_pages(0)
0
"""
@spec total_pages(tx_count :: non_neg_integer()) ::
non_neg_integer()
def total_pages(tx_count) when rem(tx_count, @display_limit) == 0,
do: count_pages(tx_count)

def total_pages(tx_count), do: count_pages(tx_count) + 1

def count_pages(tx_count), do: div(tx_count, @display_limit)
@spec display_data(
address :: binary(),
type :: :node_rewards | :mint_rewards,
timestamp :: DateTime.t()
) ::
map()
defp display_data(address, type, timestamp) do
%{address: address, type: type, timestamp: timestamp}
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 @@ -53,6 +53,7 @@ defmodule ArchethicWeb.Router do
live("/chain/oracle", OracleChainLive)
live("/chain/beacon", BeaconChainLive)
live("/chain/rewards", RewardChainLive)
live("/chain/node_shared_secrets", NodeSharedSecretsChainLive)

live("/nodes", NodeListLive)
live("/nodes/worldmap", WorldMapLive)
Expand Down
Loading