From 50b9c7508ba545a8a4c6883714031bc4bb9c9c7a Mon Sep 17 00:00:00 2001
From: Samuel Manzanera
Date: Wed, 3 Jul 2024 22:41:08 +0200
Subject: [PATCH 01/11] Add BLS curve support for mining signatures
---
config/config.exs | 3 +-
lib/archethic/crypto.ex | 70 +++++++++++++++++--
lib/archethic/crypto/id.ex | 2 +
lib/archethic/crypto/keystore/node.ex | 3 +
.../crypto/keystore/node/software_impl.ex | 36 ++++++++++
mix.exs | 3 +-
mix.lock | 4 +-
test/archethic/crypto_test.exs | 10 +++
test/support/template.ex | 8 +++
9 files changed, 130 insertions(+), 9 deletions(-)
diff --git a/config/config.exs b/config/config.exs
index 787b49594c..908d339c9f 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/crypto.ex b/lib/archethic/crypto.ex
index 58f5fd4e92..61fc81dba3 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
+ extended_seed = :crypto.hash(:sha512, seed)
+
+ keypair = {
+ BlsEx.get_public_key(extended_seed),
+ extended_seed
+ }
+
+ 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 4bf3a81797..b4f10a6d40 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 601be74695..583456cbb8 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 1976722341..fb99b33462 100644
--- a/lib/archethic/crypto/keystore/node/software_impl.ex
+++ b/lib/archethic/crypto/keystore/node/software_impl.ex
@@ -97,6 +97,20 @@ defmodule Archethic.Crypto.NodeKeystore.SoftwareImpl do
dh_fun.(public_key, index)
end
+ @impl NodeKeystore
+ @spec sign_with_mining_key(iodata()) :: binary()
+ def sign_with_mining_key(data) do
+ sign_fun = get_mining_sign_fun()
+ sign_fun.(data)
+ end
+
+ @impl NodeKeystore
+ @spec mining_public_key() :: binary()
+ def mining_public_key do
+ public_key_fun = get_mining_public_key_fun()
+ public_key_fun.()
+ end
+
defp get_last_key_index do
[{_, index}] = :ets.lookup(@keystore_table, :last_index)
index
@@ -127,6 +141,16 @@ defmodule Archethic.Crypto.NodeKeystore.SoftwareImpl do
fun
end
+ defp get_mining_sign_fun do
+ [{_, fun}] = :ets.lookup(@keystore_table, :sign_mining_fun)
+ fun
+ end
+
+ defp get_mining_public_key_fun do
+ [{_, fun}] = :ets.lookup(@keystore_table, :public_key_mining_fun)
+ fun
+ end
+
defp do_diffie_helmann(<>, public_key) do
case ID.to_curve(curve_id) do
:ed25519 ->
@@ -159,9 +183,21 @@ defmodule Archethic.Crypto.NodeKeystore.SoftwareImpl do
do_diffie_helmann(pv, public_key)
end
+ sign_mining_fun = fn data ->
+ {_, pv} = Crypto.generate_deterministic_keypair(node_seed, :bls)
+ Crypto.sign(data, pv)
+ end
+
+ public_key_mining_fn = fn ->
+ {pub, _} = Crypto.generate_deterministic_keypair(node_seed, :bls)
+ pub
+ 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})
+ :ets.insert(@keystore_table, {:sign_mining_fun, sign_mining_fun})
+ :ets.insert(@keystore_table, {:public_key_mining_fun, public_key_mining_fn})
unless File.exists?(Utils.mut_dir("crypto")) do
File.mkdir_p!(Utils.mut_dir("crypto"))
diff --git a/mix.exs b/mix.exs
index 5a4f83d64f..71cc4ba891 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 bae200dac1..bd31bf2c2a 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/test/archethic/crypto_test.exs b/test/archethic/crypto_test.exs
index 61e3aa23d8..a4274b9a55 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/support/template.ex b/test/support/template.ex
index 4e78927b99..ee66b86a3f 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 ->
From 77a82d6c50c6f9d446e38e231731fadc96295b42 Mon Sep 17 00:00:00 2001
From: Samuel Manzanera
Date: Wed, 3 Jul 2024 23:19:30 +0200
Subject: [PATCH 02/11] Register node's mining BLS key
---
lib/archethic/bootstrap.ex | 4 +-
.../bootstrap/transaction_handler.ex | 4 +-
.../mining/pending_transaction_validation.ex | 11 +-
lib/archethic/mining/proof_of_work.ex | 2 +-
lib/archethic/networking/scheduler.ex | 4 +-
lib/archethic/p2p/mem_table.ex | 508 +-----------------
lib/archethic/p2p/mem_table_loader.ex | 10 +-
lib/archethic/p2p/node.ex | 215 ++------
.../shared_secrets/mem_tables_loader.ex | 4 +-
.../explorer/live/node_details_live.html.heex | 12 +
.../explorer/live/settings_live.ex | 6 +-
.../explorer/views/explorer_view.ex | 14 +-
test/archethic/bootstrap/sync_test.exs | 7 +-
.../bootstrap/transaction_handler_test.exs | 6 +-
.../mining/distributed_workflow_test.exs | 3 +-
.../pending_transaction_validation_test.exs | 6 +-
test/archethic/p2p/mem_table_test.exs | 488 ++++++++++++++++-
test/archethic/p2p/messages_test.exs | 84 +--
test/archethic/p2p/node_test.exs | 74 +++
.../shared_secrets/mem_tables_loader_test.exs | 24 +-
20 files changed, 723 insertions(+), 763 deletions(-)
diff --git a/lib/archethic/bootstrap.ex b/lib/archethic/bootstrap.ex
index 5624c11c16..9387392dd3 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 70bb919ec8..18d22645dd 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/mining/pending_transaction_validation.ex b/lib/archethic/mining/pending_transaction_validation.ex
index b25863531e..a9076a12b9 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 c5335d8015..a3d39e7ec7 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 59cf46f955..43cc7b68c0 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 18e0d09baf..4b61706b71 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
diff --git a/lib/archethic/p2p/mem_table_loader.ex b/lib/archethic/p2p/mem_table_loader.ex
index f553e31141..8b1e3fc20c 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 3b6cfcf296..229de3189f 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,8 +86,31 @@ 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},
+ port,
+ http_port,
+ transport,
+ reward_address,
+ origin_public_key,
+ key_certificate,
+ mining_public_key
+ ) do
+ <>
+ end
+
+ @doc false
def encode_transaction_content(
{ip1, ip2, ip3, ip4},
port,
@@ -142,6 +128,7 @@ defmodule Archethic.P2P.Node do
@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 +150,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 +174,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 +242,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 +253,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,
@@ -353,7 +283,8 @@ defmodule Archethic.P2P.Node do
DateTime.to_unix(enrollment_date)::32, available_bin::1, synced_bin::1, authorized_bin::1,
authorization_date::32, first_public_key::binary, last_public_key::binary,
reward_address::binary, last_address::binary, origin_public_key::binary,
- DateTime.to_unix(last_update_date)::32, DateTime.to_unix(availability_update)::32>>
+ DateTime.to_unix(last_update_date)::32, DateTime.to_unix(availability_update)::32,
+ mining_public_key::binary>>
end
defp serialize_transport(MockTransport), do: 0
@@ -362,55 +293,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 +317,8 @@ defmodule Archethic.P2P.Node do
{origin_public_key, <>} =
Utils.deserialize_public_key(rest)
+ {mining_public_key, rest} = Utils.deserialize_public_key(rest)
+
{
%__MODULE__{
ip: {o1, o2, o3, o4},
@@ -455,7 +339,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 d5dad96c89..b4f7aedcce 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 89240a9eb4..aef45d7728 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 856c48b775..6f926c0904 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 06308b26fa..a67a5b6fb7 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/test/archethic/bootstrap/sync_test.exs b/test/archethic/bootstrap/sync_test.exs
index ec22a85ebb..94dea38589 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 dfa9e5ebad..f0053f2c10 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/mining/distributed_workflow_test.exs b/test/archethic/mining/distributed_workflow_test.exs
index 8ab1628d16..62b81ffa79 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 a4ce6c619a..861edcebe5 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 472f5ce679..49251a4770 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 f2ff6016c5..83550c7ab2 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 12ce817584..c6c98d6cbe 100644
--- a/test/archethic/p2p/node_test.exs
+++ b/test/archethic/p2p/node_test.exs
@@ -2,4 +2,78 @@ 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 node transaction content" do
+ test "should work without mining public key" do
+ reward_address = ArchethicCase.random_address()
+ origin_public_key = ArchethicCase.random_public_key()
+ certificate = ""
+
+ assert {:ok, {127, 0, 0, 1}, 3000, 4000, :tcp, ^reward_address, ^origin_public_key,
+ ^certificate,
+ nil} =
+ Node.encode_transaction_content(
+ {127, 0, 0, 1},
+ 3000,
+ 4000,
+ :tcp,
+ reward_address,
+ origin_public_key,
+ certificate
+ )
+ |> Node.decode_transaction_content()
+ end
+
+ test "should work with mining public key" 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 c2ae2a2cfa..21d820c0fe 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
From 88a4906e8573440468d7c4094229e73110dab4a2 Mon Sep 17 00:00:00 2001
From: Samuel Manzanera
Date: Wed, 17 Jul 2024 09:57:02 +0200
Subject: [PATCH 03/11] Remove keystore anonymous functions & update ets tables
---
.../crypto/keystore/node/software_impl.ex | 133 +++++++----------
.../keystore/shared_secrets/software_impl.ex | 135 +++++++-----------
lib/archethic/p2p/mem_table.ex | 16 +++
.../shared_secrets/software_impl_test.exs | 32 ++---
4 files changed, 131 insertions(+), 185 deletions(-)
diff --git a/lib/archethic/crypto/keystore/node/software_impl.ex b/lib/archethic/crypto/keystore/node/software_impl.ex
index fb99b33462..3948d5b54e 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,53 +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
- sign_fun = get_mining_sign_fun()
- sign_fun.(data)
+ {_, 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
- public_key_fun = get_mining_public_key_fun()
- public_key_fun.()
+ {pub, _} = Crypto.generate_deterministic_keypair(get_node_seed(), :bls)
+ pub
end
defp get_last_key_index do
@@ -126,29 +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
- end
-
- defp get_mining_sign_fun do
- [{_, fun}] = :ets.lookup(@keystore_table, :sign_mining_fun)
- fun
- end
-
- defp get_mining_public_key_fun do
- [{_, fun}] = :ets.lookup(@keystore_table, :public_key_mining_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
@@ -165,39 +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
-
- sign_mining_fun = fn data ->
- {_, pv} = Crypto.generate_deterministic_keypair(node_seed, :bls)
- Crypto.sign(data, pv)
- end
-
- public_key_mining_fn = fn ->
- {pub, _} = Crypto.generate_deterministic_keypair(node_seed, :bls)
- pub
- 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})
- :ets.insert(@keystore_table, {:sign_mining_fun, sign_mining_fun})
- :ets.insert(@keystore_table, {:public_key_mining_fun, public_key_mining_fn})
+ 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"))
@@ -262,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}
@@ -277,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_6, 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.6
+ @doc false
+ def migrate_ets_table_1_5_6 do
+ GenServer.cast(__MODULE__, :migrate_1_5_6)
+ 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 d424d84963..4c51a08d7e 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.6
+ @doc false
+ def migrate_ets_table_1_5_6 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/p2p/mem_table.ex b/lib/archethic/p2p/mem_table.ex
index 4b61706b71..5205ec065f 100644
--- a/lib/archethic/p2p/mem_table.ex
+++ b/lib/archethic/p2p/mem_table.ex
@@ -487,4 +487,20 @@ defmodule Archethic.P2P.MemTable do
:ok
end
end
+
+ # FIXME: to remove after 1.5.6
+ @doc false
+ def migrate_ets_table_1_5_6 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/test/archethic/crypto/keystore/shared_secrets/software_impl_test.exs b/test/archethic/crypto/keystore/shared_secrets/software_impl_test.exs
index e2be6644c2..99325e56d0 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
From 224bae6ab831a4701cb434badab5311fc8a477e2 Mon Sep 17 00:00:00 2001
From: Samuel Manzanera
Date: Thu, 18 Jul 2024 15:41:49 +0200
Subject: [PATCH 04/11] add migration task
---
.../prod/1.5.6@add_mining_bls_key.exs | 57 +++++++++++++++++++
1 file changed, 57 insertions(+)
create mode 100644 priv/migration_tasks/prod/1.5.6@add_mining_bls_key.exs
diff --git a/priv/migration_tasks/prod/1.5.6@add_mining_bls_key.exs b/priv/migration_tasks/prod/1.5.6@add_mining_bls_key.exs
new file mode 100644
index 0000000000..e1498f15ad
--- /dev/null
+++ b/priv/migration_tasks/prod/1.5.6@add_mining_bls_key.exs
@@ -0,0 +1,57 @@
+# defmodule Migration_1_5_6 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
From 2535205ff1774c65e29873e8bba6695c002797d7 Mon Sep 17 00:00:00 2001
From: Samuel Manzanera
Date: Fri, 19 Jul 2024 14:48:00 +0200
Subject: [PATCH 05/11] Add appup
---
rel/appups/archethic/1.5.5_to_1.5.6.appup | 137 ++++++++++++++++++++++
1 file changed, 137 insertions(+)
create mode 100644 rel/appups/archethic/1.5.5_to_1.5.6.appup
diff --git a/rel/appups/archethic/1.5.5_to_1.5.6.appup b/rel/appups/archethic/1.5.5_to_1.5.6.appup
new file mode 100644
index 0000000000..e89dfe661c
--- /dev/null
+++ b/rel/appups/archethic/1.5.5_to_1.5.6.appup
@@ -0,0 +1,137 @@
+{"1.5.6",
+ [{"1.5.5",
+ [{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.Crypto.ID',brutal_purge,soft_purge,[]},
+ {load_module,'Elixir.Archethic.Crypto.NodeKeystore',brutal_purge,
+ soft_purge,[]},
+ {load_module,'Elixir.Archethic.P2P.Message.ValidateSmartContractCall',
+ brutal_purge,soft_purge,[]},
+ {load_module,'Elixir.Archethic.P2P.Node',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.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']},
+ {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.SettingsLive',brutal_purge,
+ soft_purge,
+ ['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.5",
+ [{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.Crypto.ID',brutal_purge,soft_purge,[]},
+ {load_module,'Elixir.Archethic.Crypto.NodeKeystore',brutal_purge,
+ soft_purge,[]},
+ {load_module,'Elixir.Archethic.P2P.Message.ValidateSmartContractCall',
+ brutal_purge,soft_purge,[]},
+ {load_module,'Elixir.Archethic.P2P.Node',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.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']},
+ {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.SettingsLive',brutal_purge,
+ soft_purge,
+ ['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']}]}]}.
From ccbc91fe8d0839f7aac73edfda28d0fe96cfe35d Mon Sep 17 00:00:00 2001
From: Samuel Manzanera
Date: Fri, 6 Sep 2024 14:36:24 +0200
Subject: [PATCH 06/11] lint
---
lib/archethic/crypto.ex | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/lib/archethic/crypto.ex b/lib/archethic/crypto.ex
index 61fc81dba3..828eb4eaab 100755
--- a/lib/archethic/crypto.ex
+++ b/lib/archethic/crypto.ex
@@ -393,11 +393,11 @@ defmodule Archethic.Crypto do
end
defp do_generate_deterministic_keypair(:bls, origin, seed) do
- extended_seed = :crypto.hash(:sha512, seed)
+ private_key = :crypto.hash(:sha512, seed)
keypair = {
- BlsEx.get_public_key(extended_seed),
- extended_seed
+ BlsEx.get_public_key(private_key),
+ private_key
}
ID.prepend_keypair(keypair, :bls, origin)
From 6a24b2eb7f683eca8ba86272c433f3455a8f04df Mon Sep 17 00:00:00 2001
From: Samuel Manzanera
Date: Fri, 6 Sep 2024 14:42:34 +0200
Subject: [PATCH 07/11] mining public key as nil
---
lib/archethic/p2p/node.ex | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/lib/archethic/p2p/node.ex b/lib/archethic/p2p/node.ex
index 229de3189f..e76c87d090 100755
--- a/lib/archethic/p2p/node.ex
+++ b/lib/archethic/p2p/node.ex
@@ -278,13 +278,19 @@ defmodule Archethic.P2P.Node do
avg_bin = trunc(average_availability * 100)
+ mining_public_key_bin =
+ case mining_public_key do
+ nil -> ""
+ _ -> mining_public_key
+ end
+
<>
+ mining_public_key_bin::binary>>
end
defp serialize_transport(MockTransport), do: 0
@@ -317,7 +323,12 @@ defmodule Archethic.P2P.Node do
{origin_public_key, <>} =
Utils.deserialize_public_key(rest)
- {mining_public_key, rest} = Utils.deserialize_public_key(rest)
+ {mining_public_key, rest} =
+ try do
+ Utils.deserialize_public_key(rest)
+ rescue
+ _ -> {nil, rest}
+ end
{
%__MODULE__{
From 0c26dec71ff8f9f5c8117898e3c4832c82d6587d Mon Sep 17 00:00:00 2001
From: Samuel Manzanera
Date: Fri, 6 Sep 2024 14:52:17 +0200
Subject: [PATCH 08/11] remove useless encode node transaction without mining
public key
---
lib/archethic/p2p/node.ex | 23 +----------------------
test/archethic/p2p/node_test.exs | 24 ++----------------------
2 files changed, 3 insertions(+), 44 deletions(-)
diff --git a/lib/archethic/p2p/node.ex b/lib/archethic/p2p/node.ex
index e76c87d090..d9d38e8ec3 100755
--- a/lib/archethic/p2p/node.ex
+++ b/lib/archethic/p2p/node.ex
@@ -99,30 +99,9 @@ defmodule Archethic.P2P.Node do
key_certificate,
mining_public_key
) do
- <>
- end
-
- @doc false
- def encode_transaction_content(
- {ip1, ip2, ip3, ip4},
- port,
- http_port,
- transport,
- reward_address,
- origin_public_key,
- key_certificate
- ) do
<>
+ key_certificate::binary, mining_public_key::binary>>
end
@type t() :: %__MODULE__{
diff --git a/test/archethic/p2p/node_test.exs b/test/archethic/p2p/node_test.exs
index c6c98d6cbe..74be43266e 100644
--- a/test/archethic/p2p/node_test.exs
+++ b/test/archethic/p2p/node_test.exs
@@ -33,28 +33,8 @@ defmodule Archethic.P2P.NodeTest do
|> Node.deserialize()
end
- describe "encode/decode node transaction content" do
- test "should work without mining public key" do
- reward_address = ArchethicCase.random_address()
- origin_public_key = ArchethicCase.random_public_key()
- certificate = ""
-
- assert {:ok, {127, 0, 0, 1}, 3000, 4000, :tcp, ^reward_address, ^origin_public_key,
- ^certificate,
- nil} =
- Node.encode_transaction_content(
- {127, 0, 0, 1},
- 3000,
- 4000,
- :tcp,
- reward_address,
- origin_public_key,
- certificate
- )
- |> Node.decode_transaction_content()
- end
-
- test "should work with mining public key" do
+ 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 = ""
From d2eaea686a9df3d95c6dbb53fc11ab6dd90b66dd Mon Sep 17 00:00:00 2001
From: Samuel Manzanera
Date: Fri, 6 Sep 2024 17:33:36 +0200
Subject: [PATCH 09/11] Update lib/archethic/p2p/node.ex
Co-authored-by: Neylix
---
lib/archethic/p2p/node.ex | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/lib/archethic/p2p/node.ex b/lib/archethic/p2p/node.ex
index d9d38e8ec3..f26bd7b36d 100755
--- a/lib/archethic/p2p/node.ex
+++ b/lib/archethic/p2p/node.ex
@@ -258,10 +258,9 @@ defmodule Archethic.P2P.Node do
avg_bin = trunc(average_availability * 100)
mining_public_key_bin =
- case mining_public_key do
- nil -> ""
- _ -> mining_public_key
- end
+ if is_nil(mining_public_key),
+ do: <<0::8>>,
+ else: <<1::8, mining_public_key::binary>>
<
Date: Fri, 6 Sep 2024 17:33:51 +0200
Subject: [PATCH 10/11] Update lib/archethic/p2p/node.ex
Co-authored-by: Neylix
---
lib/archethic/p2p/node.ex | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/lib/archethic/p2p/node.ex b/lib/archethic/p2p/node.ex
index f26bd7b36d..45b9175e18 100755
--- a/lib/archethic/p2p/node.ex
+++ b/lib/archethic/p2p/node.ex
@@ -302,10 +302,9 @@ defmodule Archethic.P2P.Node do
Utils.deserialize_public_key(rest)
{mining_public_key, rest} =
- try do
- Utils.deserialize_public_key(rest)
- rescue
- _ -> {nil, rest}
+ case rest do
+ <<1::8, rest::bitstring>> -> Utils.deserialize_public_key(rest)
+ <<0::8, rest::bitstring>> -> {nil, rest}
end
{
From 561a32bba5d332a03ccd31860f08783af1926e83 Mon Sep 17 00:00:00 2001
From: Neylix
Date: Mon, 9 Sep 2024 11:13:39 +0200
Subject: [PATCH 11/11] Rename 1.5.6 to 1.5.9
---
.../crypto/keystore/node/software_impl.ex | 8 +--
.../keystore/shared_secrets/software_impl.ex | 4 +-
lib/archethic/p2p/mem_table.ex | 4 +-
.../prod/1.5.6@add_mining_bls_key.exs | 57 -------------------
.../prod/1.5.9@add_mining_bls_key.exs | 57 +++++++++++++++++++
....5_to_1.5.6.appup => 1.5.8_to_1.5.9.appup} | 54 +++++++++++++++---
6 files changed, 112 insertions(+), 72 deletions(-)
delete mode 100644 priv/migration_tasks/prod/1.5.6@add_mining_bls_key.exs
create mode 100644 priv/migration_tasks/prod/1.5.9@add_mining_bls_key.exs
rename rel/appups/archethic/{1.5.5_to_1.5.6.appup => 1.5.8_to_1.5.9.appup} (75%)
diff --git a/lib/archethic/crypto/keystore/node/software_impl.ex b/lib/archethic/crypto/keystore/node/software_impl.ex
index 3948d5b54e..34cda31435 100644
--- a/lib/archethic/crypto/keystore/node/software_impl.ex
+++ b/lib/archethic/crypto/keystore/node/software_impl.ex
@@ -233,7 +233,7 @@ defmodule Archethic.Crypto.NodeKeystore.SoftwareImpl do
@impl GenServer
# FIXME: use genserver message because ets table is protected
- def handle_cast(:migrate_1_5_6, state) do
+ 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)
@@ -243,9 +243,9 @@ defmodule Archethic.Crypto.NodeKeystore.SoftwareImpl do
{:noreply, state}
end
- # FIXME: to remove after 1.5.6
+ # FIXME: to remove after 1.5.9
@doc false
- def migrate_ets_table_1_5_6 do
- GenServer.cast(__MODULE__, :migrate_1_5_6)
+ 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 4c51a08d7e..cb1fc5cd93 100644
--- a/lib/archethic/crypto/keystore/shared_secrets/software_impl.ex
+++ b/lib/archethic/crypto/keystore/shared_secrets/software_impl.ex
@@ -270,9 +270,9 @@ defmodule Archethic.Crypto.SharedSecretsKeystore.SoftwareImpl do
@impl GenServer
def code_change(_old_vsn, state, _extra), do: {:ok, state}
- # FIXME: to remove after 1.5.6
+ # FIXME: to remove after 1.5.9
@doc false
- def migrate_ets_table_1_5_6 do
+ def migrate_ets_table_1_5_9 do
:node_shared_secrets
|> TransactionChain.list_addresses_by_type()
|> Stream.take(-2)
diff --git a/lib/archethic/p2p/mem_table.ex b/lib/archethic/p2p/mem_table.ex
index 5205ec065f..ffdcf47fe1 100644
--- a/lib/archethic/p2p/mem_table.ex
+++ b/lib/archethic/p2p/mem_table.ex
@@ -488,9 +488,9 @@ defmodule Archethic.P2P.MemTable do
end
end
- # FIXME: to remove after 1.5.6
+ # FIXME: to remove after 1.5.9
@doc false
- def migrate_ets_table_1_5_6 do
+ 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",
diff --git a/priv/migration_tasks/prod/1.5.6@add_mining_bls_key.exs b/priv/migration_tasks/prod/1.5.6@add_mining_bls_key.exs
deleted file mode 100644
index e1498f15ad..0000000000
--- a/priv/migration_tasks/prod/1.5.6@add_mining_bls_key.exs
+++ /dev/null
@@ -1,57 +0,0 @@
-# defmodule Migration_1_5_6 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/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 0000000000..dbcf025434
--- /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.5_to_1.5.6.appup b/rel/appups/archethic/1.5.8_to_1.5.9.appup
similarity index 75%
rename from rel/appups/archethic/1.5.5_to_1.5.6.appup
rename to rel/appups/archethic/1.5.8_to_1.5.9.appup
index e89dfe661c..64280d98fa 100644
--- a/rel/appups/archethic/1.5.5_to_1.5.6.appup
+++ b/rel/appups/archethic/1.5.8_to_1.5.9.appup
@@ -1,15 +1,21 @@
-{"1.5.6",
- [{"1.5.5",
+{"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.ValidateSmartContractCall',
- 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,
@@ -17,6 +23,10 @@
{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,
@@ -29,6 +39,10 @@
{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']},
@@ -53,9 +67,15 @@
{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,
@@ -74,16 +94,22 @@
{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.5",
+ [{"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.ValidateSmartContractCall',
- 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,
@@ -91,6 +117,10 @@
{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,
@@ -103,6 +133,10 @@
{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']},
@@ -127,9 +161,15 @@
{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,