diff --git a/config/dev.exs b/config/dev.exs index 0393b6051..cc3b00597 100755 --- a/config/dev.exs +++ b/config/dev.exs @@ -37,12 +37,7 @@ config :archethic, Archethic.BeaconChain.SummaryTimer, interval: "0 * * * * *" config :archethic, Archethic.Bootstrap, - reward_address: - System.get_env( - "ARCHETHIC_REWARD_ADDRESS", - Base.encode16(<<0::8, 0::8, :crypto.strong_rand_bytes(32)::binary>>) - ) - |> Base.decode16!(case: :mixed) + reward_address: System.get_env("ARCHETHIC_REWARD_ADDRESS", "") |> Base.decode16!(case: :mixed) config :archethic, Archethic.Bootstrap.NetworkInit, genesis_pools: [ diff --git a/lib/archethic/bootstrap/sync.ex b/lib/archethic/bootstrap/sync.ex index 86adfdca4..d12f1dd90 100644 --- a/lib/archethic/bootstrap/sync.ex +++ b/lib/archethic/bootstrap/sync.ex @@ -117,6 +117,7 @@ defmodule Archethic.Bootstrap.Sync do |> NetworkInit.self_replication() P2P.set_node_globally_available(Crypto.first_node_public_key()) + P2P.set_node_globally_synced(Crypto.first_node_public_key()) P2P.authorize_node(Crypto.last_node_public_key(), DateTime.utc_now()) NetworkInit.init_software_origin_chain() diff --git a/lib/archethic/bootstrap/transaction_handler.ex b/lib/archethic/bootstrap/transaction_handler.ex index 8266a40c3..3be2245c2 100644 --- a/lib/archethic/bootstrap/transaction_handler.ex +++ b/lib/archethic/bootstrap/transaction_handler.ex @@ -99,7 +99,10 @@ defmodule Archethic.Bootstrap.TransactionHandler do condition inherit: [ # We need to ensure the type stays consistent type: node, - content: true + + # Content and token transfers will be validated during tx's validation + content: true, + token_transfers: true ] """, content: diff --git a/lib/archethic/crypto.ex b/lib/archethic/crypto.ex index d8c3a4390..2520eb305 100755 --- a/lib/archethic/crypto.ex +++ b/lib/archethic/crypto.ex @@ -1052,6 +1052,8 @@ defmodule Archethic.Crypto do end end + def valid_address?(_), do: false + @doc """ Load the transaction for the Keystore indexing """ diff --git a/lib/archethic/mining/pending_transaction_validation.ex b/lib/archethic/mining/pending_transaction_validation.ex index 35b3d1dc7..a2976a62e 100644 --- a/lib/archethic/mining/pending_transaction_validation.ex +++ b/lib/archethic/mining/pending_transaction_validation.ex @@ -270,7 +270,12 @@ defmodule Archethic.Mining.PendingTransactionValidation do %Transaction{ type: :node, data: %TransactionData{ - content: content + content: content, + ledger: %Ledger{ + token: %TokenLedger{ + transfers: token_transfers + } + } }, previous_public_key: previous_public_key }, @@ -290,7 +295,9 @@ defmodule Archethic.Mining.PendingTransactionValidation do root_ca_public_key )}, {:conn, :ok} <- - {:conn, valid_connection(ip, port, previous_public_key)} do + {:conn, valid_connection(ip, port, previous_public_key)}, + {:transfers, true} <- + {:transfers, Enum.all?(token_transfers, &Reward.is_reward_token?(&1.token_address))} do :ok else :error -> diff --git a/lib/archethic/p2p/mem_table_loader.ex b/lib/archethic/p2p/mem_table_loader.ex index 236cca920..4df68ce8c 100644 --- a/lib/archethic/p2p/mem_table_loader.ex +++ b/lib/archethic/p2p/mem_table_loader.ex @@ -108,25 +108,38 @@ defmodule Archethic.P2P.MemTableLoader do {:ok, ip, port, http_port, transport, reward_address, origin_public_key, _certificate} = Node.decode_transaction_content(content) - node = %Node{ - ip: ip, - port: port, - http_port: http_port, - first_public_key: first_public_key, - last_public_key: previous_public_key, - geo_patch: GeoPatch.from_ip(ip), - transport: transport, - last_address: address, - reward_address: reward_address, - origin_public_key: origin_public_key - } - if first_node_change?(first_public_key, previous_public_key) do + node = %Node{ + ip: ip, + port: port, + http_port: http_port, + first_public_key: first_public_key, + last_public_key: previous_public_key, + geo_patch: GeoPatch.from_ip(ip), + transport: transport, + last_address: address, + reward_address: reward_address, + origin_public_key: origin_public_key + } + node |> Node.enroll(timestamp) |> MemTable.add_node() else - MemTable.add_node(node) + {:ok, node} = MemTable.get_node(first_public_key) + + MemTable.add_node(%{ + node + | ip: ip, + port: port, + http_port: http_port, + last_public_key: previous_public_key, + geo_patch: GeoPatch.from_ip(ip), + transport: transport, + last_address: address, + reward_address: reward_address, + origin_public_key: origin_public_key + }) end Logger.info("Node loaded into in memory p2p tables", node: Base.encode16(first_public_key)) diff --git a/lib/archethic/replication/transaction_validator.ex b/lib/archethic/replication/transaction_validator.ex index 5af8ba43a..a3deaf067 100644 --- a/lib/archethic/replication/transaction_validator.ex +++ b/lib/archethic/replication/transaction_validator.ex @@ -315,18 +315,13 @@ defmodule Archethic.Replication.TransactionValidator do end defp check_inputs( - tx = %Transaction{type: type, address: address}, + tx = %Transaction{address: address}, inputs ) do - cond do - address == Bootstrap.genesis_address() -> - :ok - - Transaction.network_type?(type) -> - :ok - - true -> - do_check_inputs(tx, inputs) + if address == Bootstrap.genesis_address() do + :ok + else + do_check_inputs(tx, inputs) end end diff --git a/lib/archethic_web/live/settings_live.ex b/lib/archethic_web/live/settings_live.ex new file mode 100644 index 000000000..801549c67 --- /dev/null +++ b/lib/archethic_web/live/settings_live.ex @@ -0,0 +1,212 @@ +defmodule ArchethicWeb.SettingsLive do + @moduledoc false + + use ArchethicWeb, :live_view + + alias Archethic.Crypto + + alias Archethic.P2P + alias Archethic.P2P.Node + + alias Archethic.Reward + + alias Archethic.TransactionChain + alias Archethic.TransactionChain.Transaction + alias Archethic.TransactionChain.TransactionData + alias Archethic.TransactionChain.TransactionData.Ledger + alias Archethic.TransactionChain.TransactionData.TokenLedger + alias Archethic.TransactionChain.TransactionData.TokenLedger.Transfer, as: TokenTransfer + + alias ArchethicWeb.TransactionSubscriber + + @ip_validate_regex ~r/(^127\.)|(^192\.168\.)/ + + def mount(_params, %{"remote_ip" => remote_ip}, socket) do + # Only authorized the page in the node's private network + private_ip? = + Regex.match?( + @ip_validate_regex, + :inet.ntoa(remote_ip) |> to_string() + ) + + new_socket = + socket + |> assign(:allowed, private_ip?) + |> assign(:reward_address, "") + |> assign(:error, nil) + |> assign(:sending, false) + |> assign(:notification, "") + |> assign(:notification_status, "") + + {:ok, new_socket} + end + + def handle_params(_params, _uri, socket = %{assigns: %{allowed: true}}) do + %Node{reward_address: reward_address} = P2P.get_node_info() + {:noreply, assign(socket, :reward_address, Base.encode16(reward_address))} + end + + def handle_params(_params, _uri, socket) do + {:noreply, push_redirect(socket, to: "/", replace: true)} + end + + def handle_event( + "save", + %{"reward_address" => reward_address}, + socket = %{assigns: %{error: nil, reward_address: previous_reward_address}} + ) do + if previous_reward_address != reward_address do + send_new_transaction(Base.decode16!(reward_address, case: :mixed)) + {:noreply, assign(socket, :sending, true)} + else + {:noreply, socket} + end + end + + def handle_event("save", _params, socket) do + {:noreply, socket} + end + + def handle_event("validate", %{"reward_address" => reward_address}, socket) do + with {:ok, reward_address_bin} <- Base.decode16(reward_address, case: :mixed), + true <- Crypto.valid_address?(reward_address_bin) do + {:noreply, assign(socket, :error, nil)} + else + _ -> + {:noreply, assign(socket, :error, "Invalid address")} + end + end + + def handle_info({:new_transaction, _tx_address}, socket) do + %Node{reward_address: reward_address} = P2P.get_node_info() + + new_socket = + socket + |> assign(:sending, false) + |> assign(:reward_address, Base.encode16(reward_address)) + |> assign(:notification, "Change applied!") + |> assign(:notification_status, "success") + + {:noreply, new_socket} + end + + def handle_info({:transaction_error, _address, _context, reason}, socket) do + new_socket = + socket + |> assign(:sending, false) + |> assign(:notification, "Transaction is invalid - #{reason}") + |> assign(:notification_status, "error") + + {:noreply, new_socket} + end + + def render(assigns) do + ~L""" + <%= if @notification != "" do %> +