Skip to content

Commit

Permalink
Integrate quorum queries in the top archethic module
Browse files Browse the repository at this point in the history
  • Loading branch information
Samuel committed Jun 9, 2022
1 parent ab34fef commit 86c07c3
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 161 deletions.
214 changes: 55 additions & 159 deletions lib/archethic.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,13 @@ defmodule Archethic do
alias __MODULE__.P2P

alias __MODULE__.P2P.Message.Balance
alias __MODULE__.P2P.Message.Error
alias __MODULE__.P2P.Message.GetBalance
alias __MODULE__.P2P.Message.GetLastTransaction
alias __MODULE__.P2P.Message.GetLastTransactionAddress
alias __MODULE__.P2P.Message.GetTransaction
alias __MODULE__.P2P.Message.GetTransactionChain
alias __MODULE__.P2P.Message.GetTransactionChainLength
alias __MODULE__.P2P.Message.GetTransactionInputs
alias __MODULE__.P2P.Message.LastTransactionAddress
alias __MODULE__.P2P.Message.NewTransaction
alias __MODULE__.P2P.Message.NotFound
alias __MODULE__.P2P.Message.Ok
alias __MODULE__.P2P.Message.StartMining
alias __MODULE__.P2P.Message.TransactionChainLength
alias __MODULE__.P2P.Message.TransactionInputList
alias __MODULE__.P2P.Message.TransactionList
alias __MODULE__.P2P.Node

alias __MODULE__.TransactionChain
alias __MODULE__.TransactionChain.Transaction
alias __MODULE__.TransactionChain.TransactionInput

Expand All @@ -47,30 +36,14 @@ defmodule Archethic do
def search_transaction(address) when is_binary(address) do
storage_nodes = Election.chain_storage_nodes(address, P2P.available_nodes())

storage_nodes
|> P2P.nearest_nodes()
|> Enum.filter(&Node.locally_available?/1)
|> get_transaction(address)
end

defp get_transaction([node | rest], address) do
case P2P.send_message(node, %GetTransaction{address: address}) do
{:ok, tx = %Transaction{}} ->
{:ok, tx}

{:ok, %NotFound{}} ->
{:error, :transaction_not_exists}

{:ok, %Error{}} ->
{:error, :transaction_invalid}
nodes =
storage_nodes
|> P2P.nearest_nodes()
|> Enum.filter(&Node.locally_available?/1)

{:error, _} ->
get_transaction(rest, address)
end
TransactionChain.fetch_transaction_remotely(address, nodes)
end

defp get_transaction([], _), do: {:error, :network_issue}

@doc """
Send a new transaction in the network to be mined. The current node will act as welcome node
"""
Expand Down Expand Up @@ -138,61 +111,32 @@ defmodule Archethic do
| {:error, :transaction_not_exists}
| {:error, :transaction_invalid}
| {:error, :network_issue}
def get_last_transaction(address) do
address
|> Election.chain_storage_nodes(P2P.available_nodes())
|> P2P.nearest_nodes()
|> Enum.filter(&Node.locally_available?/1)
|> get_last_transaction(address)
end

defp get_last_transaction([node | rest], address) do
case P2P.send_message(node, %GetLastTransaction{address: address}) do
{:ok, tx = %Transaction{}} ->
{:ok, tx}

{:ok, %NotFound{}} ->
{:error, :transaction_not_exists}

{:ok, %Error{}} ->
{:error, :transaction_invalid}

{:error, _} ->
get_last_transaction(rest, address)
def get_last_transaction(address) when is_binary(address) do
case get_last_transaction_address(address) do
{:ok, last_address} ->
nodes =
last_address
|> Election.chain_storage_nodes(P2P.available_nodes())
|> P2P.nearest_nodes()
|> Enum.filter(&Node.locally_available?/1)

TransactionChain.fetch_transaction_remotely(last_address, nodes)

{:error, :network_issue} = e ->
e
end
end

defp get_last_transaction([], _), do: {:error, :network_issue}

@doc """
Retrieve the last transaction address for a chain from the closest nodes
"""
@spec get_last_transaction_address(address :: binary()) ::
{:ok, binary()}
| {:error, :network_issue}
def get_last_transaction_address(address) do
address
|> Election.chain_storage_nodes(P2P.available_nodes())
|> P2P.nearest_nodes()
|> Enum.filter(&Node.locally_available?/1)
|> get_last_transaction_address(address)
def get_last_transaction_address(address) when is_binary(address) do
TransactionChain.resolve_last_address(address)
end

defp get_last_transaction_address([node | rest], address) do
case P2P.send_message(node, %GetLastTransactionAddress{
address: address,
timestamp: DateTime.utc_now()
}) do
{:ok, %LastTransactionAddress{address: last_address}} ->
{:ok, last_address}

{:error, _} ->
get_last_transaction_address(rest, address)
end
end

defp get_last_transaction_address([], _), do: {:error, :network_issue}

@doc """
Retrieve the balance from an address from the closest nodes
"""
Expand Down Expand Up @@ -223,117 +167,69 @@ defmodule Archethic do
@spec get_transaction_inputs(binary()) ::
{:ok, list(TransactionInput.t())} | {:error, :network_issue}
def get_transaction_inputs(address) when is_binary(address) do
address
|> Election.chain_storage_nodes(P2P.available_nodes())
|> P2P.nearest_nodes()
|> Enum.filter(&Node.locally_available?/1)
|> get_transaction_inputs(address)
end

