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?(<
Mining public key
++ <%= Base.encode16(@node.mining_public_key) %> +
++ <%= Base.encode16(:binary.part(@node.mining_public_key, 0, 13)) %>... +
+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 ->