diff --git a/config/config.exs b/config/config.exs index 787b49594..908d339c9 100644 --- a/config/config.exs +++ b/config/config.exs @@ -70,7 +70,8 @@ config :archethic, Archethic.Crypto, supported_curves: [ :ed25519, :secp256r1, - :secp256k1 + :secp256k1, + :bls ], supported_hashes: [ :sha256, diff --git a/lib/archethic/bootstrap.ex b/lib/archethic/bootstrap.ex index 5624c11c1..9387392dd 100644 --- a/lib/archethic/bootstrap.ex +++ b/lib/archethic/bootstrap.ex @@ -215,7 +215,7 @@ defmodule Archethic.Bootstrap do ) {:ok, _ip, _p2p_port, _http_port, _transport, last_reward_address, _origin_public_key, - _key_certificate} = Node.decode_transaction_content(content) + _key_certificate, _mining_public_key} = Node.decode_transaction_content(content) update_node( ip, @@ -286,7 +286,7 @@ defmodule Archethic.Bootstrap do TransactionChain.fetch_transaction(last_address, closest_bootstrapping_nodes) {:ok, _ip, _p2p_port, _http_port, _transport, last_reward_address, _origin_public_key, - _key_certificate} = Node.decode_transaction_content(content) + _key_certificate, _mining_public_key} = Node.decode_transaction_content(content) last_reward_address else diff --git a/lib/archethic/bootstrap/transaction_handler.ex b/lib/archethic/bootstrap/transaction_handler.ex index 70bb919ec..18d22645d 100644 --- a/lib/archethic/bootstrap/transaction_handler.ex +++ b/lib/archethic/bootstrap/transaction_handler.ex @@ -80,6 +80,7 @@ defmodule Archethic.Bootstrap.TransactionHandler do when is_number(port) and port >= 0 and is_binary(reward_address) do origin_public_key = Crypto.origin_node_public_key() origin_public_key_certificate = Crypto.get_key_certificate(origin_public_key) + mining_public_key = Crypto.mining_node_public_key() Transaction.new(:node, %TransactionData{ code: """ @@ -100,7 +101,8 @@ defmodule Archethic.Bootstrap.TransactionHandler do transport, reward_address, origin_public_key, - origin_public_key_certificate + origin_public_key_certificate, + mining_public_key ) }) end diff --git a/lib/archethic/crypto.ex b/lib/archethic/crypto.ex index 58f5fd4e9..828eb4eaa 100755 --- a/lib/archethic/crypto.ex +++ b/lib/archethic/crypto.ex @@ -46,7 +46,7 @@ defmodule Archethic.Crypto do @typedoc """ List of the supported elliptic curves """ - @type supported_curve :: :ed25519 | ECDSA.curve() + @type supported_curve :: :ed25519 | ECDSA.curve() | :bls @typedoc """ List of the supported key origins @@ -392,6 +392,17 @@ defmodule Archethic.Crypto do |> ID.prepend_keypair(:ed25519, origin) end + defp do_generate_deterministic_keypair(:bls, origin, seed) do + private_key = :crypto.hash(:sha512, seed) + + keypair = { + BlsEx.get_public_key(private_key), + private_key + } + + ID.prepend_keypair(keypair, :bls, origin) + end + defp do_generate_deterministic_keypair(curve, origin, seed) do curve |> ECDSA.generate_keypair(seed) @@ -431,6 +442,7 @@ defmodule Archethic.Crypto do end defp do_sign(:ed25519, data, key), do: Ed25519.sign(key, data) + defp do_sign(:bls, data, key), do: BlsEx.sign(key, data) defp do_sign(curve, data, key), do: ECDSA.sign(curve, key, data) @doc """ @@ -564,6 +576,7 @@ defmodule Archethic.Crypto do end defp do_verify?(:ed25519, key, data, sig), do: Ed25519.verify?(key, data, sig) + defp do_verify?(:bls, key, data, sig), do: BlsEx.verify_signature(key, data, sig) defp do_verify?(curve, key, data, sig), do: ECDSA.verify?(curve, key, data, sig) @doc """ @@ -1007,6 +1020,11 @@ defmodule Archethic.Crypto do |> ID.prepend_curve(curve_type) end + @type key_size :: ed25519_key_size | ecdsa_key_size | bls_key_size + @type ed25519_key_size :: 32 + @type ecdsa_key_size :: 65 + @type bls_key_size :: 48 + @doc """ Return the size of key using the curve id @@ -1021,18 +1039,20 @@ defmodule Archethic.Crypto do iex> Crypto.key_size(ID.from_curve(:secp256k1)) 65 """ - @spec key_size(curve_id :: 0 | 1 | 2) :: 32 | 65 + @spec key_size(curve_id :: 0 | 1 | 2 | 3) :: key_size() def key_size(0), do: 32 def key_size(1), do: 65 def key_size(2), do: 65 + def key_size(3), do: 48 @doc """ Determine if a public key is valid """ @spec valid_public_key?(binary()) :: boolean() - def valid_public_key?(<<0::8, _::8, _::binary-size(32)>>), do: true - def valid_public_key?(<<1::8, _::8, _::binary-size(65)>>), do: true - def valid_public_key?(<<2::8, _::8, _::binary-size(65)>>), do: true + def valid_public_key?(<>) when curve in [0, 1, 2, 3] do + byte_size(public_key) == key_size(curve) + end + def valid_public_key?(_), do: false @doc """ @@ -1392,4 +1412,44 @@ defmodule Archethic.Crypto do def list_supported_hash_functions(), do: @supported_hashes @string_hashes Enum.map(@supported_hashes, &Atom.to_string/1) def list_supported_hash_functions(:string), do: @string_hashes + + @doc """ + Retrieve the node's mining public key + """ + @spec mining_node_public_key() :: key() + defdelegate mining_node_public_key, to: NodeKeystore, as: :mining_public_key + + @doc """ + Sign a message using the node's mining key + """ + @spec sign_with_mining_node_key(data :: iodata()) :: signature :: binary() + def sign_with_mining_node_key(data) do + data + |> Utils.wrap_binary() + |> NodeKeystore.sign_with_mining_key() + end + + @doc """ + Aggregate a list of BLS signatures with the associated public keys + + The signatures and public keys order must be the same + """ + @spec aggregate_signatures(signatures :: list(binary()), public_keys :: list(key())) :: binary() + def aggregate_signatures(signatures, public_keys) do + BlsEx.aggregate_signatures( + signatures, + Enum.map(public_keys, fn <<_::8, _::8, public_key::binary>> -> public_key end) + ) + end + + @doc """ + Aggregate a list of mining BLS public keys into a single one + """ + @spec aggregate_mining_public_keys(list(key())) :: key() + def aggregate_mining_public_keys(public_keys) do + public_keys + |> Enum.map(fn <<_::8, _::8, public_key::binary>> -> public_key end) + |> BlsEx.aggregate_public_keys() + |> ID.prepend_key(:bls) + end end diff --git a/lib/archethic/crypto/id.ex b/lib/archethic/crypto/id.ex index 4bf3a8179..b4f10a6d4 100755 --- a/lib/archethic/crypto/id.ex +++ b/lib/archethic/crypto/id.ex @@ -18,6 +18,7 @@ defmodule Archethic.Crypto.ID do def from_curve(:ed25519), do: 0 def from_curve(:secp256r1), do: 1 def from_curve(:secp256k1), do: 2 + def from_curve(:bls), do: 3 @doc """ Get a curve name from an curve ID @@ -34,6 +35,7 @@ defmodule Archethic.Crypto.ID do def to_curve(0), do: :ed25519 def to_curve(1), do: :secp256r1 def to_curve(2), do: :secp256k1 + def to_curve(3), do: :bls @doc """ Get an identification from an hash algorithm diff --git a/lib/archethic/crypto/keystore/node.ex b/lib/archethic/crypto/keystore/node.ex index 601be7469..583456cbb 100644 --- a/lib/archethic/crypto/keystore/node.ex +++ b/lib/archethic/crypto/keystore/node.ex @@ -29,4 +29,7 @@ defmodule Archethic.Crypto.NodeKeystore do @spec sign_with_origin_key(iodata()) :: binary() defdelegate sign_with_origin_key(data), to: Origin + + @callback sign_with_mining_key(iodata()) :: binary() + @callback mining_public_key() :: binary() end diff --git a/lib/archethic/crypto/keystore/node/software_impl.ex b/lib/archethic/crypto/keystore/node/software_impl.ex index 197672234..34cda3143 100644 --- a/lib/archethic/crypto/keystore/node/software_impl.ex +++ b/lib/archethic/crypto/keystore/node/software_impl.ex @@ -25,32 +25,32 @@ defmodule Archethic.Crypto.NodeKeystore.SoftwareImpl do @impl NodeKeystore @spec first_public_key() :: Crypto.key() def first_public_key do - public_key_fun = get_public_key_fun() - public_key_fun.(0) + {pub, _} = Crypto.derive_keypair(get_node_seed(), 0) + pub end @impl NodeKeystore @spec last_public_key() :: Crypto.key() def last_public_key do index = get_last_key_index() - public_key_fun = get_public_key_fun() - public_key_fun.(index) + {pub, _} = Crypto.derive_keypair(get_node_seed(), index) + pub end @impl NodeKeystore @spec next_public_key() :: Crypto.key() def next_public_key do index = get_next_key_index() - public_key_fun = get_public_key_fun() - public_key_fun.(index) + {pub, _} = Crypto.derive_keypair(get_node_seed(), index) + pub end @impl NodeKeystore @spec previous_public_key() :: Crypto.key() def previous_public_key do index = get_previous_key_index() - public_key_fun = get_public_key_fun() - public_key_fun.(index) + {pub, _} = Crypto.derive_keypair(get_node_seed(), index) + pub end @impl NodeKeystore @@ -62,39 +62,53 @@ defmodule Archethic.Crypto.NodeKeystore.SoftwareImpl do @impl NodeKeystore @spec sign_with_first_key(iodata()) :: binary() def sign_with_first_key(data) do - sign_fun = get_sign_fun() - sign_fun.(data, 0) + {_, pv} = Crypto.derive_keypair(get_node_seed(), 0) + Crypto.sign(data, pv) end @impl NodeKeystore @spec sign_with_last_key(iodata()) :: binary() def sign_with_last_key(data) do index = get_last_key_index() - sign_fun = get_sign_fun() - sign_fun.(data, index) + {_, pv} = Crypto.derive_keypair(get_node_seed(), index) + Crypto.sign(data, pv) end @impl NodeKeystore @spec sign_with_previous_key(iodata()) :: binary() def sign_with_previous_key(data) do index = get_previous_key_index() - sign_fun = get_sign_fun() - sign_fun.(data, index) + {_, pv} = Crypto.derive_keypair(get_node_seed(), index) + Crypto.sign(data, pv) end @impl NodeKeystore @spec diffie_hellman_with_first_key(Crypto.key()) :: binary() def diffie_hellman_with_first_key(public_key) do - dh_fun = get_diffie_helmann_fun() - dh_fun.(public_key, 0) + {_, pv} = Crypto.derive_keypair(get_node_seed(), 0) + do_diffie_helmann(pv, public_key) end @impl NodeKeystore @spec diffie_hellman_with_last_key(Crypto.key()) :: binary() def diffie_hellman_with_last_key(public_key) do index = get_last_key_index() - dh_fun = get_diffie_helmann_fun() - dh_fun.(public_key, index) + {_, pv} = Crypto.derive_keypair(get_node_seed(), index) + do_diffie_helmann(pv, public_key) + end + + @impl NodeKeystore + @spec sign_with_mining_key(iodata()) :: binary() + def sign_with_mining_key(data) do + {_, pv} = Crypto.generate_deterministic_keypair(get_node_seed(), :bls) + Crypto.sign(data, pv) + end + + @impl NodeKeystore + @spec mining_public_key() :: binary() + def mining_public_key do + {pub, _} = Crypto.generate_deterministic_keypair(get_node_seed(), :bls) + pub end defp get_last_key_index do @@ -112,19 +126,9 @@ defmodule Archethic.Crypto.NodeKeystore.SoftwareImpl do index end - defp get_sign_fun do - [{_, fun}] = :ets.lookup(@keystore_table, :sign_fun) - fun - end - - defp get_public_key_fun do - [{_, fun}] = :ets.lookup(@keystore_table, :public_key_fun) - fun - end - - defp get_diffie_helmann_fun do - [{_, fun}] = :ets.lookup(@keystore_table, :dh_fun) - fun + defp get_node_seed do + [{_, node_seed}] = :ets.lookup(@keystore_table, :node_seed) + node_seed end defp do_diffie_helmann(<>, public_key) do @@ -141,27 +145,9 @@ defmodule Archethic.Crypto.NodeKeystore.SoftwareImpl do @impl GenServer def init(_arg \\ []) do :ets.new(@keystore_table, [:set, :named_table, :protected, read_concurrency: true]) - node_seed = Origin.retrieve_node_seed() - - # Use anynomous functions to hide the node seed from the processes or tables - sign_fun = fn data, index -> - {_, pv} = Crypto.derive_keypair(node_seed, index) - Crypto.sign(data, pv) - end - public_key_fun = fn index -> - {pub, _} = Crypto.derive_keypair(node_seed, index) - pub - end - - dh_fun = fn public_key, index -> - {_, pv} = Crypto.derive_keypair(node_seed, index) - do_diffie_helmann(pv, public_key) - end - - :ets.insert(@keystore_table, {:sign_fun, sign_fun}) - :ets.insert(@keystore_table, {:public_key_fun, public_key_fun}) - :ets.insert(@keystore_table, {:dh_fun, dh_fun}) + node_seed = Origin.retrieve_node_seed() + :ets.insert(@keystore_table, {:node_seed, node_seed}) unless File.exists?(Utils.mut_dir("crypto")) do File.mkdir_p!(Utils.mut_dir("crypto")) @@ -226,11 +212,14 @@ defmodule Archethic.Crypto.NodeKeystore.SoftwareImpl do :ets.insert(@keystore_table, {:previous_index, index + 1}) :ets.insert(@keystore_table, {:next_index, index + 2}) - public_key_fun = get_public_key_fun() + node_seed = get_node_seed() + {next_pub, _} = Crypto.derive_keypair(node_seed, index + 2) + {previous_pub, _} = Crypto.derive_keypair(node_seed, index + 1) + {last_pub, _} = Crypto.derive_keypair(node_seed, index) - Logger.info("Next public key will be #{Base.encode16(public_key_fun.(index + 2))}") - Logger.info("Previous public key will be #{Base.encode16(public_key_fun.(index + 1))}") - Logger.info("Publication/Last public key will be #{Base.encode16(public_key_fun.(index))}") + Logger.info("Next public key will be #{Base.encode16(next_pub)}") + Logger.info("Previous public key will be #{Base.encode16(previous_pub)}") + Logger.info("Publication/Last public key will be #{Base.encode16(last_pub)}") write_index(index + 1) {:reply, :ok, state} @@ -241,4 +230,22 @@ defmodule Archethic.Crypto.NodeKeystore.SoftwareImpl do store_node_key_indexes(index) {:reply, :ok, state} end + + @impl GenServer + # FIXME: use genserver message because ets table is protected + def handle_cast(:migrate_1_5_9, state) do + node_seed = Origin.retrieve_node_seed() + :ets.insert(@keystore_table, {:node_seed, node_seed}) + :ets.delete(@keystore_table, :sign_fun) + :ets.delete(@keystore_table, :public_key_fun) + :ets.delete(@keystore_table, :dh_fun) + + {:noreply, state} + end + + # FIXME: to remove after 1.5.9 + @doc false + def migrate_ets_table_1_5_9 do + GenServer.cast(__MODULE__, :migrate_1_5_9) + end end diff --git a/lib/archethic/crypto/keystore/shared_secrets/software_impl.ex b/lib/archethic/crypto/keystore/shared_secrets/software_impl.ex index d424d8496..cb1fc5cd9 100644 --- a/lib/archethic/crypto/keystore/shared_secrets/software_impl.ex +++ b/lib/archethic/crypto/keystore/shared_secrets/software_impl.ex @@ -57,18 +57,6 @@ defmodule Archethic.Crypto.SharedSecretsKeystore.SoftwareImpl do {:ok, %{}} end - @impl GenServer - # we store functions in the ETS table, so we need to reload them - # every upgrade to avoid: "xxx is invalid, likely because it points to an old version of the code" - def code_change(_, state, _extra) do - :node_shared_secrets - |> TransactionChain.list_addresses_by_type() - |> Stream.take(-2) - |> Enum.each(&load_node_shared_secrets_tx/1) - - {:ok, state} - end - defp load_storage_nonce do case DB.get_bootstrap_info("storage_nonce") do nil -> @@ -113,65 +101,72 @@ defmodule Archethic.Crypto.SharedSecretsKeystore.SoftwareImpl do @impl SharedSecretsKeystore def sign_with_node_shared_secrets_key(data) do + [{_, transaction_seed}] = :ets.lookup(@keystore_table, :transaction_seed) [{_, index}] = :ets.lookup(@keystore_table, :shared_secrets_index) - sign_with_node_shared_secrets_key(data, index) + {_, pv} = Crypto.derive_keypair(transaction_seed, index) + Crypto.sign(data, pv) end @impl SharedSecretsKeystore def sign_with_node_shared_secrets_key(data, index) do - [{_, sign_fun}] = :ets.lookup(@keystore_table, :transaction_sign_fun) - sign_fun.(data, index) + [{_, transaction_seed}] = :ets.lookup(@keystore_table, :transaction_seed) + {_, pv} = Crypto.derive_keypair(transaction_seed, index) + Crypto.sign(data, pv) end @impl SharedSecretsKeystore def sign_with_reward_key(data) do + [{_, reward_seed}] = :ets.lookup(@keystore_table, :reward_seed) [{_, index}] = :ets.lookup(@keystore_table, :reward_index) - sign_with_reward_key(data, index) + {_, pv} = Crypto.derive_keypair(reward_seed, index) + Crypto.sign(data, pv) end @impl SharedSecretsKeystore def sign_with_reward_key(data, index) do - [{_, sign_fun}] = :ets.lookup(@keystore_table, :reward_sign_fun) - sign_fun.(data, index) + [{_, reward_seed}] = :ets.lookup(@keystore_table, :reward_seed) + {_, pv} = Crypto.derive_keypair(reward_seed, index) + Crypto.sign(data, pv) end @impl SharedSecretsKeystore def sign_with_daily_nonce_key(data, timestamp) do timestamp = DateTime.to_unix(timestamp) - sign_fun = - case :ets.lookup(@daily_keys, timestamp) do - [{_, sign_fun}] -> - sign_fun - - [] -> - timestamp = :ets.prev(@daily_keys, timestamp) - [{_, sign_fun}] = :ets.lookup(@daily_keys, timestamp) - sign_fun - end + case :ets.lookup(@daily_keys, timestamp) do + [{_, daily_nonce_seed}] -> + {_, pv} = Crypto.generate_deterministic_keypair(daily_nonce_seed) + Crypto.sign(data, pv) - sign_fun.(data) + [] -> + timestamp = :ets.prev(@daily_keys, timestamp) + [{_, seed}] = :ets.lookup(@daily_keys, timestamp) + {_, pv} = Crypto.generate_deterministic_keypair(seed) + Crypto.sign(data, pv) + end end @impl SharedSecretsKeystore def node_shared_secrets_public_key(index) do - [{_, public_key_fun}] = :ets.lookup(@keystore_table, :transaction_public_key_fun) - public_key_fun.(index) + [{_, transaction_seed}] = :ets.lookup(@keystore_table, :transaction_seed) + {pub, _} = Crypto.derive_keypair(transaction_seed, index) + pub end @impl SharedSecretsKeystore def reward_public_key(index) do - [{_, public_key_fun}] = :ets.lookup(@keystore_table, :reward_public_key_fun) - public_key_fun.(index) + [{_, reward_seed}] = :ets.lookup(@keystore_table, :reward_seed) + {pub, _} = Crypto.derive_keypair(reward_seed, index) + pub end @impl SharedSecretsKeystore def wrap_secrets(secret_key) do - [{_, transaction_seed_wrap_fun}] = :ets.lookup(@keystore_table, :transaction_seed_wrap_fun) - [{_, reward_seed_wrap_fun}] = :ets.lookup(@keystore_table, :reward_seed_wrap_fun) + [{_, transaction_seed}] = :ets.lookup(@keystore_table, :transaction_seed) + [{_, reward_seed}] = :ets.lookup(@keystore_table, :reward_seed) - encrypted_transaction_seed = transaction_seed_wrap_fun.(secret_key) - encrypted_reward_seed = reward_seed_wrap_fun.(secret_key) + encrypted_transaction_seed = Crypto.aes_encrypt(transaction_seed, secret_key) + encrypted_reward_seed = Crypto.aes_encrypt(reward_seed, secret_key) {encrypted_transaction_seed, encrypted_reward_seed} end @@ -224,50 +219,11 @@ defmodule Archethic.Crypto.SharedSecretsKeystore.SoftwareImpl do {:ok, daily_nonce_seed} <- Crypto.aes_decrypt(enc_daily_nonce_seed, aes_key), {:ok, transaction_seed} <- Crypto.aes_decrypt(enc_transaction_seed, aes_key), {:ok, reward_seed} <- Crypto.aes_decrypt(enc_reward_seed, aes_key) do - sign_daily_nonce_fun = fn data -> - {pub, pv} = Crypto.generate_deterministic_keypair(daily_nonce_seed) - Logger.debug("Sign with the daily nonce for the public key #{Base.encode16(pub)}") - - Crypto.sign(data, pv) - end - - transaction_sign_fun = fn data, index -> - {_, pv} = Crypto.derive_keypair(transaction_seed, index) - Crypto.sign(data, pv) - end - - reward_sign_fun = fn data, index -> - {_, pv} = Crypto.derive_keypair(reward_seed, index) - Crypto.sign(data, pv) - end - - transaction_public_key_fun = fn index -> - {pub, _} = Crypto.derive_keypair(transaction_seed, index) - pub - end - - reward_public_key_fun = fn index -> - {pub, _} = Crypto.derive_keypair(reward_seed, index) - pub - end - - transaction_seed_wrap_fun = fn secret_key -> - Crypto.aes_encrypt(transaction_seed, secret_key) - end - - reward_seed_wrap_fun = fn secret_key -> - Crypto.aes_encrypt(reward_seed, secret_key) - end - - :ets.insert(@daily_keys, {DateTime.to_unix(timestamp), sign_daily_nonce_fun}) + :ets.insert(@daily_keys, {DateTime.to_unix(timestamp), daily_nonce_seed}) remove_older_daily_keys(DateTime.to_unix(timestamp)) - :ets.insert(@keystore_table, {:transaction_sign_fun, transaction_sign_fun}) - :ets.insert(@keystore_table, {:reward_sign_fun, reward_sign_fun}) - :ets.insert(@keystore_table, {:transaction_public_key_fun, transaction_public_key_fun}) - :ets.insert(@keystore_table, {:reward_public_key_fun, reward_public_key_fun}) - :ets.insert(@keystore_table, {:transaction_seed_wrap_fun, transaction_seed_wrap_fun}) - :ets.insert(@keystore_table, {:reward_seed_wrap_fun, reward_seed_wrap_fun}) + :ets.insert(@keystore_table, {:transaction_seed, transaction_seed}) + :ets.insert(@keystore_table, {:reward_seed, reward_seed}) :ok end @@ -280,7 +236,7 @@ defmodule Archethic.Crypto.SharedSecretsKeystore.SoftwareImpl do prev_unix_timestamp -> # generate match pattern - # :ets.fun2ms(fn {key, _sign_function} -> key < prev_unix_timestamp end) + # :ets.fun2ms(fn {key, _} -> key < prev_unix_timestamp end) match_pattern = [ {{:"$1", :_}, [{:<, :"$1", prev_unix_timestamp}], [true]} @@ -310,4 +266,23 @@ defmodule Archethic.Crypto.SharedSecretsKeystore.SoftwareImpl do [{_, nonce}] = :ets.lookup(@keystore_table, :storage_nonce) nonce end + + @impl GenServer + def code_change(_old_vsn, state, _extra), do: {:ok, state} + + # FIXME: to remove after 1.5.9 + @doc false + def migrate_ets_table_1_5_9 do + :node_shared_secrets + |> TransactionChain.list_addresses_by_type() + |> Stream.take(-2) + |> Enum.each(&load_node_shared_secrets_tx/1) + + :ets.delete(@keystore_table, :transaction_sign_fun) + :ets.delete(@keystore_table, :reward_sign_fun) + :ets.delete(@keystore_table, :transaction_public_key_fun) + :ets.delete(@keystore_table, :reward_public_key_fun) + :ets.delete(@keystore_table, :transaction_seed_wrap_fun) + :ets.delete(@keystore_table, :reward_seed_wrap_fun) + end end diff --git a/lib/archethic/mining/pending_transaction_validation.ex b/lib/archethic/mining/pending_transaction_validation.ex index b25863531..a9076a12b 100644 --- a/lib/archethic/mining/pending_transaction_validation.ex +++ b/lib/archethic/mining/pending_transaction_validation.ex @@ -314,7 +314,7 @@ defmodule Archethic.Mining.PendingTransactionValidation do }, _ ) do - with {:ok, ip, port, _http_port, _, _, origin_public_key, key_certificate} <- + with {:ok, ip, port, _http_port, _, _, origin_public_key, key_certificate, mining_public_key} <- Node.decode_transaction_content(content), {:auth_origin, true} <- {:auth_origin, @@ -331,7 +331,11 @@ defmodule Archethic.Mining.PendingTransactionValidation do {:conn, :ok} <- {:conn, valid_connection(ip, port, previous_public_key)}, {:transfers, true} <- - {:transfers, Enum.all?(token_transfers, &Reward.is_reward_token?(&1.token_address))} do + {:transfers, Enum.all?(token_transfers, &Reward.is_reward_token?(&1.token_address))}, + {:mining_public_key, true} <- + {:mining_public_key, + Crypto.valid_public_key?(mining_public_key) and + Crypto.get_public_key_curve(mining_public_key) == :bls} do :ok else :error -> @@ -352,6 +356,9 @@ defmodule Archethic.Mining.PendingTransactionValidation do {:transfers, false} -> {:error, "Invalid transfers, only mining rewards tokens are allowed"} + + {:mining_public_key, false} -> + {:error, "Invalid mining public key"} end end diff --git a/lib/archethic/mining/proof_of_work.ex b/lib/archethic/mining/proof_of_work.ex index c5335d801..a3d39e7ec 100644 --- a/lib/archethic/mining/proof_of_work.ex +++ b/lib/archethic/mining/proof_of_work.ex @@ -139,7 +139,7 @@ defmodule Archethic.Mining.ProofOfWork do } }) do {:ok, _ip, _p2p_port, _http_port, _transport, _reward_address, origin_public_key, - _origin_certificate} = Node.decode_transaction_content(content) + _origin_certificate, _mining_public_key} = Node.decode_transaction_content(content) [origin_public_key] end diff --git a/lib/archethic/networking/scheduler.ex b/lib/archethic/networking/scheduler.ex index 59cf46f95..43cc7b68c 100644 --- a/lib/archethic/networking/scheduler.ex +++ b/lib/archethic/networking/scheduler.ex @@ -103,6 +103,7 @@ defmodule Archethic.Networking.Scheduler do {:ok, %Transaction{data: %TransactionData{code: code}}} <- TransactionChain.get_last_transaction(genesis_address, data: [:code]) do origin_public_key = Crypto.origin_node_public_key() + mining_public_key = Crypto.mining_node_public_key() key_certificate = Crypto.get_key_certificate(origin_public_key) tx = @@ -116,7 +117,8 @@ defmodule Archethic.Networking.Scheduler do transport, reward_address, origin_public_key, - key_certificate + key_certificate, + mining_public_key ) }) diff --git a/lib/archethic/p2p/mem_table.ex b/lib/archethic/p2p/mem_table.ex index 18e0d09ba..ffdcf47fe 100644 --- a/lib/archethic/p2p/mem_table.ex +++ b/lib/archethic/p2p/mem_table.ex @@ -33,7 +33,8 @@ defmodule Archethic.P2P.MemTable do synced?: 14, last_update_date: 15, available?: 16, - availability_update: 17 + availability_update: 17, + mining_public_key: 18 ] @doc """ @@ -58,161 +59,6 @@ defmodule Archethic.P2P.MemTable do Add a node into the P2P view. If a node already exists with the first public key, the P2P information will be updated. - - ## Examples - - iex> MemTable.start_link() - ...> - ...> node = %Node{ - ...> ip: {127, 0, 0, 1}, - ...> port: 3000, - ...> http_port: 4000, - ...> first_public_key: "key1", - ...> last_public_key: "key2", - ...> geo_patch: "AFZ", - ...> network_patch: "AAA", - ...> average_availability: 0.9, - ...> available?: true, - ...> synced?: true, - ...> authorized?: true, - ...> authorization_date: ~U[2020-10-22 23:19:45.797109Z], - ...> enrollment_date: ~U[2020-10-22 23:19:45.797109Z], - ...> last_update_date: ~U[2020-10-22 23:19:45.797109Z], - ...> availability_update: ~U[2020-10-22 23:19:45.797109Z], - ...> transport: :tcp, - ...> reward_address: - ...> <<0, 163, 237, 233, 93, 14, 241, 241, 8, 144, 218, 105, 16, 138, 243, 223, 17, 182, - ...> 87, 9, 7, 53, 146, 174, 125, 5, 244, 42, 35, 209, 142, 24, 164>>, - ...> last_address: - ...> <<0, 165, 32, 187, 102, 112, 133, 38, 17, 232, 54, 228, 173, 254, 94, 179, 32, 173, - ...> 88, 122, 234, 88, 139, 82, 26, 113, 42, 8, 183, 190, 163, 221, 112>>, - ...> origin_public_key: - ...> <<0, 0, 172, 147, 188, 9, 66, 252, 112, 77, 143, 178, 233, 51, 125, 102, 244, 36, - ...> 232, 185, 38, 7, 238, 128, 41, 30, 192, 61, 223, 119, 62, 249, 39, 212>> - ...> } - ...> - ...> :ok = MemTable.add_node(node) - ...> - ...> { - ...> :ets.tab2list(:archethic_node_discovery), - ...> :ets.tab2list(:archethic_authorized_nodes), - ...> :ets.tab2list(:archethic_node_keys) - ...> } - { - # Discovery table - [ - { - "key1", - "key2", - {127, 0, 0, 1}, - 3000, - 4000, - "AFZ", - "AAA", - 0.9, - ~U[2020-10-22 23:19:45.797109Z], - :tcp, - <<0, 163, 237, 233, 93, 14, 241, 241, 8, 144, 218, 105, 16, 138, 243, 223, 17, 182, 87, - 9, 7, 53, 146, 174, 125, 5, 244, 42, 35, 209, 142, 24, 164>>, - <<0, 165, 32, 187, 102, 112, 133, 38, 17, 232, 54, 228, 173, 254, 94, 179, 32, 173, 88, - 122, 234, 88, 139, 82, 26, 113, 42, 8, 183, 190, 163, 221, 112>>, - <<0, 0, 172, 147, 188, 9, 66, 252, 112, 77, 143, 178, 233, 51, 125, 102, 244, 36, 232, - 185, 38, 7, 238, 128, 41, 30, 192, 61, 223, 119, 62, 249, 39, 212>>, - true, - ~U[2020-10-22 23:19:45.797109Z], - true, - ~U[2020-10-22 23:19:45.797109Z] - } - ], - # Authorized nodes - [{"key1", ~U[2020-10-22 23:19:45.797109Z]}], - # Node key lookup - [{"key2", "key1"}] - } - - Update the node P2P view if exists - - iex> MemTable.start_link() - ...> - ...> node = %Node{ - ...> ip: {127, 0, 0, 1}, - ...> port: 3000, - ...> http_port: 4000, - ...> first_public_key: "key1", - ...> last_public_key: "key2", - ...> geo_patch: "AFZ", - ...> network_patch: "AAA", - ...> average_availability: 0.9, - ...> available?: true, - ...> synced?: true, - ...> authorized?: true, - ...> authorization_date: ~U[2020-10-22 23:19:45.797109Z], - ...> enrollment_date: ~U[2020-10-22 23:19:45.797109Z], - ...> last_update_date: ~U[2020-10-22 23:19:45.797109Z], - ...> availability_update: ~U[2020-10-22 23:19:45.797109Z], - ...> transport: :tcp, - ...> reward_address: - ...> <<0, 163, 237, 233, 93, 14, 241, 241, 8, 144, 218, 105, 16, 138, 243, 223, 17, 182, - ...> 87, 9, 7, 53, 146, 174, 125, 5, 244, 42, 35, 209, 142, 24, 164>>, - ...> last_address: - ...> <<0, 165, 32, 187, 102, 112, 133, 38, 17, 232, 54, 228, 173, 254, 94, 179, 32, 173, - ...> 88, 122, 234, 88, 139, 82, 26, 113, 42, 8, 183, 190, 163, 221, 112>>, - ...> origin_public_key: - ...> <<0, 0, 172, 147, 188, 9, 66, 252, 112, 77, 143, 178, 233, 51, 125, 102, 244, 36, - ...> 232, 185, 38, 7, 238, 128, 41, 30, 192, 61, 223, 119, 62, 249, 39, 212>> - ...> } - ...> - ...> :ok = MemTable.add_node(node) - ...> - ...> :ok = - ...> MemTable.add_node(%Node{ - ...> ip: {80, 20, 10, 122}, - ...> port: 5000, - ...> http_port: 4000, - ...> first_public_key: "key1", - ...> last_public_key: "key5", - ...> average_availability: 90, - ...> last_update_date: ~U[2020-10-22 23:20:45.797109Z], - ...> synced?: false, - ...> availability_update: ~U[2020-10-23 23:20:45.797109Z], - ...> available?: false, - ...> transport: :sctp, - ...> reward_address: - ...> <<0, 163, 237, 233, 93, 14, 241, 241, 8, 144, 218, 105, 16, 138, 243, 223, 17, 182, - ...> 87, 9, 7, 53, 146, 174, 125, 5, 244, 42, 35, 209, 142, 24, 164>>, - ...> last_address: - ...> <<0, 165, 32, 187, 102, 112, 133, 38, 17, 232, 54, 228, 173, 254, 94, 179, 32, 173, - ...> 88, 122, 234, 88, 139, 82, 26, 113, 42, 8, 183, 190, 163, 221, 112>>, - ...> origin_public_key: - ...> <<0, 0, 172, 147, 188, 9, 66, 252, 112, 77, 143, 178, 233, 51, 125, 102, 244, 36, - ...> 232, 185, 38, 7, 238, 128, 41, 30, 192, 61, 223, 119, 62, 249, 39, 212>> - ...> }) - ...> - ...> :ets.lookup(:archethic_node_discovery, "key1") - [ - { - "key1", - "key5", - {80, 20, 10, 122}, - 5000, - 4000, - "AFZ", - "AAA", - 90, - ~U[2020-10-22 23:19:45.797109Z], - :sctp, - <<0, 163, 237, 233, 93, 14, 241, 241, 8, 144, 218, 105, 16, 138, 243, 223, 17, 182, 87, 9, - 7, 53, 146, 174, 125, 5, 244, 42, 35, 209, 142, 24, 164>>, - <<0, 165, 32, 187, 102, 112, 133, 38, 17, 232, 54, 228, 173, 254, 94, 179, 32, 173, 88, - 122, 234, 88, 139, 82, 26, 113, 42, 8, 183, 190, 163, 221, 112>>, - <<0, 0, 172, 147, 188, 9, 66, 252, 112, 77, 143, 178, 233, 51, 125, 102, 244, 36, 232, - 185, 38, 7, 238, 128, 41, 30, 192, 61, 223, 119, 62, 249, 39, 212>>, - false, - ~U[2020-10-22 23:20:45.797109Z], - false, - ~U[2020-10-23 23:20:45.797109Z] - } - ] """ @spec add_node(Node.t()) :: :ok def add_node( @@ -252,6 +98,7 @@ defmodule Archethic.P2P.MemTable do defp insert_p2p_discovery(%Node{ first_public_key: first_public_key, last_public_key: last_public_key, + mining_public_key: mining_public_key, ip: ip, port: port, http_port: http_port, @@ -272,13 +119,15 @@ defmodule Archethic.P2P.MemTable do @discovery_table, {first_public_key, last_public_key, ip, port, http_port, geo_patch, network_patch, average_availability, enrollment_date, transport, reward_address, last_address, - origin_public_key, synced?, last_update_date, available?, availability_update} + origin_public_key, synced?, last_update_date, available?, availability_update, + mining_public_key} ) end defp update_p2p_discovery(%Node{ first_public_key: first_public_key, last_public_key: last_public_key, + mining_public_key: mining_public_key, ip: ip, port: port, http_port: http_port, @@ -308,7 +157,8 @@ defmodule Archethic.P2P.MemTable do {Keyword.fetch!(@discovery_index_position, :port), port}, {Keyword.fetch!(@discovery_index_position, :http_port), http_port}, {Keyword.fetch!(@discovery_index_position, :transport), transport}, - {Keyword.fetch!(@discovery_index_position, :last_update_date), timestamp} + {Keyword.fetch!(@discovery_index_position, :last_update_date), timestamp}, + {Keyword.fetch!(@discovery_index_position, :mining_public_key), mining_public_key} ] changes = @@ -359,44 +209,6 @@ defmodule Archethic.P2P.MemTable do @doc """ Retrieve the node entry by its first public key by default otherwise perform a lookup to retrieved it by the last key. - - ## Examples - - iex> MemTable.start_link() - ...> - ...> node = %Node{ - ...> ip: {127, 0, 0, 1}, - ...> port: 3000, - ...> http_port: 4000, - ...> first_public_key: "key1", - ...> last_public_key: "key2" - ...> } - ...> - ...> :ok = MemTable.add_node(node) - ...> {:ok, node} == MemTable.get_node("key1") - true - - Retrieve by the last public key will perform a lookup to get the first one - - iex> MemTable.start_link() - ...> - ...> node = %Node{ - ...> ip: {127, 0, 0, 1}, - ...> port: 3000, - ...> http_port: 4000, - ...> first_public_key: "key1", - ...> last_public_key: "key2" - ...> } - ...> - ...> :ok = MemTable.add_node(node) - ...> {:ok, node} == MemTable.get_node("key2") - true - - Returns an error if the node is not present - - iex> MemTable.start_link() - ...> MemTable.get_node("key10") - {:error, :not_found} """ @spec get_node(public_key :: Crypto.key()) :: {:ok, Node.t()} | {:error, :not_found} def get_node(key) do @@ -416,22 +228,6 @@ defmodule Archethic.P2P.MemTable do @doc """ List the P2P nodes - - ## Examples - - iex> MemTable.start_link() - ...> - ...> node = %Node{ - ...> ip: {127, 0, 0, 1}, - ...> port: 3000, - ...> http_port: 4000, - ...> first_public_key: "key1", - ...> last_public_key: "key2" - ...> } - ...> - ...> MemTable.add_node(node) - ...> [node] == MemTable.list_nodes() - true """ @spec list_nodes() :: list(Node.t()) def list_nodes do @@ -451,35 +247,6 @@ defmodule Archethic.P2P.MemTable do @doc """ List the authorized nodes - - ## Examples - - iex> MemTable.start_link() - ...> - ...> node1 = %Node{ - ...> ip: {127, 0, 0, 1}, - ...> port: 3000, - ...> http_port: 4000, - ...> first_public_key: "key1", - ...> last_public_key: "key2" - ...> } - ...> - ...> MemTable.add_node(node1) - ...> - ...> node2 = %Node{ - ...> ip: {127, 0, 0, 1}, - ...> port: 3000, - ...> http_port: 4000, - ...> first_public_key: "key3", - ...> last_public_key: "key3", - ...> authorized?: true, - ...> available?: true, - ...> authorization_date: ~U[2020-10-22 23:19:45.797109Z] - ...> } - ...> - ...> MemTable.add_node(node2) - ...> [node2] == MemTable.authorized_nodes() - true """ @spec authorized_nodes() :: list(Node.t()) def authorized_nodes do @@ -501,35 +268,6 @@ defmodule Archethic.P2P.MemTable do @doc """ List the nodes whicih are globally available - - ## Examples - - iex> MemTable.start_link() - ...> - ...> node1 = %Node{ - ...> ip: {127, 0, 0, 1}, - ...> port: 3000, - ...> http_port: 4000, - ...> first_public_key: "key1", - ...> last_public_key: "key2" - ...> } - ...> - ...> MemTable.add_node(node1) - ...> - ...> node2 = %Node{ - ...> ip: {127, 0, 0, 1}, - ...> port: 3000, - ...> http_port: 4000, - ...> first_public_key: "key3", - ...> last_public_key: "key3", - ...> available?: true, - ...> authorized?: true, - ...> authorization_date: DateTime.utc_now() - ...> } - ...> - ...> MemTable.add_node(node2) - ...> [node2] == MemTable.available_nodes() - true """ @spec available_nodes() :: list(Node.t()) def available_nodes do @@ -555,32 +293,6 @@ defmodule Archethic.P2P.MemTable do @doc """ List all the node first public keys - - ## Examples - - iex> MemTable.start_link() - ...> - ...> node1 = %Node{ - ...> ip: {127, 0, 0, 1}, - ...> port: 3000, - ...> http_port: 4000, - ...> first_public_key: "key1", - ...> last_public_key: "key2" - ...> } - ...> - ...> MemTable.add_node(node1) - ...> - ...> node2 = %Node{ - ...> ip: {127, 0, 0, 1}, - ...> port: 3000, - ...> http_port: 4000, - ...> first_public_key: "key3", - ...> last_public_key: "key3" - ...> } - ...> - ...> MemTable.add_node(node2) - ...> MemTable.list_node_first_public_keys() - ["key1", "key3"] """ @spec list_node_first_public_keys() :: list(Crypto.key()) def list_node_first_public_keys do @@ -589,34 +301,6 @@ defmodule Archethic.P2P.MemTable do @doc """ List the authorized node public keys - - ## Examples - - iex> MemTable.start_link() - ...> - ...> node1 = %Node{ - ...> ip: {127, 0, 0, 1}, - ...> port: 3000, - ...> http_port: 4000, - ...> first_public_key: "key1", - ...> last_public_key: "key2" - ...> } - ...> - ...> MemTable.add_node(node1) - ...> - ...> node2 = %Node{ - ...> ip: {127, 0, 0, 1}, - ...> port: 3000, - ...> http_port: 4000, - ...> first_public_key: "key3", - ...> last_public_key: "key3", - ...> authorized?: true, - ...> authorization_date: ~U[2020-10-22 23:19:45.797109Z] - ...> } - ...> - ...> MemTable.add_node(node2) - ...> MemTable.list_authorized_public_keys() - ["key3"] """ @spec list_authorized_public_keys() :: list(Crypto.key()) def list_authorized_public_keys do @@ -625,23 +309,6 @@ defmodule Archethic.P2P.MemTable do @doc """ Mark the node as authorized. - - ## Examples - - iex> MemTable.start_link() - ...> - ...> :ok = - ...> MemTable.add_node(%Node{ - ...> ip: {127, 0, 0, 1}, - ...> port: 3000, - ...> http_port: 4000, - ...> first_public_key: "key1", - ...> last_public_key: "key1" - ...> }) - ...> - ...> :ok = MemTable.authorize_node("key1", ~U[2020-10-22 23:45:41.181903Z]) - ...> MemTable.list_authorized_public_keys() - ["key1"] """ @spec authorize_node(first_public_key :: Crypto.key(), authorization_date :: DateTime.t()) :: :ok @@ -656,25 +323,6 @@ defmodule Archethic.P2P.MemTable do @doc """ Reset the authorized nodes - - ## Examples - - iex> MemTable.start_link() - ...> - ...> :ok = - ...> MemTable.add_node(%Node{ - ...> ip: {127, 0, 0, 1}, - ...> port: 3000, - ...> http_port: 4000, - ...> first_public_key: "key1", - ...> last_public_key: "key1", - ...> authorized?: true, - ...> authorization_date: ~U[2020-10-22 23:45:41.181903Z] - ...> }) - ...> - ...> :ok = MemTable.unauthorize_node("key1") - ...> MemTable.list_authorized_public_keys() - [] """ @spec unauthorize_node(Crypto.key()) :: :ok def unauthorize_node(first_public_key) when is_binary(first_public_key) do @@ -689,36 +337,6 @@ defmodule Archethic.P2P.MemTable do If the given key is the first one, it will returns Otherwise a lookup table is used to match the last key from the first key - - ## Examples - - iex> MemTable.start_link() - ...> - ...> node = %Node{ - ...> ip: {127, 0, 0, 1}, - ...> port: 3000, - ...> http_port: 4000, - ...> first_public_key: "key1", - ...> last_public_key: "key2" - ...> } - ...> - ...> MemTable.add_node(node) - ...> MemTable.get_first_node_key("key1") - "key1" - - iex> MemTable.start_link() - ...> - ...> node = %Node{ - ...> ip: {127, 0, 0, 1}, - ...> port: 3000, - ...> http_port: 4000, - ...> first_public_key: "key1", - ...> last_public_key: "key2" - ...> } - ...> - ...> MemTable.add_node(node) - ...> MemTable.get_first_node_key("key2") - "key1" """ @spec get_first_node_key(Crypto.key()) :: Crypto.key() def get_first_node_key(key) when is_binary(key) do @@ -733,30 +351,6 @@ defmodule Archethic.P2P.MemTable do @doc """ Mark the node as globally available - - ## Examples - - iex> MemTable.start_link() - ...> - ...> node = %Node{ - ...> ip: {127, 0, 0, 1}, - ...> port: 3000, - ...> http_port: 4000, - ...> first_public_key: "key1", - ...> last_public_key: "key2" - ...> } - ...> - ...> MemTable.add_node(node) - ...> :ok = MemTable.set_node_available("key1", ~U[2020-10-22 23:45:41Z]) - ...> - ...> {:ok, %Node{available?: true, availability_update: ~U[2020-10-22 23:45:41Z]}} = - ...> MemTable.get_node("key1") - ...> - ...> MemTable.add_node(node) - ...> :ok = MemTable.set_node_available("key1", ~U[2020-10-23 23:45:41Z]) - ...> - ...> {:ok, %Node{available?: true, availability_update: ~U[2020-10-23 23:45:41Z]}} = - ...> MemTable.get_node("key1") """ @spec set_node_available(Crypto.key(), DateTime.t()) :: :ok def set_node_available(first_public_key, availability_update) @@ -778,25 +372,6 @@ defmodule Archethic.P2P.MemTable do @doc """ Mark the node globally unavailable - - ## Examples - - iex> MemTable.start_link() - ...> - ...> node = %Node{ - ...> ip: {127, 0, 0, 1}, - ...> port: 3000, - ...> http_port: 4000, - ...> first_public_key: "key1", - ...> last_public_key: "key2" - ...> } - ...> - ...> MemTable.add_node(node) - ...> :ok = MemTable.set_node_available("key1", ~U[2020-10-22 23:45:41Z]) - ...> :ok = MemTable.set_node_unavailable("key1", ~U[2020-10-23 23:45:41Z]) - ...> - ...> {:ok, %Node{available?: false, availability_update: ~U[2020-10-23 23:45:41Z]}} = - ...> MemTable.get_node("key1") """ @spec set_node_unavailable(Crypto.key(), DateTime.t()) :: :ok def set_node_unavailable(first_public_key, availability_update) @@ -818,22 +393,6 @@ defmodule Archethic.P2P.MemTable do @doc """ Mark the node synced - - ## Examples - - iex> MemTable.start_link() - ...> - ...> node = %Node{ - ...> ip: {127, 0, 0, 1}, - ...> port: 3000, - ...> http_port: 4000, - ...> first_public_key: "key1", - ...> last_public_key: "key2" - ...> } - ...> - ...> MemTable.add_node(node) - ...> :ok = MemTable.set_node_synced("key1") - ...> {:ok, %Node{synced?: true}} = MemTable.get_node("key1") """ @spec set_node_synced(Crypto.key()) :: :ok def set_node_synced(first_public_key) when is_binary(first_public_key) do @@ -846,23 +405,6 @@ defmodule Archethic.P2P.MemTable do @doc """ Mark the node unsynced - - ## Examples - - iex> MemTable.start_link() - ...> - ...> node = %Node{ - ...> ip: {127, 0, 0, 1}, - ...> port: 3000, - ...> http_port: 4000, - ...> first_public_key: "key1", - ...> last_public_key: "key2" - ...> } - ...> - ...> MemTable.add_node(node) - ...> :ok = MemTable.set_node_synced("key1") - ...> :ok = MemTable.set_node_unsynced("key1") - ...> {:ok, %Node{synced?: false}} = MemTable.get_node("key1") """ @spec set_node_unsynced(Crypto.key()) :: :ok def set_node_unsynced(first_public_key) when is_binary(first_public_key) do @@ -875,23 +417,6 @@ defmodule Archethic.P2P.MemTable do @doc """ Update the average availability of the node and reset the history - - ## Examples - - iex> MemTable.start_link() - ...> - ...> node = %Node{ - ...> ip: {127, 0, 0, 1}, - ...> port: 3000, - ...> http_port: 4000, - ...> first_public_key: "key1", - ...> last_public_key: "key2", - ...> average_availability: 0.4 - ...> } - ...> - ...> MemTable.add_node(node) - ...> :ok = MemTable.update_node_average_availability("key1", 0.8) - ...> {:ok, %Node{average_availability: 0.8}} = MemTable.get_node("key1") """ @spec update_node_average_availability( first_public_key :: Crypto.key(), @@ -916,23 +441,6 @@ defmodule Archethic.P2P.MemTable do @doc """ Update the network patch - - ## Examples - - iex> MemTable.start_link() - ...> - ...> node = %Node{ - ...> ip: {127, 0, 0, 1}, - ...> port: 3000, - ...> http_port: 4000, - ...> first_public_key: "key1", - ...> last_public_key: "key2", - ...> network_patch: "AAA" - ...> } - ...> - ...> MemTable.add_node(node) - ...> :ok = MemTable.update_node_network_patch("key1", "3FC") - ...> {:ok, %Node{network_patch: "3FC"}} = MemTable.get_node("key1") """ @spec update_node_network_patch(first_public_key :: Crypto.key(), network_patch :: binary()) :: :ok @@ -979,4 +487,20 @@ defmodule Archethic.P2P.MemTable do :ok end end + + # FIXME: to remove after 1.5.9 + @doc false + def migrate_ets_table_1_5_9 do + # Inject the mining public key as nil awaiting the migration scripts to be executed + ms = [ + {{:"$1", :"$2", :"$3", :"$4", :"$5", :"$6", :"$7", :"$8", :"$9", :"$10", :"$11", :"$12", + :"$13", :"$14", :"$15", :"$16", :"$17"}, [], + [ + {{:"$1", :"$2", :"$3", :"$4", :"$5", :"$6", :"$7", :"$8", :"$9", :"$10", :"$11", :"$12", + :"$13", :"$14", :"$15", :"$16", :"$17", nil}} + ]} + ] + + :ets.select_replace(@discovery_table, ms) + end end diff --git a/lib/archethic/p2p/mem_table_loader.ex b/lib/archethic/p2p/mem_table_loader.ex index f553e3114..8b1e3fc20 100644 --- a/lib/archethic/p2p/mem_table_loader.ex +++ b/lib/archethic/p2p/mem_table_loader.ex @@ -104,8 +104,8 @@ defmodule Archethic.P2P.MemTableLoader do first_public_key = TransactionChain.get_first_public_key(previous_public_key) - {:ok, ip, port, http_port, transport, reward_address, origin_public_key, _certificate} = - Node.decode_transaction_content(content) + {:ok, ip, port, http_port, transport, reward_address, origin_public_key, _certificate, + mining_public_key} = Node.decode_transaction_content(content) if first_node_change?(first_public_key, previous_public_key) do node = %Node{ @@ -119,7 +119,8 @@ defmodule Archethic.P2P.MemTableLoader do last_address: address, reward_address: reward_address, origin_public_key: origin_public_key, - last_update_date: timestamp + last_update_date: timestamp, + mining_public_key: mining_public_key } node @@ -139,7 +140,8 @@ defmodule Archethic.P2P.MemTableLoader do last_address: address, reward_address: reward_address, origin_public_key: origin_public_key, - last_update_date: timestamp + last_update_date: timestamp, + mining_public_key: mining_public_key }) end diff --git a/lib/archethic/p2p/node.ex b/lib/archethic/p2p/node.ex index 3b6cfcf29..45b9175e1 100755 --- a/lib/archethic/p2p/node.ex +++ b/lib/archethic/p2p/node.ex @@ -20,6 +20,7 @@ defmodule Archethic.P2P.Node do defstruct [ :first_public_key, :last_public_key, + :mining_public_key, :last_address, :reward_address, :ip, @@ -41,34 +42,12 @@ defmodule Archethic.P2P.Node do @doc """ Decode node information from transaction content - - ## Examples - - iex> Node.decode_transaction_content( - ...> <<127, 0, 0, 1, 11, 184, 15, 160, 1, 0, 0, 173, 179, 246, 126, 247, 223, 20, 86, 201, - ...> 55, 190, 29, 59, 212, 196, 36, 89, 178, 185, 211, 23, 68, 30, 22, 75, 39, 197, 8, - ...> 186, 167, 123, 182, 0, 0, 130, 83, 96, 217, 99, 234, 242, 235, 175, 236, 109, 166, - ...> 95, 250, 255, 90, 210, 227, 150, 117, 26, 64, 143, 55, 199, 95, 222, 35, 137, 221, - ...> 196, 169, 0, 64, 63, 40, 158, 160, 56, 156, 206, 193, 107, 50, 250, 244, 6, 212, 171, - ...> 158, 240, 175, 162, 2, 55, 86, 26, 215, 44, 61, 198, 143, 141, 22, 122, 16, 89, 155, - ...> 28, 132, 231, 22, 143, 53, 126, 102, 148, 210, 88, 103, 216, 37, 175, 164, 87, 10, - ...> 255, 229, 33, 178, 204, 184, 228, 130, 173, 148, 82, 126>> - ...> ) - {:ok, {127, 0, 0, 1}, 3000, 4000, :tcp, - <<0, 0, 173, 179, 246, 126, 247, 223, 20, 86, 201, 55, 190, 29, 59, 212, 196, 36, 89, 178, - 185, 211, 23, 68, 30, 22, 75, 39, 197, 8, 186, 167, 123, 182>>, - <<0, 0, 130, 83, 96, 217, 99, 234, 242, 235, 175, 236, 109, 166, 95, 250, 255, 90, 210, 227, - 150, 117, 26, 64, 143, 55, 199, 95, 222, 35, 137, 221, 196, 169>>, - <<63, 40, 158, 160, 56, 156, 206, 193, 107, 50, 250, 244, 6, 212, 171, 158, 240, 175, 162, 2, - 55, 86, 26, 215, 44, 61, 198, 143, 141, 22, 122, 16, 89, 155, 28, 132, 231, 22, 143, 53, - 126, 102, 148, 210, 88, 103, 216, 37, 175, 164, 87, 10, 255, 229, 33, 178, 204, 184, 228, - 130, 173, 148, 82, 126>>} """ @spec decode_transaction_content(binary()) :: {:ok, ip_address :: :inet.ip_address(), p2p_port :: :inet.port_number(), http_port :: :inet.port_number(), P2P.supported_transport(), reward_address :: binary(), origin_public_key :: Crypto.key(), - key_certificate :: binary()} + key_certificate :: binary(), mining_public_key :: binary() | nil} | :error def decode_transaction_content( <> @@ -77,9 +56,18 @@ defmodule Archethic.P2P.Node do {reward_address, rest} <- Utils.deserialize_address(rest), {origin_public_key, rest} <- Utils.deserialize_public_key(rest), <> <- rest do + rest::binary>> <- rest do + mining_public_key = + case rest do + "" -> + nil + + mining_public_key -> + mining_public_key |> Utils.deserialize_public_key() |> elem(0) + end + {:ok, {ip0, ip1, ip2, ip3}, port, http_port, deserialize_transport(transport), - reward_address, origin_public_key, key_certificate} + reward_address, origin_public_key, key_certificate, mining_public_key} else _ -> :error @@ -90,31 +78,6 @@ defmodule Archethic.P2P.Node do @doc """ Encode node's transaction content - - ## Examples - - iex> Node.encode_transaction_content( - ...> {127, 0, 0, 1}, - ...> 3000, - ...> 4000, - ...> :tcp, - ...> <<0, 0, 173, 179, 246, 126, 247, 223, 20, 86, 201, 55, 190, 29, 59, 212, 196, 36, 89, - ...> 178, 185, 211, 23, 68, 30, 22, 75, 39, 197, 8, 186, 167, 123, 182>>, - ...> <<0, 0, 130, 83, 96, 217, 99, 234, 242, 235, 175, 236, 109, 166, 95, 250, 255, 90, 210, - ...> 227, 150, 117, 26, 64, 143, 55, 199, 95, 222, 35, 137, 221, 196, 169>>, - ...> <<63, 40, 158, 160, 56, 156, 206, 193, 107, 50, 250, 244, 6, 212, 171, 158, 240, 175, - ...> 162, 2, 55, 86, 26, 215, 44, 61, 198, 143, 141, 22, 122, 16, 89, 155, 28, 132, 231, - ...> 22, 143, 53, 126, 102, 148, 210, 88, 103, 216, 37, 175, 164, 87, 10, 255, 229, 33, - ...> 178, 204, 184, 228, 130, 173, 148, 82, 126>> - ...> ) - <<127, 0, 0, 1, 11, 184, 15, 160, 1, 0, 0, 173, 179, 246, 126, 247, 223, 20, 86, 201, 55, 190, - 29, 59, 212, 196, 36, 89, 178, 185, 211, 23, 68, 30, 22, 75, 39, 197, 8, 186, 167, 123, 182, - 0, 0, 130, 83, 96, 217, 99, 234, 242, 235, 175, 236, 109, 166, 95, 250, 255, 90, 210, 227, - 150, 117, 26, 64, 143, 55, 199, 95, 222, 35, 137, 221, 196, 169, 0, 64, 63, 40, 158, 160, - 56, 156, 206, 193, 107, 50, 250, 244, 6, 212, 171, 158, 240, 175, 162, 2, 55, 86, 26, 215, - 44, 61, 198, 143, 141, 22, 122, 16, 89, 155, 28, 132, 231, 22, 143, 53, 126, 102, 148, 210, - 88, 103, 216, 37, 175, 164, 87, 10, 255, 229, 33, 178, 204, 184, 228, 130, 173, 148, 82, - 126>> """ @spec encode_transaction_content( :inet.ip_address(), @@ -123,7 +86,8 @@ defmodule Archethic.P2P.Node do P2P.supported_transport(), reward_address :: binary(), origin_public_key :: Crypto.key(), - origin_key_certificate :: binary() + origin_key_certificate :: binary(), + mining_public_key :: Crypto.key() ) :: binary() def encode_transaction_content( {ip1, ip2, ip3, ip4}, @@ -132,16 +96,18 @@ defmodule Archethic.P2P.Node do transport, reward_address, origin_public_key, - key_certificate + key_certificate, + mining_public_key ) do <> + key_certificate::binary, mining_public_key::binary>> end @type t() :: %__MODULE__{ first_public_key: nil | Crypto.key(), last_public_key: Crypto.key(), + mining_public_key: Crypto.key() | nil, last_address: nil | Crypto.key(), reward_address: nil | Crypto.key(), ip: nil | :inet.ip_address(), @@ -163,11 +129,12 @@ defmodule Archethic.P2P.Node do @doc """ Convert a tuple from NodeLedger to a Node instance """ - @spec cast(tuple()) :: __MODULE__.t() + @spec cast(tuple()) :: t() def cast( {first_public_key, last_public_key, ip, port, http_port, geo_patch, network_patch, average_availability, enrollment_date, transport, reward_address, last_address, - origin_public_key, synced?, last_update_date, available?, availability_update} + origin_public_key, synced?, last_update_date, available?, availability_update, + mining_public_key} ) do %__MODULE__{ ip: ip, @@ -186,7 +153,8 @@ defmodule Archethic.P2P.Node do origin_public_key: origin_public_key, last_update_date: last_update_date, available?: available?, - availability_update: availability_update + availability_update: availability_update, + mining_public_key: mining_public_key } end @@ -253,68 +221,8 @@ defmodule Archethic.P2P.Node do %{node | enrollment_date: date, network_patch: geo_patch} end - # defp new_average_availability(history) do - # list = for <>, do: view - - # list - # |> Enum.frequencies() - # |> Map.get(1) - # |> case do - # nil -> - # 0.0 - - # available_times -> - # Float.floor(available_times / bit_size(history), 1) - # end - # end - @doc """ Serialize a node into binary format - - ## Examples - - iex> Node.serialize(%Node{ - ...> first_public_key: - ...> <<0, 0, 182, 67, 168, 252, 227, 203, 142, 164, 142, 248, 159, 209, 249, 247, 86, 64, - ...> 92, 224, 91, 182, 122, 49, 209, 169, 96, 111, 219, 204, 57, 250, 59, 226>>, - ...> last_public_key: - ...> <<0, 0, 182, 67, 168, 252, 227, 203, 142, 164, 142, 248, 159, 209, 249, 247, 86, 64, - ...> 92, 224, 91, 182, 122, 49, 209, 169, 96, 111, 219, 204, 57, 250, 59, 226>>, - ...> ip: {127, 0, 0, 1}, - ...> port: 3000, - ...> http_port: 4000, - ...> transport: :tcp, - ...> geo_patch: "FA9", - ...> network_patch: "AVC", - ...> available?: true, - ...> synced?: true, - ...> average_availability: 0.8, - ...> enrollment_date: ~U[2020-06-26 08:36:11Z], - ...> authorization_date: ~U[2020-06-26 08:36:11Z], - ...> last_update_date: ~U[2020-06-26 08:36:11Z], - ...> availability_update: ~U[2020-06-26 08:36:11Z], - ...> authorized?: true, - ...> reward_address: - ...> <<0, 0, 163, 237, 233, 93, 14, 241, 241, 8, 144, 218, 105, 16, 138, 243, 223, 17, - ...> 182, 87, 9, 7, 53, 146, 174, 125, 5, 244, 42, 35, 209, 142, 24, 164>>, - ...> last_address: - ...> <<0, 0, 165, 32, 187, 102, 112, 133, 38, 17, 232, 54, 228, 173, 254, 94, 179, 32, - ...> 173, 88, 122, 234, 88, 139, 82, 26, 113, 42, 8, 183, 190, 163, 221, 112>>, - ...> origin_public_key: - ...> <<0, 0, 130, 83, 96, 217, 99, 234, 242, 235, 175, 236, 109, 166, 95, 250, 255, 90, - ...> 210, 227, 150, 117, 26, 64, 143, 55, 199, 95, 222, 35, 137, 221, 196, 169>> - ...> }) - <<127, 0, 0, 1, 11, 184, 15, 160, 1, "FA9", "AVC", 80, 94, 245, 179, 123, 1::1, 1::1, 1::1, - 94, 245, 179, 123, 0, 0, 182, 67, 168, 252, 227, 203, 142, 164, 142, 248, 159, 209, 249, - 247, 86, 64, 92, 224, 91, 182, 122, 49, 209, 169, 96, 111, 219, 204, 57, 250, 59, 226, 0, 0, - 182, 67, 168, 252, 227, 203, 142, 164, 142, 248, 159, 209, 249, 247, 86, 64, 92, 224, 91, - 182, 122, 49, 209, 169, 96, 111, 219, 204, 57, 250, 59, 226, 0, 0, 163, 237, 233, 93, 14, - 241, 241, 8, 144, 218, 105, 16, 138, 243, 223, 17, 182, 87, 9, 7, 53, 146, 174, 125, 5, 244, - 42, 35, 209, 142, 24, 164, 0, 0, 165, 32, 187, 102, 112, 133, 38, 17, 232, 54, 228, 173, - 254, 94, 179, 32, 173, 88, 122, 234, 88, 139, 82, 26, 113, 42, 8, 183, 190, 163, 221, 112, - 0, 0, 130, 83, 96, 217, 99, 234, 242, 235, 175, 236, 109, 166, 95, 250, 255, 90, 210, 227, - 150, 117, 26, 64, 143, 55, 199, 95, 222, 35, 137, 221, 196, 169, 94, 245, 179, 123, 94, 245, - 179, 123>> """ @spec serialize(__MODULE__.t()) :: bitstring() def serialize(%__MODULE__{ @@ -324,6 +232,7 @@ defmodule Archethic.P2P.Node do transport: transport, first_public_key: first_public_key, last_public_key: last_public_key, + mining_public_key: mining_public_key, geo_patch: geo_patch, network_patch: network_patch, average_availability: average_availability, @@ -348,12 +257,18 @@ defmodule Archethic.P2P.Node do avg_bin = trunc(average_availability * 100) + mining_public_key_bin = + if is_nil(mining_public_key), + do: <<0::8>>, + else: <<1::8, mining_public_key::binary>> + <> + DateTime.to_unix(last_update_date)::32, DateTime.to_unix(availability_update)::32, + mining_public_key_bin::binary>> end defp serialize_transport(MockTransport), do: 0 @@ -362,55 +277,6 @@ defmodule Archethic.P2P.Node do @doc """ Deserialize an encoded node - ## Examples - - iex> Node.deserialize( - ...> <<127, 0, 0, 1, 11, 184, 15, 160, 1, "FA9", "AVC", 80, 94, 245, 179, 123, 1::1, 1::1, - ...> 1::1, 94, 245, 179, 123, 0, 0, 182, 67, 168, 252, 227, 203, 142, 164, 142, 248, 159, - ...> 209, 249, 247, 86, 64, 92, 224, 91, 182, 122, 49, 209, 169, 96, 111, 219, 204, 57, - ...> 250, 59, 226, 0, 0, 182, 67, 168, 252, 227, 203, 142, 164, 142, 248, 159, 209, 249, - ...> 247, 86, 64, 92, 224, 91, 182, 122, 49, 209, 169, 96, 111, 219, 204, 57, 250, 59, - ...> 226, 0, 0, 163, 237, 233, 93, 14, 241, 241, 8, 144, 218, 105, 16, 138, 243, 223, 17, - ...> 182, 87, 9, 7, 53, 146, 174, 125, 5, 244, 42, 35, 209, 142, 24, 164, 0, 0, 165, 32, - ...> 187, 102, 112, 133, 38, 17, 232, 54, 228, 173, 254, 94, 179, 32, 173, 88, 122, 234, - ...> 88, 139, 82, 26, 113, 42, 8, 183, 190, 163, 221, 112, 0, 0, 130, 83, 96, 217, 99, - ...> 234, 242, 235, 175, 236, 109, 166, 95, 250, 255, 90, 210, 227, 150, 117, 26, 64, 143, - ...> 55, 199, 95, 222, 35, 137, 221, 196, 169, 94, 245, 179, 123, 94, 245, 179, 123>> - ...> ) - { - %Node{ - first_public_key: - <<0, 0, 182, 67, 168, 252, 227, 203, 142, 164, 142, 248, 159, 209, 249, 247, 86, 64, 92, - 224, 91, 182, 122, 49, 209, 169, 96, 111, 219, 204, 57, 250, 59, 226>>, - last_public_key: - <<0, 0, 182, 67, 168, 252, 227, 203, 142, 164, 142, 248, 159, 209, 249, 247, 86, 64, 92, - 224, 91, 182, 122, 49, 209, 169, 96, 111, 219, 204, 57, 250, 59, 226>>, - ip: {127, 0, 0, 1}, - port: 3000, - http_port: 4000, - transport: :tcp, - geo_patch: "FA9", - network_patch: "AVC", - available?: true, - synced?: true, - average_availability: 0.8, - enrollment_date: ~U[2020-06-26 08:36:11Z], - authorization_date: ~U[2020-06-26 08:36:11Z], - last_update_date: ~U[2020-06-26 08:36:11Z], - availability_update: ~U[2020-06-26 08:36:11Z], - authorized?: true, - reward_address: - <<0, 0, 163, 237, 233, 93, 14, 241, 241, 8, 144, 218, 105, 16, 138, 243, 223, 17, 182, - 87, 9, 7, 53, 146, 174, 125, 5, 244, 42, 35, 209, 142, 24, 164>>, - last_address: - <<0, 0, 165, 32, 187, 102, 112, 133, 38, 17, 232, 54, 228, 173, 254, 94, 179, 32, 173, - 88, 122, 234, 88, 139, 82, 26, 113, 42, 8, 183, 190, 163, 221, 112>>, - origin_public_key: - <<0, 0, 130, 83, 96, 217, 99, 234, 242, 235, 175, 236, 109, 166, 95, 250, 255, 90, 210, - 227, 150, 117, 26, 64, 143, 55, 199, 95, 222, 35, 137, 221, 196, 169>> - }, - "" - } """ @spec deserialize(bitstring()) :: {Archethic.P2P.Node.t(), bitstring} def deserialize( @@ -435,6 +301,12 @@ defmodule Archethic.P2P.Node do {origin_public_key, <>} = Utils.deserialize_public_key(rest) + {mining_public_key, rest} = + case rest do + <<1::8, rest::bitstring>> -> Utils.deserialize_public_key(rest) + <<0::8, rest::bitstring>> -> {nil, rest} + end + { %__MODULE__{ ip: {o1, o2, o3, o4}, @@ -455,7 +327,8 @@ defmodule Archethic.P2P.Node do last_address: last_address, origin_public_key: origin_public_key, last_update_date: DateTime.from_unix!(last_update_date), - availability_update: DateTime.from_unix!(availability_update) + availability_update: DateTime.from_unix!(availability_update), + mining_public_key: mining_public_key }, rest } diff --git a/lib/archethic/shared_secrets/mem_tables_loader.ex b/lib/archethic/shared_secrets/mem_tables_loader.ex index d5dad96c8..b4f7aedcc 100644 --- a/lib/archethic/shared_secrets/mem_tables_loader.ex +++ b/lib/archethic/shared_secrets/mem_tables_loader.ex @@ -60,8 +60,8 @@ defmodule Archethic.SharedSecrets.MemTablesLoader do content: content } }) do - {:ok, _ip, _p2p_port, _http_port, _transport, _reward_address, origin_public_key, _cert} = - Node.decode_transaction_content(content) + {:ok, _ip, _p2p_port, _http_port, _transport, _reward_address, origin_public_key, _cert, + _mining_public_key} = Node.decode_transaction_content(content) <<_::8, origin_id::8, _::binary>> = origin_public_key diff --git a/lib/archethic_web/explorer/live/node_details_live.html.heex b/lib/archethic_web/explorer/live/node_details_live.html.heex index 89240a9eb..aef45d772 100644 --- a/lib/archethic_web/explorer/live/node_details_live.html.heex +++ b/lib/archethic_web/explorer/live/node_details_live.html.heex @@ -61,6 +61,18 @@

+ <%= if @node.mining_public_key do %> +
+

Mining public key

+

+ <%= Base.encode16(@node.mining_public_key) %> +

+

+ <%= Base.encode16(:binary.part(@node.mining_public_key, 0, 13)) %>... +

+
+ <% end %> +

IP

diff --git a/lib/archethic_web/explorer/live/settings_live.ex b/lib/archethic_web/explorer/live/settings_live.ex index 856c48b77..6f926c090 100644 --- a/lib/archethic_web/explorer/live/settings_live.ex +++ b/lib/archethic_web/explorer/live/settings_live.ex @@ -156,7 +156,8 @@ defmodule ArchethicWeb.Explorer.SettingsLive do transport, next_reward_address, Crypto.origin_node_public_key(), - Crypto.get_key_certificate(Crypto.origin_node_public_key()) + Crypto.get_key_certificate(Crypto.origin_node_public_key()), + Crypto.mining_node_public_key() ) }) @@ -190,7 +191,8 @@ defmodule ArchethicWeb.Explorer.SettingsLive do transport, reward_address, Crypto.origin_node_public_key(), - Crypto.get_key_certificate(Crypto.origin_node_public_key()) + Crypto.get_key_certificate(Crypto.origin_node_public_key()), + Crypto.mining_node_public_key() ) }) diff --git a/lib/archethic_web/explorer/views/explorer_view.ex b/lib/archethic_web/explorer/views/explorer_view.ex index 06308b26f..a67a5b6fb 100644 --- a/lib/archethic_web/explorer/views/explorer_view.ex +++ b/lib/archethic_web/explorer/views/explorer_view.ex @@ -50,10 +50,10 @@ defmodule ArchethicWeb.Explorer.ExplorerView do end def format_transaction_content(:node, content) do - {:ok, ip, port, http_port, transport, reward_address, origin_public_key, key_certificate} = - Node.decode_transaction_content(content) + {:ok, ip, port, http_port, transport, reward_address, origin_public_key, key_certificate, + mining_public_key} = Node.decode_transaction_content(content) - """ + content = """ IP: #{:inet.ntoa(ip)} P2P Port: #{port} HTTP Port: #{http_port} @@ -62,6 +62,14 @@ defmodule ArchethicWeb.Explorer.ExplorerView do Origin public key: #{Base.encode16(origin_public_key)} Key certificate: #{Base.encode16(key_certificate)} """ + + case mining_public_key do + nil -> + content + + _ -> + content <> "Mining public key: #{Base.encode16(mining_public_key)}" + end end def format_transaction_content(:beacon, content) do diff --git a/mix.exs b/mix.exs index 5a4f83d64..71cc4ba89 100644 --- a/mix.exs +++ b/mix.exs @@ -132,7 +132,8 @@ defmodule Archethic.MixProject do {:exla, "~> 0.5"}, {:ex_keccak, "0.7.1"}, {:ex_secp256k1, "~> 0.7.2"}, - {:nimble_csv, "~> 1.1", only: :test, runtime: false} + {:nimble_csv, "~> 1.1", only: :test, runtime: false}, + {:bls_ex, "~> 0.1"} ] end diff --git a/mix.lock b/mix.lock index bae200dac..bd31bf2c2 100644 --- a/mix.lock +++ b/mix.lock @@ -7,6 +7,7 @@ "benchee_html": {:hex, :benchee_html, "1.0.0", "5b4d24effebd060f466fb460ec06576e7b34a00fc26b234fe4f12c4f05c95947", [:mix], [{:benchee, ">= 0.99.0 and < 2.0.0", [hex: :benchee, repo: "hexpm", optional: false]}, {:benchee_json, "~> 1.0", [hex: :benchee_json, repo: "hexpm", optional: false]}], "hexpm", "5280af9aac432ff5ca4216d03e8a93f32209510e925b60e7f27c33796f69e699"}, "benchee_json": {:hex, :benchee_json, "1.0.0", "cc661f4454d5995c08fe10dd1f2f72f229c8f0fb1c96f6b327a8c8fc96a91fe5", [:mix], [{:benchee, ">= 0.99.0 and < 2.0.0", [hex: :benchee, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "da05d813f9123505f870344d68fb7c86a4f0f9074df7d7b7e2bb011a63ec231c"}, "blankable": {:hex, :blankable, "1.0.0", "89ab564a63c55af117e115144e3b3b57eb53ad43ba0f15553357eb283e0ed425", [:mix], [], "hexpm", "7cf11aac0e44f4eedbee0c15c1d37d94c090cb72a8d9fddf9f7aec30f9278899"}, + "bls_ex": {:hex, :bls_ex, "0.1.0", "53c0b3a28936114bb18aa62300c6ddcb5a2a5bad89587d23c3914abd1ccab1fd", [:mix], [{:rustler, ">= 0.0.0", [hex: :rustler, repo: "hexpm", optional: true]}, {:rustler_precompiled, "~> 0.4", [hex: :rustler_precompiled, repo: "hexpm", optional: false]}], "hexpm", "4f37db50b065fd71cb3911fc255c2dc29879f6729efdd09e03c36f8c6de63ac5"}, "bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"}, "castore": {:hex, :castore, "1.0.3", "7130ba6d24c8424014194676d608cb989f62ef8039efd50ff4b3f33286d06db8", [:mix], [], "hexpm", "680ab01ef5d15b161ed6a95449fac5c6b8f60055677a8e79acf01b27baa4390b"}, "cldr_utils": {:hex, :cldr_utils, "2.21.0", "1bdbb8de3870ab4831f11f877b40cce838a03bf7da272430c232c19726d53f14", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "26f56101663f5aca4e727e0eb983b578ba5b170e2f12e8456df9995809a7a93b"}, @@ -64,7 +65,6 @@ "mox": {:hex, :mox, "1.0.2", "dc2057289ac478b35760ba74165b4b3f402f68803dd5aecd3bfd19c183815d64", [:mix], [], "hexpm", "f9864921b3aaf763c8741b5b8e6f908f44566f1e427b2630e89e9a73b981fef2"}, "nimble_csv": {:hex, :nimble_csv, "1.2.0", "4e26385d260c61eba9d4412c71cea34421f296d5353f914afe3f2e71cce97722", [:mix], [], "hexpm", "d0628117fcc2148178b034044c55359b26966c6eaa8e2ce15777be3bbc91b12a"}, "nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"}, - "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, "nx": {:hex, :nx, "0.5.1", "118134b8c97c2a8f86c87aa8434994c1cbbe139a306b89cca04e08dd46228067", [:mix], [{:complex, "~> 0.5", [hex: :complex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ceb8fbbe19b3c4252a7188d8b0e059fac9da0f4a4f3bb770fc665fdd0b29f0c5"}, "observer_cli": {:hex, :observer_cli, "1.7.4", "3c1bfb6d91bf68f6a3d15f46ae20da0f7740d363ee5bc041191ce8722a6c4fae", [:mix, :rebar3], [{:recon, "~> 2.5.1", [hex: :recon, repo: "hexpm", optional: false]}], "hexpm", "50de6d95d814f447458bd5d72666a74624eddb0ef98bdcee61a0153aae0865ff"}, "pathex": {:hex, :pathex, "2.5.0", "350ed75b41dd7c579843bc6052463d36d9a35362f5430ff3ad12a13c6a783ce6", [:mix], [], "hexpm", "031a2063c59eae2f697373f41814e9d9076105ab2173bd3a88fbe8789fdb434b"}, @@ -94,5 +94,5 @@ "telemetry_metrics_prometheus_core": {:hex, :telemetry_metrics_prometheus_core, "1.1.0", "4e15f6d7dbedb3a4e3aed2262b7e1407f166fcb9c30ca3f96635dfbbef99965c", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "0dd10e7fe8070095df063798f82709b0a1224c31b8baf6278b423898d591a069"}, "telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"}, "websockex": {:hex, :websockex, "0.4.3", "92b7905769c79c6480c02daacaca2ddd49de936d912976a4d3c923723b647bf0", [:mix], [], "hexpm", "95f2e7072b85a3a4cc385602d42115b73ce0b74a9121d0d6dbbf557645ac53e4"}, - "xla": {:hex, :xla, "0.4.4", "c3a8ed1f579bda949df505e49ff65415c8281d991fbd6ae1d8f3c5d0fd155f54", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "484f3f9011db3c9f1ff1e98eecefd382f3882a07ada540fd58803db1d2dab671"} + "xla": {:hex, :xla, "0.4.4", "c3a8ed1f579bda949df505e49ff65415c8281d991fbd6ae1d8f3c5d0fd155f54", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "484f3f9011db3c9f1ff1e98eecefd382f3882a07ada540fd58803db1d2dab671"}, } diff --git a/priv/migration_tasks/prod/1.5.9@add_mining_bls_key.exs b/priv/migration_tasks/prod/1.5.9@add_mining_bls_key.exs new file mode 100644 index 000000000..dbcf02543 --- /dev/null +++ b/priv/migration_tasks/prod/1.5.9@add_mining_bls_key.exs @@ -0,0 +1,57 @@ +defmodule Migration_1_5_9 do + @moduledoc false + + alias Archethic.Crypto + alias Archethic.P2P + alias Archethic.P2P.Node + + alias Archethic.TransactionChain + alias Archethic.TransactionChain.Transaction + alias Archethic.TransactionChain.TransactionData + + alias Archethic.Utils + + require Logger + + def run() do + %Node{ip: ip, port: p2p_port, http_port: http_port, transport: transport, reward_address: reward_address, origin_public_key: origin_public_key} = P2P.get_node_info() + + mining_public_key = Crypto.mining_node_public_key() + key_certificate = Crypto.get_key_certificate(origin_public_key) + + genesis_address = Crypto.first_node_public_key() |> Crypto.derive_address() + {:ok, %Transaction{data: %TransactionData{code: code}}} = + TransactionChain.get_last_transaction(genesis_address, data: [:code]) + + tx = + Transaction.new(:node, %TransactionData{ + code: code, + content: + Node.encode_transaction_content( + ip, + p2p_port, + http_port, + transport, + reward_address, + origin_public_key, + key_certificate, + mining_public_key + ) + }) + + :ok = Archethic.send_new_transaction(tx, forward?: true) + + nodes = + P2P.authorized_and_available_nodes() + |> Enum.filter(&P2P.node_connected?/1) + |> P2P.nearest_nodes() + + case Utils.await_confirmation(tx.address, nodes) do + {:ok, _} -> + Logger.info("Mining node updated") + + {:error, reason} -> + Logger.warning("Cannot update node transaction - #{inspect reason}") + end + end +end diff --git a/rel/appups/archethic/1.5.8_to_1.5.9.appup b/rel/appups/archethic/1.5.8_to_1.5.9.appup new file mode 100644 index 000000000..64280d98f --- /dev/null +++ b/rel/appups/archethic/1.5.8_to_1.5.9.appup @@ -0,0 +1,177 @@ +{"1.5.9", + [{"1.5.8", + [{apply,{'Elixir.Archethic.PubSub',notify_node_status,[node_down]}}, + {load_module,'Elixir.Archethic.Cldr.Currency',brutal_purge,soft_purge,[]}, + {load_module,'Elixir.Archethic.Cldr.Number.Transliterate',brutal_purge, + soft_purge,[]}, + {load_module,'Elixir.Archethic.Contracts.Interpreter.Library.Common.List', + brutal_purge,soft_purge,[]}, + {load_module,'Elixir.Archethic.Contracts.Interpreter.Library.Common.Math', + brutal_purge,soft_purge,[]}, + {load_module,'Elixir.Archethic.Crypto.ID',brutal_purge,soft_purge,[]}, + {load_module,'Elixir.Archethic.Crypto.NodeKeystore',brutal_purge, + soft_purge,[]}, + {load_module,'Elixir.Archethic.P2P.Message.ShardRepair',brutal_purge, + soft_purge,[]}, + {load_module,'Elixir.Archethic.P2P.Node',brutal_purge,soft_purge,[]}, + {load_module,'Elixir.ArchethicWeb.Explorer.LayoutView',brutal_purge, + soft_purge,[]}, + {load_module,'Elixir.ArchethicWeb.Explorer.WorldMapLive',brutal_purge, + soft_purge,[]}, + {load_module,'Elixir.ArchethicWeb.ExplorerRouter',brutal_purge,soft_purge, + []}, + {load_module,'Elixir.Archethic.Crypto',brutal_purge,soft_purge, + ['Elixir.Archethic.Crypto.ID', + 'Elixir.Archethic.Crypto.NodeKeystore']}, + {update,'Elixir.Archethic.Contracts.Worker', + {advanced,[]}, + brutal_purge,soft_purge, + ['Elixir.Archethic.Crypto']}, + {update,'Elixir.Archethic.Crypto.SharedSecretsKeystore.SoftwareImpl', + {advanced,[]}, + brutal_purge,soft_purge, + ['Elixir.Archethic.Crypto']}, + {update,'Elixir.Archethic.P2P.BootstrappingSeeds', + {advanced,[]}, + brutal_purge,soft_purge, + ['Elixir.Archethic.Crypto']}, + {update,'Elixir.Archethic.P2P.MemTable', + {advanced,[]}, + brutal_purge,soft_purge, + ['Elixir.Archethic.P2P.Node']}, + {update,'Elixir.Archethic.SelfRepair.Notifier', + {advanced,[]}, + brutal_purge,soft_purge, + ['Elixir.Archethic.Crypto']}, + {load_module,'Elixir.ArchethicWeb.Explorer.NodeDetailsLive',brutal_purge, + soft_purge, + ['Elixir.Archethic.Crypto']}, + {load_module,'Elixir.Archethic.Bootstrap.TransactionHandler',brutal_purge, + soft_purge, + ['Elixir.Archethic.Crypto','Elixir.Archethic.P2P.Node']}, + {update,'Elixir.Archethic.Crypto.NodeKeystore.SoftwareImpl', + {advanced,[]}, + brutal_purge,soft_purge, + ['Elixir.Archethic.Crypto','Elixir.Archethic.Crypto.ID']}, + {load_module,'Elixir.Archethic.Mining.PendingTransactionValidation', + brutal_purge,soft_purge, + ['Elixir.Archethic.Crypto','Elixir.Archethic.P2P.Node']}, + {load_module,'Elixir.Archethic.Mining.ProofOfWork',brutal_purge, + soft_purge, + ['Elixir.Archethic.Crypto','Elixir.Archethic.P2P.Node']}, + {update,'Elixir.Archethic.Networking.Scheduler', + {advanced,[]}, + brutal_purge,soft_purge, + ['Elixir.Archethic.Crypto','Elixir.Archethic.P2P.Node']}, + {update,'Elixir.Archethic.SharedSecrets.MemTablesLoader', + {advanced,[]}, + brutal_purge,soft_purge, + ['Elixir.Archethic.Crypto','Elixir.Archethic.P2P.Node']}, + {load_module,'Elixir.ArchethicWeb.Explorer.ExplorerView',brutal_purge, + soft_purge, + ['Elixir.Archethic.Crypto','Elixir.Archethic.P2P.Node']}, + {load_module,'Elixir.ArchethicWeb.Explorer.SettingsLive',brutal_purge, + soft_purge, + ['Elixir.Archethic.Crypto','Elixir.Archethic.P2P.Node']}, + {load_module,'Elixir.Archethic.Bootstrap',brutal_purge,soft_purge, + ['Elixir.Archethic.Bootstrap.TransactionHandler', + 'Elixir.Archethic.Crypto','Elixir.Archethic.P2P.Node']}, + {update,'Elixir.Archethic.P2P.MemTableLoader', + {advanced,[]}, + brutal_purge,soft_purge, + ['Elixir.Archethic.Crypto','Elixir.Archethic.P2P.MemTable', + 'Elixir.Archethic.P2P.Node']}, + {apply,{supervisor,terminate_child, + ['Elixir.Archethic.Telemetry',prometheus_metrics]}}, + {apply,{supervisor,restart_child, + ['Elixir.Archethic.Telemetry',prometheus_metrics]}}, + {apply,{supervisor,terminate_child, + ['Elixir.Archethic.Telemetry',telemetry_poller]}}, + {apply,{supervisor,restart_child, + ['Elixir.Archethic.Telemetry',telemetry_poller]}}, + {apply,{'Elixir.Archethic.Contracts.Loader',reparse_workers_contract,[]}}, + {apply,{'Elixir.Archethic.P2P.MemTable', migrate_ets_table_1_5_6, []}}, + {apply,{'Elixir.Archethic.Crypto.NodeKeystore.SoftwareImpl', migrate_ets_table_1_5_6, []}}, + {apply,{'Elixir.Archethic.Crypto.SharedSecretsKeystore.SoftwareImpl', migrate_ets_table_1_5_6, []}}, + {apply,{'Elixir.Archethic.PubSub',notify_node_status, [node_up]}}]}], + [{"1.5.8", + [{load_module,'Elixir.Archethic.Cldr.Currency',brutal_purge,soft_purge,[]}, + {load_module,'Elixir.Archethic.Cldr.Number.Transliterate',brutal_purge, + soft_purge,[]}, + {load_module,'Elixir.Archethic.Contracts.Interpreter.Library.Common.List', + brutal_purge,soft_purge,[]}, + {load_module,'Elixir.Archethic.Contracts.Interpreter.Library.Common.Math', + brutal_purge,soft_purge,[]}, + {load_module,'Elixir.Archethic.Crypto.ID',brutal_purge,soft_purge,[]}, + {load_module,'Elixir.Archethic.Crypto.NodeKeystore',brutal_purge, + soft_purge,[]}, + {load_module,'Elixir.Archethic.P2P.Message.ShardRepair',brutal_purge, + soft_purge,[]}, + {load_module,'Elixir.Archethic.P2P.Node',brutal_purge,soft_purge,[]}, + {load_module,'Elixir.ArchethicWeb.Explorer.LayoutView',brutal_purge, + soft_purge,[]}, + {load_module,'Elixir.ArchethicWeb.Explorer.WorldMapLive',brutal_purge, + soft_purge,[]}, + {load_module,'Elixir.ArchethicWeb.ExplorerRouter',brutal_purge,soft_purge, + []}, + {load_module,'Elixir.Archethic.Crypto',brutal_purge,soft_purge, + ['Elixir.Archethic.Crypto.ID', + 'Elixir.Archethic.Crypto.NodeKeystore']}, + {update,'Elixir.Archethic.Contracts.Worker', + {advanced,[]}, + brutal_purge,soft_purge, + ['Elixir.Archethic.Crypto']}, + {update,'Elixir.Archethic.Crypto.SharedSecretsKeystore.SoftwareImpl', + {advanced,[]}, + brutal_purge,soft_purge, + ['Elixir.Archethic.Crypto']}, + {update,'Elixir.Archethic.P2P.BootstrappingSeeds', + {advanced,[]}, + brutal_purge,soft_purge, + ['Elixir.Archethic.Crypto']}, + {update,'Elixir.Archethic.P2P.MemTable', + {advanced,[]}, + brutal_purge,soft_purge, + ['Elixir.Archethic.P2P.Node']}, + {update,'Elixir.Archethic.SelfRepair.Notifier', + {advanced,[]}, + brutal_purge,soft_purge, + ['Elixir.Archethic.Crypto']}, + {load_module,'Elixir.ArchethicWeb.Explorer.NodeDetailsLive',brutal_purge, + soft_purge, + ['Elixir.Archethic.Crypto']}, + {load_module,'Elixir.Archethic.Bootstrap.TransactionHandler',brutal_purge, + soft_purge, + ['Elixir.Archethic.Crypto','Elixir.Archethic.P2P.Node']}, + {update,'Elixir.Archethic.Crypto.NodeKeystore.SoftwareImpl', + {advanced,[]}, + brutal_purge,soft_purge, + ['Elixir.Archethic.Crypto','Elixir.Archethic.Crypto.ID']}, + {load_module,'Elixir.Archethic.Mining.PendingTransactionValidation', + brutal_purge,soft_purge, + ['Elixir.Archethic.Crypto','Elixir.Archethic.P2P.Node']}, + {load_module,'Elixir.Archethic.Mining.ProofOfWork',brutal_purge, + soft_purge, + ['Elixir.Archethic.Crypto','Elixir.Archethic.P2P.Node']}, + {update,'Elixir.Archethic.Networking.Scheduler', + {advanced,[]}, + brutal_purge,soft_purge, + ['Elixir.Archethic.Crypto','Elixir.Archethic.P2P.Node']}, + {update,'Elixir.Archethic.SharedSecrets.MemTablesLoader', + {advanced,[]}, + brutal_purge,soft_purge, + ['Elixir.Archethic.Crypto','Elixir.Archethic.P2P.Node']}, + {load_module,'Elixir.ArchethicWeb.Explorer.ExplorerView',brutal_purge, + soft_purge, + ['Elixir.Archethic.Crypto','Elixir.Archethic.P2P.Node']}, + {load_module,'Elixir.ArchethicWeb.Explorer.SettingsLive',brutal_purge, + soft_purge, + ['Elixir.Archethic.Crypto','Elixir.Archethic.P2P.Node']}, + {load_module,'Elixir.Archethic.Bootstrap',brutal_purge,soft_purge, + ['Elixir.Archethic.Bootstrap.TransactionHandler', + 'Elixir.Archethic.Crypto','Elixir.Archethic.P2P.Node']}, + {update,'Elixir.Archethic.P2P.MemTableLoader', + {advanced,[]}, + brutal_purge,soft_purge, + ['Elixir.Archethic.Crypto','Elixir.Archethic.P2P.MemTable', + 'Elixir.Archethic.P2P.Node']}]}]}. diff --git a/test/archethic/bootstrap/sync_test.exs b/test/archethic/bootstrap/sync_test.exs index ec22a85eb..94dea3858 100644 --- a/test/archethic/bootstrap/sync_test.exs +++ b/test/archethic/bootstrap/sync_test.exs @@ -313,9 +313,10 @@ defmodule Archethic.Bootstrap.SyncTest do 3000, 4000, :tcp, - <<0::8, 0::8, :crypto.strong_rand_bytes(32)::binary>>, - <<0::8, 0::8, :crypto.strong_rand_bytes(32)::binary>>, - :crypto.strong_rand_bytes(64) + ArchethicCase.random_public_key(), + ArchethicCase.random_public_key(), + :crypto.strong_rand_bytes(64), + Crypto.generate_random_keypair(:bls) |> elem(0) ) }) diff --git a/test/archethic/bootstrap/transaction_handler_test.exs b/test/archethic/bootstrap/transaction_handler_test.exs index dfa9e5eba..f0053f2c1 100644 --- a/test/archethic/bootstrap/transaction_handler_test.exs +++ b/test/archethic/bootstrap/transaction_handler_test.exs @@ -32,8 +32,10 @@ defmodule Archethic.Bootstrap.TransactionHandlerTest do <<0::8, 0::8, :crypto.strong_rand_bytes(32)::binary>> ) - assert {:ok, {127, 0, 0, 1}, 3000, 4000, :tcp, _reward_address, _origin_public_key, _cert} = - Node.decode_transaction_content(content) + assert {:ok, {127, 0, 0, 1}, 3000, 4000, :tcp, _reward_address, _origin_public_key, _cert, + mining_public_key} = Node.decode_transaction_content(content) + + assert Archethic.Crypto.mining_node_public_key() == mining_public_key end test "send_transaction/2 should send the transaction to a welcome node" do diff --git a/test/archethic/crypto/keystore/shared_secrets/software_impl_test.exs b/test/archethic/crypto/keystore/shared_secrets/software_impl_test.exs index e2be6644c..99325e56d 100644 --- a/test/archethic/crypto/keystore/shared_secrets/software_impl_test.exs +++ b/test/archethic/crypto/keystore/shared_secrets/software_impl_test.exs @@ -50,38 +50,22 @@ defmodule Archethic.Crypto.SharedSecrets.SoftwareImplTest do daily_nonce_keypair = Crypto.generate_deterministic_keypair(daily_nonce_seed) - unix_timestamp = DateTime.to_unix(~U[2021-10-10 00:00:00Z]) + assert 1 == Keystore.get_node_shared_key_index() + assert 2 == Keystore.get_reward_key_index() - assert [{_, 1}] = :ets.lookup(:archethic_shared_secrets_keystore, :shared_secrets_index) - assert [{_, 2}] = :ets.lookup(:archethic_shared_secrets_keystore, :reward_index) - - assert [{^unix_timestamp, sign_fun}] = :ets.tab2list(:archethic_shared_secrets_daily_keys) - assert sign_fun.("hello") == Crypto.sign("hello", elem(daily_nonce_keypair, 1)) + assert Keystore.sign_with_daily_nonce_key("hello", ~U[2021-10-10 00:00:00Z]) == + Crypto.sign("hello", elem(daily_nonce_keypair, 1)) end end test "unwrap_secrets/3 should load encrypted secrets by decrypting them" do {:ok, _pid} = Keystore.start_link() + {_daily_nonce_seed, transaction_seed, reward_seed} = load_secrets(~U[2021-04-08 06:35:17Z]) - timestamp = ~U[2021-04-08 06:35:17Z] - - {daily_nonce_seed, transaction_seed, reward_seed} = load_secrets(~U[2021-04-08 06:35:17Z]) - - unix_timestamp = DateTime.to_unix(timestamp) - - assert [{^unix_timestamp, sign_fun}] = :ets.tab2list(:archethic_shared_secrets_daily_keys) - [{_, tx_sign_fun}] = :ets.lookup(:archethic_shared_secrets_keystore, :transaction_sign_fun) - - [{_, reward_sign_fun}] = :ets.lookup(:archethic_shared_secrets_keystore, :reward_sign_fun) + assert transaction_seed |> Crypto.derive_keypair(0) |> elem(0) == + Keystore.node_shared_secrets_public_key(0) - {_, pv} = Crypto.generate_deterministic_keypair(daily_nonce_seed) - assert sign_fun.("hello") == Crypto.sign("hello", pv) - - {_, pv} = Crypto.derive_keypair(transaction_seed, 0) - assert tx_sign_fun.("hello", 0) == Crypto.sign("hello", pv) - - {_, pv} = Crypto.derive_keypair(reward_seed, 0) - assert reward_sign_fun.("hello", 0) == Crypto.sign("hello", pv) + assert reward_seed |> Crypto.derive_keypair(0) |> elem(0) == Keystore.reward_public_key(0) end test "sign_with_node_shared_secrets_key/1 should sign the data with the latest node shared secrets private key" do diff --git a/test/archethic/crypto_test.exs b/test/archethic/crypto_test.exs index 61e3aa23d..a4274b9a5 100644 --- a/test/archethic/crypto_test.exs +++ b/test/archethic/crypto_test.exs @@ -154,4 +154,14 @@ defmodule CryptoTest do assert_receive {:network_seed, ^network_seed} end end + + test "aggregate_signatures/1 should produce a valid signature" do + {pub1, pv1} = Crypto.generate_deterministic_keypair("seed1", :bls) + {pub2, pv2} = Crypto.generate_deterministic_keypair("seed2", :bls) + sig1 = Crypto.sign("hello", pv1) + sig2 = Crypto.sign("hello", pv2) + aggregated_signature = Crypto.aggregate_signatures([sig1, sig2], [pub1, pub2]) + aggregated_public_key = Crypto.aggregate_mining_public_keys([pub1, pub2]) + assert Crypto.verify?(aggregated_signature, "hello", aggregated_public_key) + end end diff --git a/test/archethic/mining/distributed_workflow_test.exs b/test/archethic/mining/distributed_workflow_test.exs index 8ab1628d1..62b81ffa7 100644 --- a/test/archethic/mining/distributed_workflow_test.exs +++ b/test/archethic/mining/distributed_workflow_test.exs @@ -112,7 +112,8 @@ defmodule Archethic.Mining.DistributedWorkflowTest do <<0, 0, 16, 233, 156, 172, 143, 228, 236, 12, 227, 76, 1, 80, 12, 236, 69, 10, 209, 6, 234, 172, 97, 188, 240, 207, 70, 115, 64, 117, 44, 82, 132, 186>>, origin_public_key, - certificate + certificate, + Crypto.generate_random_keypair(:bls) |> elem(0) ) }) diff --git a/test/archethic/mining/pending_transaction_validation_test.exs b/test/archethic/mining/pending_transaction_validation_test.exs index a4ce6c619..861edcebe 100644 --- a/test/archethic/mining/pending_transaction_validation_test.exs +++ b/test/archethic/mining/pending_transaction_validation_test.exs @@ -548,7 +548,8 @@ defmodule Archethic.Mining.PendingTransactionValidationTest do <<0, 0, 4, 221, 19, 74, 75, 69, 16, 50, 149, 253, 24, 115, 128, 241, 110, 118, 139, 7, 48, 217, 58, 43, 145, 233, 77, 125, 190, 207, 31, 64, 157, 137>>, origin_public_key, - certificate + certificate, + Crypto.generate_random_keypair(:bls) |> elem(0) ) tx = TransactionFactory.create_non_valided_transaction(type: :node, content: content) @@ -581,7 +582,8 @@ defmodule Archethic.Mining.PendingTransactionValidationTest do <<0, 0, 4, 221, 19, 74, 75, 69, 16, 50, 149, 253, 24, 115, 128, 241, 110, 118, 139, 7, 48, 217, 58, 43, 145, 233, 77, 125, 190, 207, 31, 64, 157, 137>>, <<0::8, 0::8, :crypto.strong_rand_bytes(32)::binary>>, - certificate + certificate, + Crypto.generate_random_keypair(:bls) |> elem(0) ) tx = diff --git a/test/archethic/p2p/mem_table_test.exs b/test/archethic/p2p/mem_table_test.exs index 472f5ce67..49251a477 100644 --- a/test/archethic/p2p/mem_table_test.exs +++ b/test/archethic/p2p/mem_table_test.exs @@ -4,5 +4,491 @@ defmodule Archethic.P2P.MemTableTest do alias Archethic.P2P.MemTable alias Archethic.P2P.Node - doctest MemTable + describe "add_node/1" do + test "should insert a node in the P2P table" do + MemTable.start_link() + + node = %Node{ + ip: {127, 0, 0, 1}, + port: 3000, + http_port: 4000, + first_public_key: "key1", + last_public_key: "key2", + geo_patch: "AFZ", + network_patch: "AAA", + average_availability: 0.9, + available?: true, + synced?: true, + authorized?: true, + authorization_date: ~U[2020-10-22 23:19:45.797109Z], + enrollment_date: ~U[2020-10-22 23:19:45.797109Z], + last_update_date: ~U[2020-10-22 23:19:45.797109Z], + availability_update: ~U[2020-10-22 23:19:45.797109Z], + transport: :tcp, + reward_address: + <<0, 163, 237, 233, 93, 14, 241, 241, 8, 144, 218, 105, 16, 138, 243, 223, 17, 182, 87, + 9, 7, 53, 146, 174, 125, 5, 244, 42, 35, 209, 142, 24, 164>>, + last_address: + <<0, 165, 32, 187, 102, 112, 133, 38, 17, 232, 54, 228, 173, 254, 94, 179, 32, 173, 88, + 122, 234, 88, 139, 82, 26, 113, 42, 8, 183, 190, 163, 221, 112>>, + origin_public_key: + <<0, 0, 172, 147, 188, 9, 66, 252, 112, 77, 143, 178, 233, 51, 125, 102, 244, 36, 232, + 185, 38, 7, 238, 128, 41, 30, 192, 61, 223, 119, 62, 249, 39, 212>> + } + + :ok = MemTable.add_node(node) + + { + :ets.tab2list(:archethic_node_discovery), + :ets.tab2list(:archethic_authorized_nodes), + :ets.tab2list(:archethic_node_keys) + } + + assert [ + { + "key1", + "key2", + {127, 0, 0, 1}, + 3000, + 4000, + "AFZ", + "AAA", + 0.9, + ~U[2020-10-22 23:19:45.797109Z], + :tcp, + <<0, 163, 237, 233, 93, 14, 241, 241, 8, 144, 218, 105, 16, 138, 243, 223, 17, + 182, 87, 9, 7, 53, 146, 174, 125, 5, 244, 42, 35, 209, 142, 24, 164>>, + <<0, 165, 32, 187, 102, 112, 133, 38, 17, 232, 54, 228, 173, 254, 94, 179, 32, + 173, 88, 122, 234, 88, 139, 82, 26, 113, 42, 8, 183, 190, 163, 221, 112>>, + <<0, 0, 172, 147, 188, 9, 66, 252, 112, 77, 143, 178, 233, 51, 125, 102, 244, 36, + 232, 185, 38, 7, 238, 128, 41, 30, 192, 61, 223, 119, 62, 249, 39, 212>>, + true, + ~U[2020-10-22 23:19:45.797109Z], + true, + ~U[2020-10-22 23:19:45.797109Z], + nil + } + ] = :ets.tab2list(:archethic_node_discovery) + + assert [{"key1", ~U[2020-10-22 23:19:45.797109Z]}] = + :ets.tab2list(:archethic_authorized_nodes) + + assert([{"key2", "key1"}] = :ets.tab2list(:archethic_node_keys)) + end + + test "should update a node entry" do + MemTable.start_link() + + node = %Node{ + ip: {127, 0, 0, 1}, + port: 3000, + http_port: 4000, + first_public_key: "key1", + last_public_key: "key2", + geo_patch: "AFZ", + network_patch: "AAA", + average_availability: 0.9, + available?: true, + synced?: true, + authorized?: true, + authorization_date: ~U[2020-10-22 23:19:45.797109Z], + enrollment_date: ~U[2020-10-22 23:19:45.797109Z], + last_update_date: ~U[2020-10-22 23:19:45.797109Z], + availability_update: ~U[2020-10-22 23:19:45.797109Z], + transport: :tcp, + reward_address: + <<0, 163, 237, 233, 93, 14, 241, 241, 8, 144, 218, 105, 16, 138, 243, 223, 17, 182, 87, + 9, 7, 53, 146, 174, 125, 5, 244, 42, 35, 209, 142, 24, 164>>, + last_address: + <<0, 165, 32, 187, 102, 112, 133, 38, 17, 232, 54, 228, 173, 254, 94, 179, 32, 173, 88, + 122, 234, 88, 139, 82, 26, 113, 42, 8, 183, 190, 163, 221, 112>>, + origin_public_key: + <<0, 0, 172, 147, 188, 9, 66, 252, 112, 77, 143, 178, 233, 51, 125, 102, 244, 36, 232, + 185, 38, 7, 238, 128, 41, 30, 192, 61, 223, 119, 62, 249, 39, 212>> + } + + :ok = MemTable.add_node(node) + + :ok = + MemTable.add_node(%{ + node + | ip: {80, 20, 10, 122}, + port: 5000, + last_public_key: "key5", + synced?: false, + last_update_date: ~U[2020-10-22 23:20:45.797109Z], + availability_update: ~U[2020-10-23 23:20:45.797109Z], + available?: false, + transport: :sctp, + mining_public_key: + <<3, 0, 224, 186, 136, 105, 213, 175, 202, 16, 163, 252, 116, 117, 68, 105, 114, 78, + 141, 48, 56, 211, 235, 26, 97, 145, 234, 76, 202, 52, 251, 52, 161, 200>> + }) + + assert [ + { + "key1", + "key5", + {80, 20, 10, 122}, + 5000, + 4000, + "AFZ", + "AAA", + 0.9, + ~U[2020-10-22 23:19:45.797109Z], + :sctp, + <<0, 163, 237, 233, 93, 14, 241, 241, 8, 144, 218, 105, 16, 138, 243, 223, 17, + 182, 87, 9, 7, 53, 146, 174, 125, 5, 244, 42, 35, 209, 142, 24, 164>>, + <<0, 165, 32, 187, 102, 112, 133, 38, 17, 232, 54, 228, 173, 254, 94, 179, 32, + 173, 88, 122, 234, 88, 139, 82, 26, 113, 42, 8, 183, 190, 163, 221, 112>>, + <<0, 0, 172, 147, 188, 9, 66, 252, 112, 77, 143, 178, 233, 51, 125, 102, 244, 36, + 232, 185, 38, 7, 238, 128, 41, 30, 192, 61, 223, 119, 62, 249, 39, 212>>, + false, + ~U[2020-10-22 23:20:45.797109Z], + false, + ~U[2020-10-23 23:20:45.797109Z], + <<3, 0, 224, 186, 136, 105, 213, 175, 202, 16, 163, 252, 116, 117, 68, 105, 114, + 78, 141, 48, 56, 211, 235, 26, 97, 145, 234, 76, 202, 52, 251, 52, 161, 200>> + } + ] = :ets.lookup(:archethic_node_discovery, "key1") + end + end + + describe "get_node/1" do + test "should retrieve node by the first public key" do + MemTable.start_link() + + node = %Node{ + ip: {127, 0, 0, 1}, + port: 3000, + http_port: 4000, + first_public_key: "key1", + last_public_key: "key2" + } + + :ok = MemTable.add_node(node) + assert {:ok, node} == MemTable.get_node("key1") + end + + test "should retrieve node by the last public key" do + MemTable.start_link() + + node = %Node{ + ip: {127, 0, 0, 1}, + port: 3000, + http_port: 4000, + first_public_key: "key1", + last_public_key: "key2" + } + + :ok = MemTable.add_node(node) + assert {:ok, node} == MemTable.get_node("key2") + end + + test "should return an error if the node is not found" do + MemTable.start_link() + assert {:error, :not_found} = MemTable.get_node("key10") + end + end + + test "list_nodes/0 should list all the nodes in the table" do + MemTable.start_link() + + node = %Node{ + ip: {127, 0, 0, 1}, + port: 3000, + http_port: 4000, + first_public_key: "key1", + last_public_key: "key2" + } + + MemTable.add_node(node) + assert [node] == MemTable.list_nodes() + end + + test "authorized_nodes/0 should list only the authorized nodes" do + MemTable.start_link() + + node1 = %Node{ + ip: {127, 0, 0, 1}, + port: 3000, + http_port: 4000, + first_public_key: "key1", + last_public_key: "key2" + } + + MemTable.add_node(node1) + + node2 = %Node{ + ip: {127, 0, 0, 1}, + port: 3000, + http_port: 4000, + first_public_key: "key3", + last_public_key: "key3", + authorized?: true, + available?: true, + authorization_date: ~U[2020-10-22 23:19:45.797109Z] + } + + MemTable.add_node(node2) + assert [node2] == MemTable.authorized_nodes() + end + + test "available_nodes/0 shoud list only the nodes which are globally available" do + MemTable.start_link() + + node1 = %Node{ + ip: {127, 0, 0, 1}, + port: 3000, + http_port: 4000, + first_public_key: "key1", + last_public_key: "key2" + } + + MemTable.add_node(node1) + + node2 = %Node{ + ip: {127, 0, 0, 1}, + port: 3000, + http_port: 4000, + first_public_key: "key3", + last_public_key: "key3", + available?: true, + authorized?: true, + authorization_date: DateTime.utc_now() + } + + MemTable.add_node(node2) + assert [node2] == MemTable.available_nodes() + end + + test "list_node_first_public_keys/0 should list all the node first public keys" do + MemTable.start_link() + + node1 = %Node{ + ip: {127, 0, 0, 1}, + port: 3000, + http_port: 4000, + first_public_key: "key1", + last_public_key: "key2" + } + + MemTable.add_node(node1) + + node2 = %Node{ + ip: {127, 0, 0, 1}, + port: 3000, + http_port: 4000, + first_public_key: "key3", + last_public_key: "key3" + } + + MemTable.add_node(node2) + assert ["key1", "key3"] = MemTable.list_node_first_public_keys() + end + + test "list_authorized_public_keys/0 should list all the public keys of the authorized nodes" do + MemTable.start_link() + + node1 = %Node{ + ip: {127, 0, 0, 1}, + port: 3000, + http_port: 4000, + first_public_key: "key1", + last_public_key: "key2" + } + + MemTable.add_node(node1) + + node2 = %Node{ + ip: {127, 0, 0, 1}, + port: 3000, + http_port: 4000, + first_public_key: "key3", + last_public_key: "key3", + authorized?: true, + authorization_date: ~U[2020-10-22 23:19:45.797109Z] + } + + MemTable.add_node(node2) + assert ["key3"] = MemTable.list_authorized_public_keys() + end + + test "authorize_node/2 should define a node as authorized" do + MemTable.start_link() + + :ok = + MemTable.add_node(%Node{ + ip: {127, 0, 0, 1}, + port: 3000, + http_port: 4000, + first_public_key: "key1", + last_public_key: "key1" + }) + + :ok = MemTable.authorize_node("key1", ~U[2020-10-22 23:45:41.181903Z]) + assert ["key1"] = MemTable.list_authorized_public_keys() + end + + test "unauthorize_node/1 should unset a node as authorized" do + MemTable.start_link() + + :ok = + MemTable.add_node(%Node{ + ip: {127, 0, 0, 1}, + port: 3000, + http_port: 4000, + first_public_key: "key1", + last_public_key: "key1", + authorized?: true, + authorization_date: ~U[2020-10-22 23:45:41.181903Z] + }) + + assert ["key1"] = MemTable.list_authorized_public_keys() + + :ok = MemTable.unauthorize_node("key1") + assert [] = MemTable.list_authorized_public_keys() + end + + describe "get_first_node_key/1" do + test "should retrieve first node key from the first key" do + MemTable.start_link() + + node = %Node{ + ip: {127, 0, 0, 1}, + port: 3000, + http_port: 4000, + first_public_key: "key1", + last_public_key: "key2" + } + + MemTable.add_node(node) + assert "key1" = MemTable.get_first_node_key("key1") + end + + test "should retrieve first node key from the last key" do + MemTable.start_link() + + node = %Node{ + ip: {127, 0, 0, 1}, + port: 3000, + http_port: 4000, + first_public_key: "key1", + last_public_key: "key2" + } + + MemTable.add_node(node) + assert "key1" = MemTable.get_first_node_key("key2") + end + end + + test "set_node_available/2 should define a node as available" do + MemTable.start_link() + + node = %Node{ + ip: {127, 0, 0, 1}, + port: 3000, + http_port: 4000, + first_public_key: "key1", + last_public_key: "key2" + } + + MemTable.add_node(node) + :ok = MemTable.set_node_available("key1", ~U[2020-10-22 23:45:41Z]) + + {:ok, %Node{available?: true, availability_update: ~U[2020-10-22 23:45:41Z]}} = + MemTable.get_node("key1") + + MemTable.add_node(node) + :ok = MemTable.set_node_available("key1", ~U[2020-10-23 23:45:41Z]) + + assert {:ok, %Node{available?: true, availability_update: ~U[2020-10-23 23:45:41Z]}} = + MemTable.get_node("key1") + end + + test "set_node_unavailable/2 should define a node as unavailable" do + MemTable.start_link() + + node = %Node{ + ip: {127, 0, 0, 1}, + port: 3000, + http_port: 4000, + first_public_key: "key1", + last_public_key: "key2" + } + + MemTable.add_node(node) + :ok = MemTable.set_node_available("key1", ~U[2020-10-22 23:45:41Z]) + :ok = MemTable.set_node_unavailable("key1", ~U[2020-10-23 23:45:41Z]) + + assert {:ok, %Node{available?: false, availability_update: ~U[2020-10-23 23:45:41Z]}} = + MemTable.get_node("key1") + end + + test "set_node_synced/1 should define a node as synchronized" do + MemTable.start_link() + + node = %Node{ + ip: {127, 0, 0, 1}, + port: 3000, + http_port: 4000, + first_public_key: "key1", + last_public_key: "key2" + } + + MemTable.add_node(node) + :ok = MemTable.set_node_synced("key1") + assert {:ok, %Node{synced?: true}} = MemTable.get_node("key1") + end + + test "set_node_unsynced/1 should define a node as unsynchronized" do + MemTable.start_link() + + node = %Node{ + ip: {127, 0, 0, 1}, + port: 3000, + http_port: 4000, + first_public_key: "key1", + last_public_key: "key2" + } + + MemTable.add_node(node) + :ok = MemTable.set_node_synced("key1") + :ok = MemTable.set_node_unsynced("key1") + assert {:ok, %Node{synced?: false}} = MemTable.get_node("key1") + end + + test "update_node_average_availability/2 should update node average availability" do + MemTable.start_link() + + node = %Node{ + ip: {127, 0, 0, 1}, + port: 3000, + http_port: 4000, + first_public_key: "key1", + last_public_key: "key2", + average_availability: 0.4 + } + + MemTable.add_node(node) + :ok = MemTable.update_node_average_availability("key1", 0.8) + assert {:ok, %Node{average_availability: 0.8}} = MemTable.get_node("key1") + end + + test "update_node_network_patch/2 should update node network patch" do + MemTable.start_link() + + node = %Node{ + ip: {127, 0, 0, 1}, + port: 3000, + http_port: 4000, + first_public_key: "key1", + last_public_key: "key2", + network_patch: "AAA" + } + + MemTable.add_node(node) + :ok = MemTable.update_node_network_patch("key1", "3FC") + assert {:ok, %Node{network_patch: "3FC"}} = MemTable.get_node("key1") + end end diff --git a/test/archethic/p2p/messages_test.exs b/test/archethic/p2p/messages_test.exs index f2ff6016c..83550c7ab 100644 --- a/test/archethic/p2p/messages_test.exs +++ b/test/archethic/p2p/messages_test.exs @@ -540,12 +540,8 @@ defmodule Archethic.P2P.MessageTest do ip: {127, 0, 0, 1}, port: 3000, http_port: 4000, - first_public_key: - <<0, 0, 182, 67, 168, 252, 227, 203, 142, 164, 142, 248, 159, 209, 249, 247, 86, 64, - 92, 224, 91, 182, 122, 49, 209, 169, 96, 111, 219, 204, 57, 250, 59, 226>>, - last_public_key: - <<0, 0, 182, 67, 168, 252, 227, 203, 142, 164, 142, 248, 159, 209, 249, 247, 86, 64, - 92, 224, 91, 182, 122, 49, 209, 169, 96, 111, 219, 204, 57, 250, 59, 226>>, + first_public_key: ArchethicCase.random_public_key(), + last_public_key: ArchethicCase.random_public_key(), geo_patch: "FA9", network_patch: "AVC", available?: true, @@ -553,15 +549,10 @@ defmodule Archethic.P2P.MessageTest do enrollment_date: ~U[2020-06-26 08:36:11Z], authorization_date: ~U[2020-06-26 08:36:11Z], authorized?: true, - last_address: - <<0, 0, 165, 32, 187, 102, 112, 133, 38, 17, 232, 54, 228, 173, 254, 94, 179, 32, - 173, 88, 122, 234, 88, 139, 82, 26, 113, 42, 8, 183, 190, 163, 221, 112>>, - reward_address: - <<0, 0, 163, 237, 233, 93, 14, 241, 241, 8, 144, 218, 105, 16, 138, 243, 223, 17, - 182, 87, 9, 7, 53, 146, 174, 125, 5, 244, 42, 35, 209, 142, 24, 164>>, - origin_public_key: - <<0, 0, 76, 168, 99, 61, 84, 206, 158, 226, 212, 161, 60, 62, 55, 101, 249, 142, - 174, 178, 157, 241, 148, 35, 19, 177, 109, 40, 224, 179, 31, 66, 129, 4>> + last_address: ArchethicCase.random_address(), + reward_address: ArchethicCase.random_address(), + origin_public_key: ArchethicCase.random_public_key(), + mining_public_key: ArchethicCase.random_public_key() } ] } @@ -594,12 +585,8 @@ defmodule Archethic.P2P.MessageTest do ip: {127, 0, 0, 1}, port: 3000, http_port: 4000, - first_public_key: - <<0, 0, 182, 67, 168, 252, 227, 203, 142, 164, 142, 248, 159, 209, 249, 247, 86, 64, - 92, 224, 91, 182, 122, 49, 209, 169, 96, 111, 219, 204, 57, 250, 59, 226>>, - last_public_key: - <<0, 0, 182, 67, 168, 252, 227, 203, 142, 164, 142, 248, 159, 209, 249, 247, 86, 64, - 92, 224, 91, 182, 122, 49, 209, 169, 96, 111, 219, 204, 57, 250, 59, 226>>, + first_public_key: ArchethicCase.random_public_key(), + last_public_key: ArchethicCase.random_public_key(), geo_patch: "FA9", network_patch: "AVC", available?: true, @@ -607,15 +594,10 @@ defmodule Archethic.P2P.MessageTest do enrollment_date: ~U[2020-06-26 08:36:11Z], authorization_date: ~U[2020-06-26 08:36:11Z], authorized?: true, - reward_address: - <<0, 0, 163, 237, 233, 93, 14, 241, 241, 8, 144, 218, 105, 16, 138, 243, 223, 17, - 182, 87, 9, 7, 53, 146, 174, 125, 5, 244, 42, 35, 209, 142, 24, 164>>, - last_address: - <<0, 0, 165, 32, 187, 102, 112, 133, 38, 17, 232, 54, 228, 173, 254, 94, 179, 32, - 173, 88, 122, 234, 88, 139, 82, 26, 113, 42, 8, 183, 190, 163, 221, 112>>, - origin_public_key: - <<0, 0, 76, 168, 99, 61, 84, 206, 158, 226, 212, 161, 60, 62, 55, 101, 249, 142, - 174, 178, 157, 241, 148, 35, 19, 177, 109, 40, 224, 179, 31, 66, 129, 4>> + reward_address: ArchethicCase.random_address(), + last_address: ArchethicCase.random_address(), + origin_public_key: ArchethicCase.random_public_key(), + mining_public_key: ArchethicCase.random_public_key() } ], closest_nodes: [ @@ -623,12 +605,8 @@ defmodule Archethic.P2P.MessageTest do ip: {127, 0, 0, 1}, port: 3000, http_port: 4000, - first_public_key: - <<0, 0, 182, 67, 168, 252, 227, 203, 142, 164, 142, 248, 159, 209, 249, 247, 86, 64, - 92, 224, 91, 182, 122, 49, 209, 169, 96, 111, 219, 204, 57, 250, 59, 226>>, - last_public_key: - <<0, 0, 182, 67, 168, 252, 227, 203, 142, 164, 142, 248, 159, 209, 249, 247, 86, 64, - 92, 224, 91, 182, 122, 49, 209, 169, 96, 111, 219, 204, 57, 250, 59, 226>>, + first_public_key: ArchethicCase.random_public_key(), + last_public_key: ArchethicCase.random_public_key(), geo_patch: "FA9", network_patch: "AVC", available?: true, @@ -636,27 +614,18 @@ defmodule Archethic.P2P.MessageTest do enrollment_date: ~U[2020-06-26 08:36:11Z], authorization_date: ~U[2020-06-26 08:36:11Z], authorized?: true, - reward_address: - <<0, 0, 163, 237, 233, 93, 14, 241, 241, 8, 144, 218, 105, 16, 138, 243, 223, 17, - 182, 87, 9, 7, 53, 146, 174, 125, 5, 244, 42, 35, 209, 142, 24, 164>>, - last_address: - <<0, 0, 165, 32, 187, 102, 112, 133, 38, 17, 232, 54, 228, 173, 254, 94, 179, 32, - 173, 88, 122, 234, 88, 139, 82, 26, 113, 42, 8, 183, 190, 163, 221, 112>>, - origin_public_key: - <<0, 0, 76, 168, 99, 61, 84, 206, 158, 226, 212, 161, 60, 62, 55, 101, 249, 142, - 174, 178, 157, 241, 148, 35, 19, 177, 109, 40, 224, 179, 31, 66, 129, 4>> + reward_address: ArchethicCase.random_address(), + last_address: ArchethicCase.random_address(), + origin_public_key: ArchethicCase.random_public_key(), + mining_public_key: ArchethicCase.random_public_key() } ], first_enrolled_node: %Node{ ip: {127, 0, 0, 1}, port: 3000, http_port: 4000, - first_public_key: - <<0, 0, 182, 67, 168, 252, 227, 203, 142, 164, 142, 248, 159, 209, 249, 247, 86, 64, - 92, 224, 91, 182, 122, 49, 209, 169, 96, 111, 219, 204, 57, 250, 59, 226>>, - last_public_key: - <<0, 0, 182, 67, 168, 252, 227, 203, 142, 164, 142, 248, 159, 209, 249, 247, 86, 64, - 92, 224, 91, 182, 122, 49, 209, 169, 96, 111, 219, 204, 57, 250, 59, 226>>, + first_public_key: ArchethicCase.random_public_key(), + last_public_key: ArchethicCase.random_public_key(), geo_patch: "FA9", network_patch: "AVC", available?: true, @@ -664,15 +633,10 @@ defmodule Archethic.P2P.MessageTest do enrollment_date: ~U[2020-06-26 08:36:11Z], authorization_date: ~U[2020-06-26 08:36:11Z], authorized?: true, - reward_address: - <<0, 0, 163, 237, 233, 93, 14, 241, 241, 8, 144, 218, 105, 16, 138, 243, 223, 17, 182, - 87, 9, 7, 53, 146, 174, 125, 5, 244, 42, 35, 209, 142, 24, 164>>, - last_address: - <<0, 0, 165, 32, 187, 102, 112, 133, 38, 17, 232, 54, 228, 173, 254, 94, 179, 32, 173, - 88, 122, 234, 88, 139, 82, 26, 113, 42, 8, 183, 190, 163, 221, 112>>, - origin_public_key: - <<0, 0, 76, 168, 99, 61, 84, 206, 158, 226, 212, 161, 60, 62, 55, 101, 249, 142, 174, - 178, 157, 241, 148, 35, 19, 177, 109, 40, 224, 179, 31, 66, 129, 4>> + reward_address: ArchethicCase.random_address(), + last_address: ArchethicCase.random_address(), + origin_public_key: ArchethicCase.random_public_key(), + mining_public_key: ArchethicCase.random_public_key() } } diff --git a/test/archethic/p2p/node_test.exs b/test/archethic/p2p/node_test.exs index 12ce81758..74be43266 100644 --- a/test/archethic/p2p/node_test.exs +++ b/test/archethic/p2p/node_test.exs @@ -2,4 +2,58 @@ defmodule Archethic.P2P.NodeTest do use ExUnit.Case alias alias Archethic.P2P.Node doctest Node + + test "serialize/deserialize node" do + node = %Node{ + first_public_key: ArchethicCase.random_public_key(), + last_public_key: ArchethicCase.random_public_key(), + ip: {127, 0, 0, 1}, + port: 3000, + http_port: 4000, + transport: :tcp, + geo_patch: "FA9", + network_patch: "AVC", + available?: true, + synced?: true, + average_availability: 0.8, + enrollment_date: ~U[2020-06-26 08:36:11Z], + authorization_date: ~U[2020-06-26 08:36:11Z], + last_update_date: ~U[2020-06-26 08:36:11Z], + availability_update: ~U[2020-06-26 08:36:11Z], + authorized?: true, + reward_address: ArchethicCase.random_address(), + last_address: ArchethicCase.random_address(), + origin_public_key: ArchethicCase.random_public_key(), + mining_public_key: ArchethicCase.random_public_key() + } + + assert {^node, _} = + node + |> Node.serialize() + |> Node.deserialize() + end + + describe "encode/decode " do + test "should encode & decode node transaction content" do + reward_address = ArchethicCase.random_address() + origin_public_key = ArchethicCase.random_public_key() + certificate = "" + mining_public_key = ArchethicCase.random_public_key() + + assert {:ok, {127, 0, 0, 1}, 3000, 4000, :tcp, ^reward_address, ^origin_public_key, + ^certificate, + ^mining_public_key} = + Node.encode_transaction_content( + {127, 0, 0, 1}, + 3000, + 4000, + :tcp, + reward_address, + origin_public_key, + certificate, + mining_public_key + ) + |> Node.decode_transaction_content() + end + end end diff --git a/test/archethic/shared_secrets/mem_tables_loader_test.exs b/test/archethic/shared_secrets/mem_tables_loader_test.exs index c2ae2a2cf..21d820c0f 100644 --- a/test/archethic/shared_secrets/mem_tables_loader_test.exs +++ b/test/archethic/shared_secrets/mem_tables_loader_test.exs @@ -34,7 +34,7 @@ defmodule Archethic.SharedSecrets.MemTablesLoaderTest do describe "load_transaction/1" do test "should load node transaction and extract origin public key from the tx's content" do - origin_public_key = <<0::8, 0::8, :crypto.strong_rand_bytes(32)::binary>> + origin_public_key = ArchethicCase.random_public_key() tx = %Transaction{ type: :node, @@ -45,9 +45,10 @@ defmodule Archethic.SharedSecrets.MemTablesLoaderTest do 3000, 4000, :tcp, - <<0::8, 0::8, :crypto.strong_rand_bytes(32)::binary>>, + ArchethicCase.random_address(), origin_public_key, - :crypto.strong_rand_bytes(64) + :crypto.strong_rand_bytes(32), + Crypto.generate_random_keypair(:bls) |> elem(0) ) } } @@ -59,8 +60,8 @@ defmodule Archethic.SharedSecrets.MemTablesLoaderTest do end test "should load transaction but node add node public key as origin key (already existing)" do - first_public_key = <<0::8, 0::8, :crypto.strong_rand_bytes(32)::binary>> - second_public_key = <<0::8, 0::8, :crypto.strong_rand_bytes(32)::binary>> + first_public_key = ArchethicCase.random_public_key() + second_public_key = ArchethicCase.random_public_key() MockDB |> stub(:get_first_public_key, fn _ -> first_public_key end) @@ -145,6 +146,8 @@ defmodule Archethic.SharedSecrets.MemTablesLoaderTest do } } + node_origin_public_key = ArchethicCase.random_public_key() + node_tx = %Transaction{ type: :node, data: %TransactionData{ @@ -154,10 +157,10 @@ defmodule Archethic.SharedSecrets.MemTablesLoaderTest do 3000, 4000, :tcp, - <<0::8, 0::8, :crypto.strong_rand_bytes(32)::binary>>, - <<0, 0, 174, 5, 254, 137, 242, 45, 117, 124, 241, 11, 154, 120, 62, 254, 137, 49, - 24, 186, 216, 182, 81, 64, 93, 92, 48, 231, 23, 124, 127, 140, 103, 105>>, - :crypto.strong_rand_bytes(32) + ArchethicCase.random_address(), + node_origin_public_key, + :crypto.strong_rand_bytes(32), + Crypto.generate_random_keypair(:bls) |> elem(0) ) } } @@ -194,8 +197,7 @@ defmodule Archethic.SharedSecrets.MemTablesLoaderTest do [ <<0, 0, 44, 109, 55, 248, 40, 227, 68, 248, 1, 34, 31, 172, 75, 3, 244, 11, 58, 245, 170, 246, 70, 204, 242, 12, 14, 36, 248, 240, 71, 218, 245, 78>>, - <<0, 0, 174, 5, 254, 137, 242, 45, 117, 124, 241, 11, 154, 120, 62, 254, 137, 49, 24, - 186, 216, 182, 81, 64, 93, 92, 48, 231, 23, 124, 127, 140, 103, 105>>, + node_origin_public_key, <<0, 1, 39, 103, 38, 51, 71, 159, 74, 33, 122, 134, 153, 147, 202, 66, 229, 213, 140, 129, 186, 156, 39, 168, 129, 94, 161, 133, 2, 177, 176, 158, 246, 10>> ] ++ @origin_genesis_public_keys diff --git a/test/support/template.ex b/test/support/template.ex index 4e78927b9..ee66b86a3 100644 --- a/test/support/template.ex +++ b/test/support/template.ex @@ -139,6 +139,14 @@ defmodule ArchethicCase do {_, <<_::8, _::8, pv::binary>>} = Crypto.derive_keypair("seed", 0, :secp256r1) :crypto.compute_key(:ecdh, pub, pv, :secp256r1) end) + |> stub(:mining_public_key, fn -> + {pub, _} = Crypto.generate_deterministic_keypair("seed", :bls) + pub + end) + |> stub(:sign_with_mining_key, fn data -> + {_, pv} = Crypto.generate_deterministic_keypair("seed", :bls) + Crypto.sign(data, pv) + end) MockCrypto.SharedSecretsKeystore |> stub(:sign_with_node_shared_secrets_key, fn data ->