diff --git a/lib/archethic/bootstrap.ex b/lib/archethic/bootstrap.ex index f724835b8c..39dbf85164 100644 --- a/lib/archethic/bootstrap.ex +++ b/lib/archethic/bootstrap.ex @@ -16,6 +16,8 @@ defmodule Archethic.Bootstrap do alias Archethic.P2P.Listener alias Archethic.SelfRepair + alias Archethic.TransactionChain + alias Archethic.Replication require Logger @@ -207,6 +209,10 @@ defmodule Archethic.Bootstrap do Logger.info("Synchronization finished") end + if P2P.authorized_node?() do + resync_network_chain([:oracle, :node_shared_secrets], P2P.authorized_and_available_nodes()) + end + Sync.publish_end_of_sync() SelfRepair.start_scheduler() @@ -214,6 +220,35 @@ defmodule Archethic.Bootstrap do Listener.listen() end + def resync_network_chain(_type_list, _nodes = []), + do: Logger.info("Enforced Resync of Network Txs: failure, No-Nodes") + + def resync_network_chain(type_list, nodes) when is_list(nodes) do + # spawn process for each type of txn to sync + Task.Supervisor.async_stream_nolink(Archethic.TaskSupervisor, type_list, fn type -> + # acquire a(any) address of type txn, (for querying DB) + with addr when is_binary(addr) <- + TransactionChain.list_addresses_by_type(type) |> Stream.take(1) |> Enum.at(0), + {:ok, last_addr} <- TransactionChain.resolve_last_address(addr), + false <- TransactionChain.transaction_exists?(last_addr), + {:ok, tx} <- TransactionChain.fetch_transaction_remotely(last_addr, nodes), + :ok <- Replication.validate_and_store_transaction_chain(tx) do + Logger.info("Enforced Resync: Success", transaction_type: type) + else + e when e in [nil, true, []] -> + Logger.debug("Enforced Resync: Unexpected Error Transaction Address: nil/exists", + transaction_type: type + ) + + e -> + Logger.debug("Enforced Resync: Unexpected Error", transaction_type: type) + Logger.debug(e) + :ok + end + end) + |> Stream.run() + end + defp first_initialization( ip, port, diff --git a/src/c/nat/miniupnp b/src/c/nat/miniupnp index fa42d8f931..fa190f294a 160000 --- a/src/c/nat/miniupnp +++ b/src/c/nat/miniupnp @@ -1 +1 @@ -Subproject commit fa42d8f9316bf9c1ca14317e5a6e0d4a21365629 +Subproject commit fa190f294a6a4799487f95e23c93646bcabeffef diff --git a/test/archethic/bootstrap_test.exs b/test/archethic/bootstrap_test.exs index 6eff8e8c92..04b9e4e931 100644 --- a/test/archethic/bootstrap_test.exs +++ b/test/archethic/bootstrap_test.exs @@ -431,4 +431,82 @@ defmodule Archethic.BootstrapTest do Process.sleep(100) end end + + describe "resync_network_chain/2" do + test "should Retrieve and Store Network tx's" do + pbk = + Crypto.derive_keypair("node_for_bootstrap1", 0) + |> elem(0) + + P2P.add_and_connect_node(%Node{ + ip: {80, 10, 20, 102}, + port: 3002, + http_port: 4000, + last_public_key: pbk, + first_public_key: pbk, + network_patch: "AAA", + geo_patch: "AAA", + available?: true, + enrollment_date: DateTime.utc_now(), + reward_address: pbk |> Crypto.derive_address(), + authorized?: true, + authorization_date: DateTime.utc_now() |> DateTime.add(-86_400), + synced?: true + }) + + addr = Crypto.derive_keypair("nss_test", 1) |> elem(0) |> Crypto.derive_address() + + MockDB + |> expect(:list_addresses_by_type, 1, fn :node_shared_secrets -> [addr] end) + |> stub(:transaction_exists?, fn _ -> false end) + + MockClient + |> stub(:send_message, fn + _, %GetTransactionChainLength{}, _ -> + %TransactionChainLength{length: 4} + + _, %GetLastTransactionAddress{}, _ -> + %LastTransactionAddress{ + address: + Crypto.derive_keypair("nss_test", 4) + |> elem(0) + |> Crypto.derive_address() + } + + _, %GetTransactionChain{}, _ -> + {:ok, + %TransactionList{ + transactions: [ + %Transaction{ + type: :node_shared_secrets, + address: + Crypto.derive_keypair("nss_test", 2) + |> elem(0) + |> Crypto.derive_address() + }, + %Transaction{ + type: :node_shared_secrets, + address: + Crypto.derive_keypair("nss_test", 3) + |> elem(0) + |> Crypto.derive_address() + }, + %Transaction{ + type: :node_shared_secrets, + address: + Crypto.derive_keypair("nss_test", 4) + |> elem(0) + |> Crypto.derive_address() + } + ] + }} + end) + + :ok = + Bootstrap.resync_network_chain( + [:node_shared_secrets], + _nodes = P2P.authorized_and_available_nodes() + ) + end + end end