diff --git a/lib/archethic.ex b/lib/archethic.ex index 70f9a1b512..928f3fd563 100644 --- a/lib/archethic.ex +++ b/lib/archethic.ex @@ -61,36 +61,37 @@ defmodule Archethic do do_send_transaction(tx, welcome_node_key) {:error, :node_out_of_sync} -> - forward_transaction(tx) + forward_transaction(tx, welcome_node_key) {:error, last_address_to_sync} -> Resync.node_out_of_sync(last_address_to_sync) - forward_transaction(tx) + forward_transaction(tx, welcome_node_key) end else # node not authorized - forward_transaction(tx) + forward_transaction(tx, welcome_node_key) end end defp forward_transaction( tx, + welcome_node_key, nodes \\ P2P.authorized_and_available_nodes() |> Enum.filter(&Node.locally_available?/1) |> P2P.nearest_nodes() ) - defp forward_transaction(tx, [node | rest]) do - case P2P.send_message(node, %NewTransaction{transaction: tx}) do + defp forward_transaction(tx, welcome_node_key, [node | rest]) do + case P2P.send_message(node, %NewTransaction{transaction: tx, welcome_node: welcome_node_key}) do {:ok, %Ok{}} -> :ok {:error, _} -> - forward_transaction(tx, rest) + forward_transaction(tx, welcome_node_key, rest) end end - defp forward_transaction(_, []), do: {:error, :network_issue} + defp forward_transaction(_, _, []), do: {:error, :network_issue} defp do_send_transaction(tx = %Transaction{type: tx_type}, welcome_node_key) do current_date = DateTime.utc_now() diff --git a/lib/archethic/bootstrap/transaction_handler.ex b/lib/archethic/bootstrap/transaction_handler.ex index 3f05c738e8..00baa50833 100644 --- a/lib/archethic/bootstrap/transaction_handler.ex +++ b/lib/archethic/bootstrap/transaction_handler.ex @@ -36,7 +36,10 @@ defmodule Archethic.Bootstrap.TransactionHandler do @spec do_send_transaction(list(Node.t()), Transaction.t()) :: :ok defp do_send_transaction([node | rest], tx) do - case P2P.send_message(node, %NewTransaction{transaction: tx}) do + case P2P.send_message(node, %NewTransaction{ + transaction: tx, + welcome_node: node.first_public_key + }) do {:ok, %Ok{}} -> Logger.info("Waiting transaction validation", transaction_address: Base.encode16(tx.address), diff --git a/lib/archethic/p2p/message/new_transaction.ex b/lib/archethic/p2p/message/new_transaction.ex index e4b2a20d36..158e53df06 100644 --- a/lib/archethic/p2p/message/new_transaction.ex +++ b/lib/archethic/p2p/message/new_transaction.ex @@ -4,20 +4,20 @@ defmodule Archethic.P2P.Message.NewTransaction do This message is used locally within a node during the bootstrap """ - defstruct [:transaction] + @enforce_keys [:transaction, :welcome_node] + defstruct [:transaction, :welcome_node] - alias Archethic.TransactionChain.Transaction - alias Archethic.P2P.Message.Ok - alias Archethic.P2P.Message.Error - alias Archethic.Crypto + alias Archethic.{TransactionChain.Transaction, Crypto, Utils, P2P.Message} + alias Message.{Ok, Error} @type t :: %__MODULE__{ - transaction: Transaction.t() + transaction: Transaction.t(), + welcome_node: Crypto.key() } @spec process(__MODULE__.t(), Crypto.key()) :: Ok.t() | Error.t() - def process(%__MODULE__{transaction: tx}, sender_public_key) do - case Archethic.send_new_transaction(tx, sender_public_key) do + def process(%__MODULE__{transaction: tx, welcome_node: node_pbkey}, _) do + case Archethic.send_new_transaction(tx, node_pbkey) do :ok -> %Ok{} @@ -27,13 +27,14 @@ defmodule Archethic.P2P.Message.NewTransaction do end @spec serialize(t()) :: bitstring() - def serialize(%__MODULE__{transaction: tx}) do - <> + def serialize(%__MODULE__{transaction: tx, welcome_node: node_pbkey}) do + <> end @spec deserialize(bitstring()) :: {t(), bitstring} def deserialize(<>) do {tx, rest} = Transaction.deserialize(rest) - {%__MODULE__{transaction: tx}, rest} + {node_pbkey, rest} = Utils.deserialize_public_key(rest) + {%__MODULE__{transaction: tx, welcome_node: node_pbkey}, rest} end end diff --git a/test/archethic/p2p/messages_test.exs b/test/archethic/p2p/messages_test.exs index cdc2814374..dbd8d1f6eb 100644 --- a/test/archethic/p2p/messages_test.exs +++ b/test/archethic/p2p/messages_test.exs @@ -145,8 +145,8 @@ defmodule Archethic.P2P.MessageTest do test "NewTransaction message" do tx = Transaction.new(:transfer, %TransactionData{}, "seed", 0) - assert %NewTransaction{transaction: tx} == - %NewTransaction{transaction: tx} + assert %NewTransaction{transaction: tx, welcome_node: Crypto.first_node_public_key()} == + %NewTransaction{transaction: tx, welcome_node: Crypto.first_node_public_key()} |> Message.encode() |> Message.decode() |> elem(0) diff --git a/test/archethic_test.exs b/test/archethic_test.exs index 7cd8ca7c74..50d87d68a1 100644 --- a/test/archethic_test.exs +++ b/test/archethic_test.exs @@ -79,7 +79,11 @@ defmodule ArchethicTest do assert :ok = Archethic.send_new_transaction(tx) end - test "When NOT synced should forward the tx, start repair " do + test "When NOT synced should forward the tx and start repair " do + nss_genesis_address = "nss_genesis_address" + nss_last_address = "nss_last_address" + :persistent_term.put(:node_shared_secrets_gen_addr, nss_genesis_address) + P2P.add_and_connect_node(%Node{ ip: {127, 0, 0, 1}, port: 3000, @@ -94,9 +98,6 @@ defmodule ArchethicTest do start_supervised!({SummaryTimer, Application.get_env(:archethic, SummaryTimer)}) - nss_genesis_address = "nss_genesis_address" - nss_last_address = "nss_last_address" - MockDB |> stub(:get_last_chain_address, fn ^nss_genesis_address -> {nss_last_address, DateTime.utc_now()} @@ -109,7 +110,8 @@ defmodule ArchethicTest do %Transaction{ validation_stamp: %{ __struct__: :ValidationStamp, - # fail mathematical ccheck with irregular timestamp + # fail mathematical check with irregular timestamp + # causes validate_scheduling_time() to fail timestamp: DateTime.utc_now() |> DateTime.add(-86_400) } }} @@ -120,26 +122,34 @@ defmodule ArchethicTest do ) MockClient - |> expect(:send_message, 3, fn + |> expect(:send_message, 4, fn # validate nss chain from network # anticippated to be failed _, %GetLastTransactionAddress{}, _ -> {:ok, %LastTransactionAddress{address: "willnotmatchaddress"}} - _, %NewTransaction{}, _ -> + _, %NewTransaction{transaction: _, welcome_node: _}, _ -> # forward the tx {:ok, %Ok{}} _, %GetTransaction{address: _}, _ -> {:ok, %NotFound{}} + + _, _, _ -> + {:ok, %Ok{}} end) - tx = Transaction.new(:transfer, %TransactionData{}, "seed", 0) + # last address is d/f it returns last address from quorum + assert {:error, "willnotmatchaddress"} = P2P.verify_synchronization() + # trying to ssend a tx when NSS chain not synced + tx = Transaction.new(:transfer, %TransactionData{}, "seed", 0) assert :ok = Archethic.send_new_transaction(tx) + + # start repair and should bottleneck requests pid = SelfRepair.repair_in_progress?(nss_genesis_address) - Process.sleep(50) - assert pid != false + Process.sleep(150) + assert pid != nil end end