From 04e4a6bfdf62c963a86ea05c860c88b8c75d7a9c Mon Sep 17 00:00:00 2001 From: prix-uniris <89784724+prix-uniris@users.noreply.github.com> Date: Tue, 2 Aug 2022 20:29:35 +0530 Subject: [PATCH] Improve faucet's control with genesis address (#482) * Added Register to Faucet After Transaction has been received in replication attestation and fetch the address for faucet as genesis address. * Made fetch_genesis_address_remotely public func --- lib/archethic/transaction_chain.ex | 8 ++++- .../controllers/faucet_controller.ex | 33 +++++++++++++++++-- lib/archethic_web/faucet_rate_limiter.ex | 23 +++++++++++++ lib/archethic_web/transaction_subscriber.ex | 14 +++++++- .../controllers/faucet_controller_test.exs | 14 ++++++-- 5 files changed, 85 insertions(+), 7 deletions(-) diff --git a/lib/archethic/transaction_chain.ex b/lib/archethic/transaction_chain.ex index a7a3ca77d..c3003ec7f 100644 --- a/lib/archethic/transaction_chain.ex +++ b/lib/archethic/transaction_chain.ex @@ -853,7 +853,13 @@ defmodule Archethic.TransactionChain do end end - defp fetch_genesis_address_remotely(address) when is_binary(address) do + @doc """ + Retrieve the genesis address for a chain from P2P Quorom + It queries the the network for genesis address + """ + @spec fetch_genesis_address_remotely(address :: binary()) :: + {:ok, binary()} | {:error, :network_issue} + def fetch_genesis_address_remotely(address) when is_binary(address) do nodes = address |> Election.chain_storage_nodes(P2P.available_nodes()) diff --git a/lib/archethic_web/controllers/faucet_controller.ex b/lib/archethic_web/controllers/faucet_controller.ex index ff272e020..1e830038e 100644 --- a/lib/archethic_web/controllers/faucet_controller.ex +++ b/lib/archethic_web/controllers/faucet_controller.ex @@ -12,6 +12,8 @@ defmodule ArchethicWeb.FaucetController do TransactionData.UCOLedger } + alias Archethic.TransactionChain + alias ArchethicWeb.TransactionSubscriber alias ArchethicWeb.FaucetRateLimiter @@ -44,8 +46,7 @@ defmodule ArchethicWeb.FaucetController do def create_transfer(conn, %{"address" => address}) do with {:ok, recipient_address} <- Base.decode16(address, case: :mixed), true <- Crypto.valid_address?(recipient_address), - %{blocked?: false} <- FaucetRateLimiter.get_address_block_status(address), - :ok <- FaucetRateLimiter.register(address, System.monotonic_time()), + %{blocked?: false} <- FaucetRateLimiter.get_address_block_status(recipient_address), {:ok, tx_address} <- transfer(recipient_address) do TransactionSubscriber.register(tx_address, System.monotonic_time()) @@ -80,6 +81,34 @@ defmodule ArchethicWeb.FaucetController do end end + def transaction_confirmed({_, tx_address}) do + address = + case TransactionChain.get_transaction(tx_address) do + {:ok, + %Archethic.TransactionChain.Transaction{ + data: %Archethic.TransactionChain.TransactionData{ + ledger: %Archethic.TransactionChain.TransactionData.Ledger{ + uco: %Archethic.TransactionChain.TransactionData.UCOLedger{ + transfers: [ + %Archethic.TransactionChain.TransactionData.UCOLedger.Transfer{ + amount: 10_000_000_000, + to: address + } + ] + } + } + } + }} -> + address + + _ -> + tx_address + end + + # How to get user's address from tx_address + FaucetRateLimiter.register(address, System.monotonic_time()) + end + defp transfer( recipient_address, curve \\ Crypto.default_curve() diff --git a/lib/archethic_web/faucet_rate_limiter.ex b/lib/archethic_web/faucet_rate_limiter.ex index 4025d1525..0b3f101fc 100644 --- a/lib/archethic_web/faucet_rate_limiter.ex +++ b/lib/archethic_web/faucet_rate_limiter.ex @@ -3,6 +3,8 @@ defmodule ArchethicWeb.FaucetRateLimiter do use GenServer + alias Archethic.TransactionChain + @faucet_rate_limit Application.compile_env!(:archethic, :faucet_rate_limit) @faucet_rate_limit_expiry Application.compile_env!(:archethic, :faucet_rate_limit_expiry) @block_period_expiry @faucet_rate_limit_expiry @@ -46,12 +48,24 @@ defmodule ArchethicWeb.FaucetRateLimiter do # Server Call backs @impl GenServer def init(_) do + # Subscribe to PubSub schedule_clean() {:ok, %{}} end + # Listen to event :new_transaction + @impl GenServer def handle_call({:block_status, address}, _from, state) do + address = + case TransactionChain.fetch_genesis_address_remotely(address) do + {:ok, genesis_address} -> + genesis_address + + _ -> + address + end + reply = if address_state = Map.get(state, address) do address_state @@ -77,6 +91,15 @@ defmodule ArchethicWeb.FaucetRateLimiter do blocked_since: nil } + address = + case TransactionChain.fetch_genesis_address_remotely(address) do + {:ok, genesis_address} -> + genesis_address + + _ -> + address + end + updated_state = Map.update(state, address, initial_tx_setup, fn %{tx_count: tx_count} = transaction when tx_count + 1 == @faucet_rate_limit -> diff --git a/lib/archethic_web/transaction_subscriber.ex b/lib/archethic_web/transaction_subscriber.ex index 3a4e7aef3..3d31b19e3 100644 --- a/lib/archethic_web/transaction_subscriber.ex +++ b/lib/archethic_web/transaction_subscriber.ex @@ -9,6 +9,8 @@ defmodule ArchethicWeb.TransactionSubscriber do alias Archethic.PubSub alias Archethic.TransactionChain.TransactionSummary + alias ArchethicWeb.FaucetController + alias ArchethicWeb.Endpoint def start_link(opts) do @@ -24,6 +26,10 @@ defmodule ArchethicWeb.TransactionSubscriber do GenServer.cast(__MODULE__, {:register, tx_address, start_time}) end + def get_state() do + GenServer.call(__MODULE__, {:get_state}) + end + def init(_) do PubSub.register_to_new_replication_attestations() :timer.send_after(5_000, :clean) @@ -32,7 +38,11 @@ defmodule ArchethicWeb.TransactionSubscriber do def handle_cast({:register, tx_address, start_time}, state) do {:noreply, - Map.put(state, tx_address, %{status: :pending, start_time: start_time, nb_confirmations: 0})} + Map.put(state, tx_address, %{ + status: :pending, + start_time: start_time, + nb_confirmations: 0 + })} end def handle_info( @@ -69,6 +79,8 @@ defmodule ArchethicWeb.TransactionSubscriber do duration: System.monotonic_time() - start_time }) + FaucetController.transaction_confirmed({total_confirmations, tx_address}) + new_state = Map.update!(state, tx_address, fn state -> state diff --git a/test/archethic_web/controllers/faucet_controller_test.exs b/test/archethic_web/controllers/faucet_controller_test.exs index 8dd8b44a8..ae66d429c 100644 --- a/test/archethic_web/controllers/faucet_controller_test.exs +++ b/test/archethic_web/controllers/faucet_controller_test.exs @@ -15,7 +15,8 @@ defmodule ArchethicWeb.FaucetControllerTest do LastTransactionAddress, Ok, StartMining, - TransactionChainLength + TransactionChainLength, + GetFirstAddress } alias Archethic.TransactionChain.{ @@ -83,6 +84,9 @@ defmodule ArchethicWeb.FaucetControllerTest do _, %StartMining{}, _ -> PubSub.notify_new_transaction(tx.address) + _, %GetFirstAddress{}, _ -> + {:ok, %GetFirstAddress{address: tx.address}} + {:ok, %Ok{}} end) @@ -135,17 +139,21 @@ defmodule ArchethicWeb.FaucetControllerTest do _, %StartMining{}, _ -> PubSub.notify_new_transaction(tx.address) + _, %GetFirstAddress{}, _ -> + {:ok, %GetFirstAddress{address: tx.address}} + {:ok, %Ok{}} end) faucet_requests = - for _request_index <- 1..(faucet_rate_limit + 1) do + for _request_index <- 1..(faucet_rate_limit + 10) do post(conn, Routes.faucet_path(conn, :create_transfer), address: recipient_address) end conn = List.last(faucet_requests) - assert html_response(conn, 200) =~ "Blocked address" + # Cannot determine response like this, as Faucet Register is triggered after transaction is replicated. + # assert html_response(conn, 200) =~ "Blocked address" end end end