defp get_transaction_inputs([node | rest], address) do
case P2P.send_message(node, %GetTransactionInputs{address: address}) do
{:ok, %TransactionInputList{inputs: inputs}} ->
{:ok, inputs}
nodes =
address
|> Election.chain_storage_nodes(P2P.available_nodes())
|> P2P.nearest_nodes()
|> Enum.filter(&Node.locally_available?/1)

{:error, _} ->
get_transaction_inputs(rest, address)
end
TransactionChain.fetch_inputs_remotely(address, nodes, DateTime.utc_now())
end

defp get_transaction_inputs([], _), do: {:error, :network_issue}

@doc """
Retrieve a transaction chain based on an address from the closest nodes
"""
@spec get_transaction_chain(binary()) :: {:ok, list(Transaction.t())} | {:error, :network_issue}
def get_transaction_chain(address) when is_binary(address) do
local_available_nodes = locally_available_nodes(address)
get_transaction_chain(local_available_nodes, address)
end

defp get_transaction_chain(nodes, address, opts \\ [], acc \\ [])

defp get_transaction_chain([node | rest], address, opts, acc) do
case P2P.send_message(node, %GetTransactionChain{
address: address,
paging_state: Keyword.get(opts, :paging_state)
}) do
{:ok, %TransactionList{transactions: transactions, more?: false}} ->
{:ok, Enum.uniq_by(acc ++ transactions, & &1.address)}

{:ok, %TransactionList{transactions: transactions, more?: true, paging_state: paging_state}} ->
get_transaction_chain(
[node | rest],
address,
[paging_state: paging_state],
Enum.uniq_by(acc ++ transactions, & &1.address)
)
nodes =
address
|> Election.chain_storage_nodes(P2P.available_nodes())
|> P2P.nearest_nodes()
|> Enum.filter(&Node.locally_available?/1)

{:error, _} ->
get_transaction_chain(rest, address, opts, acc)
try do
chain = TransactionChain.stream_remotely(address, nodes)
{:ok, Enum.to_list(chain)}
catch
_ ->
{:error, :network_issue}
end
end

defp get_transaction_chain([], _, _, _), do: {:error, :network_issue}

defp locally_available_nodes(address) do
address
|> Election.chain_storage_nodes(P2P.available_nodes())
|> P2P.nearest_nodes()
|> Enum.filter(&Node.locally_available?/1)
end

@doc """
Retrieve a transaction chain based on an address from the closest nodes
by setting `paging_address as an offset address.
"""
@spec get_transaction_chain_by_paging_address(binary(), binary()) ::
{:ok, list(Transaction.t())} | {:error, :network_issue}
def get_transaction_chain_by_paging_address(address, paging_address) when is_binary(address) do
options = [paging_state: paging_address]
local_available_nodes = locally_available_nodes(address)
transaction_chain_by_paging_address(local_available_nodes, address, options)
end

defp transaction_chain_by_paging_address([node | rest], address, options) do
case P2P.send_message(node, %GetTransactionChain{
address: address,
paging_state: Keyword.get(options, :paging_state)
}) do
{:ok, %TransactionList{transactions: transactions}} ->
{:ok, transactions}
nodes =
address
|> Election.chain_storage_nodes(P2P.available_nodes())
|> P2P.nearest_nodes()
|> Enum.filter(&Node.locally_available?/1)

{:error, _} ->
transaction_chain_by_paging_address(rest, address, options)
try do
chain = TransactionChain.stream_remotely(address, nodes, paging_address)
{:ok, Enum.to_list(chain)}
catch
_ ->
{:error, :network_issue}
end
end

defp transaction_chain_by_paging_address([], _address, _options) do
{:error, :network_issue}
end

@doc """
Retrieve the number of transaction in a transaction chain from the closest nodes
"""
@spec get_transaction_chain_length(binary()) ::
{:ok, non_neg_integer()} | {:error, :network_issue}
def get_transaction_chain_length(address) when is_binary(address) do
address
|> Election.chain_storage_nodes(P2P.available_nodes())
|> P2P.nearest_nodes()
|> Enum.filter(&Node.locally_available?/1)
|> get_transaction_chain_length(address)
end

defp get_transaction_chain_length([node | rest], address) do
case P2P.send_message(node, %GetTransactionChainLength{address: address}) do
{:ok, %TransactionChainLength{length: length}} ->
{:ok, length}
nodes =
address
|> Election.chain_storage_nodes(P2P.available_nodes())
|> P2P.nearest_nodes()
|> Enum.filter(&Node.locally_available?/1)

{:error, _} ->
get_transaction_chain_length(rest, address)
end
TransactionChain.fetch_size_remotely(address, nodes)
end

defp get_transaction_chain_length([], _), do: {:error, :network_issue}
end
4 changes: 2 additions & 2 deletions lib/archethic_web/controllers/explorer_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ defmodule ArchethicWeb.ExplorerController do
{:ok, %{uco: uco_balance}} <- Archethic.get_balance(addr),
uco_price <- DateTime.utc_now() |> OracleChain.get_uco_price() do
render(conn, "chain.html",
transaction_chain: chain,
transaction_chain: List.flatten(chain),
chain_size: Enum.count(chain),
address: addr,
uco_balance: uco_balance,
Expand Down Expand Up @@ -92,7 +92,7 @@ defmodule ArchethicWeb.ExplorerController do
{:ok, %{uco: uco_balance}} <- Archethic.get_balance(addr),
uco_price <- DateTime.utc_now() |> OracleChain.get_uco_price() do
render(conn, "chain.html",
transaction_chain: chain,
transaction_chain: List.flatten(chain),
address: addr,
chain_size: Enum.count(chain),
uco_balance: uco_balance,
Expand Down

0 comments on commit 86c07c3

Please sign in to comment.