Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Leverage sharded origin key #288

Merged
6 commits merged into from
May 3, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions assets/css/explorer.scss
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ pre {
}

.text_wrap {
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-word;
}

.modal-content {
Expand Down
45 changes: 45 additions & 0 deletions lib/archethic/bootstrap/network_init.ex
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,19 @@ defmodule ArchEthic.Bootstrap.NetworkInit do
alias ArchEthic.TransactionChain.TransactionData.Ledger
alias ArchEthic.TransactionChain.TransactionData.UCOLedger
alias ArchEthic.TransactionChain.TransactionData.UCOLedger.Transfer
alias ArchEthic.TransactionChain.TransactionData.Ownership

alias ArchEthic.TransactionChain.TransactionSummary

require Logger

@genesis_seed Application.compile_env(:archethic, [__MODULE__, :genesis_seed])

@genesis_origin_public_keys Application.compile_env!(
:archethic,
[__MODULE__, :genesis_origin_public_keys]
)

defp get_genesis_pools do
Application.get_env(:archethic, __MODULE__) |> Keyword.get(:genesis_pools, [])
end
Expand Down Expand Up @@ -74,6 +80,45 @@ defmodule ArchEthic.Bootstrap.NetworkInit do
|> self_replication()
end

@doc """
Create the first origin shared secret transaction
"""
@spec init_software_origin_shared_secrets_chain() :: :ok
def init_software_origin_shared_secrets_chain do
Logger.info("Create first software origin shared secret transaction")

origin_seed = :crypto.strong_rand_bytes(32)
secret_key = :crypto.strong_rand_bytes(32)
signing_seed = Crypto.get_origin_family_seed(:software)

# Default keypair generation creates software public key
{origin_public_key, origin_private_key} = Crypto.generate_deterministic_keypair(origin_seed)

encrypted_origin_private_key = Crypto.aes_encrypt(origin_private_key, secret_key)

Transaction.new(
:origin_shared_secrets,
%TransactionData{
code: """
This conversation was marked as resolved.
Show resolved Hide resolved
condition inherit: [
# We need to ensure the type stays consistent
# So we can apply specific rules during the transaction validation
type: origin_shared_secrets,
origin_family: software
]
""",
content: <<origin_public_key::binary>>,
ownerships: [
Ownership.new(encrypted_origin_private_key, secret_key, @genesis_origin_public_keys)
]
},
signing_seed,
0
)
|> self_validation()
|> self_replication()
end

@doc """
Initializes the genesis wallets for the UCO distribution
"""
Expand Down
1 change: 1 addition & 0 deletions lib/archethic/bootstrap/sync.ex
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ defmodule ArchEthic.Bootstrap.Sync do
P2P.set_node_globally_available(Crypto.first_node_public_key())
P2P.authorize_node(Crypto.last_node_public_key(), DateTime.utc_now())

NetworkInit.init_software_origin_shared_secrets_chain()
NetworkInit.init_node_shared_secrets_chain()
NetworkInit.init_genesis_wallets()
end
Expand Down
18 changes: 18 additions & 0 deletions lib/archethic/crypto.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1168,6 +1168,24 @@ defmodule ArchEthic.Crypto do
@spec default_curve() :: supported_curve()
def default_curve, do: Application.get_env(:archethic, __MODULE__)[:default_curve]

@doc """
Get the origin seed for a given origin family
"""
@spec get_origin_family_seed(supported_origin()) :: binary()
def get_origin_family_seed(origin_id) do
storage_nonce() <>
case origin_id do
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of putting this in the crypto.ex, you should move it to shared_secrets.ex, to avoid confusion with origin_id for public keys

:software ->
"software"

:on_chain_wallet ->
"software"

_ ->
"hardware"
end
end

@doc """
Determine if the origin of the key is allowed

Expand Down
24 changes: 1 addition & 23 deletions lib/archethic/shared_secrets/mem_tables/origin_key_lookup.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,13 @@ defmodule ArchEthic.SharedSecrets.MemTables.OriginKeyLookup do

alias ArchEthic.Crypto

alias ArchEthic.Bootstrap.NetworkInit

alias ArchEthic.SharedSecrets

require Logger

@origin_key_table :archethic_origin_keys
@origin_key_by_type_table :archethic_origin_key_by_type

@genesis_origin_public_keys Application.compile_env!(
:archethic,
[NetworkInit, :genesis_origin_public_keys]
)
@doc """
Initialize memory tables to index public information from the shared secrets

Expand All @@ -39,10 +33,6 @@ defmodule ArchEthic.SharedSecrets.MemTables.OriginKeyLookup do
:ets.new(@origin_key_by_type_table, [:bag, :named_table, :public, read_concurrency: true])
:ets.new(@origin_key_table, [:set, :named_table, :public, read_concurrency: true])

Enum.each(@genesis_origin_public_keys, fn key ->
add_public_key(:software, key)
end)

{:ok, []}
end

Expand All @@ -60,21 +50,13 @@ defmodule ArchEthic.SharedSecrets.MemTables.OriginKeyLookup do
iex> { :ets.tab2list(:archethic_origin_keys), :ets.tab2list(:archethic_origin_key_by_type) }
{
[
{<<1, 0, 4, 171, 65, 41, 31, 132, 122, 96, 16, 85, 174, 221, 26, 242, 79, 247,
111, 169, 112, 214, 68, 30, 45, 202, 56, 24, 168, 49, 155, 0, 76, 150, 178,
123, 143, 235, 29, 163, 26, 4, 75, 160, 164, 128, 11, 67, 83, 53, 151, 53,
113, 158, 187, 58, 5, 249, 131, 147, 169, 204, 89, 156, 63, 175, 214>>, :software},
{"key1", :software},
{"key3", :hardware},
{"key2", :hardware}
],
[
{:hardware, "key2"},
{:hardware, "key3"},
{:software, <<1, 0, 4, 171, 65, 41, 31, 132, 122, 96, 16, 85, 174, 221, 26, 242, 79, 247,
111, 169, 112, 214, 68, 30, 45, 202, 56, 24, 168, 49, 155, 0, 76, 150, 178,
123, 143, 235, 29, 163, 26, 4, 75, 160, 164, 128, 11, 67, 83, 53, 151, 53,
113, 158, 187, 58, 5, 249, 131, 147, 169, 204, 89, 156, 63, 175, 214>>},
{:software, "key1"}
],
}
Expand Down Expand Up @@ -118,11 +100,7 @@ defmodule ArchEthic.SharedSecrets.MemTables.OriginKeyLookup do
iex> :ok = OriginKeyLookup.add_public_key(:hardware, "key3")
iex> OriginKeyLookup.list_public_keys()
[
<<1, 0, 4, 171, 65, 41, 31, 132, 122, 96, 16, 85, 174, 221, 26, 242, 79, 247,
111, 169, 112, 214, 68, 30, 45, 202, 56, 24, 168, 49, 155, 0, 76, 150, 178,
123, 143, 235, 29, 163, 26, 4, 75, 160, 164, 128, 11, 67, 83, 53, 151, 53,
113, 158, 187, 58, 5, 249, 131, 147, 169, 204, 89, 156, 63, 175, 214>>,
"key1",
"key1",
"key3",
"key2"
]
Expand Down
49 changes: 49 additions & 0 deletions lib/archethic_web/views/explorer_view.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ defmodule ArchEthicWeb.ExplorerView do

alias ArchEthic.Utils

alias ArchEthic.Crypto

alias Phoenix.Naming

def roles_to_string(roles) do
Expand Down Expand Up @@ -196,5 +198,52 @@ defmodule ArchEthicWeb.ExplorerView do
"""
end

def format_transaction_content(:origin_shared_secrets, content) do
get_origin_public_keys(content, %{software: [], hardware: []})
|> Enum.map_join("\n", fn {family, keys} ->
format_origin_shared_secrets_content(family, keys)
end)
end

def format_transaction_content(_, content), do: content

defp format_origin_shared_secrets_content(family, keys) do
case Enum.count(keys) do
1 ->
"#{family} origin public key : #{Enum.at(keys, 0) |> Base.encode16()}"

x when x > 1 ->
"""
#{family} origin public keys :
#{Enum.map_join(keys, "\n", fn key -> "- " <> Base.encode16(key) end)}
"""

_ ->
""
end
end

defp get_origin_public_keys(<<>>, acc), do: acc

defp get_origin_public_keys(<<curve_id::8, origin_id::8, rest::binary>>, acc) do
key_size = Crypto.key_size(curve_id)
<<key::binary-size(key_size), rest::binary>> = rest

family =
case Crypto.key_origin(origin_id) do
:software ->
:software

:tpm ->
:hardware

:on_chain_wallet ->
:software
end

get_origin_public_keys(
rest,
Map.update!(acc, family, &[<<curve_id::8, origin_id::8, key::binary>> | &1])
)
end
end
66 changes: 66 additions & 0 deletions test/archethic/bootstrap/network_init_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,18 @@ defmodule ArchEthic.Bootstrap.NetworkInitTest do

alias ArchEthic.TransactionChain.Transaction.ValidationStamp.LedgerOperations.UnspentOutput
alias ArchEthic.TransactionChain.TransactionData
alias ArchEthic.TransactionChain.TransactionData.Ownership
alias ArchEthic.TransactionChain.TransactionData.Ledger
alias ArchEthic.TransactionChain.TransactionData.UCOLedger
alias ArchEthic.TransactionChain.TransactionData.UCOLedger.Transfer
alias ArchEthic.TransactionChain.TransactionSummary
alias ArchEthic.TransactionFactory

@genesis_origin_public_keys Application.compile_env!(
:archethic,
[NetworkInit, :genesis_origin_public_keys]
)

import Mox

setup do
Expand Down Expand Up @@ -267,4 +273,64 @@ defmodule ArchEthic.Bootstrap.NetworkInitTest do
assert %{uco: 146_000_000_000_000_000} =
Account.get_balance(SharedSecrets.get_network_pool_address())
end

test "init_software_origin_shared_secrets_chain/1 should create first origin shared secret transaction" do
MockClient
|> stub(:send_message, fn
_, %GetTransaction{}, _ ->
{:ok, %NotFound{}}

_, %GetTransactionChain{}, _ ->
{:ok, %TransactionList{transactions: [], more?: false, paging_state: nil}}

_, %GetUnspentOutputs{}, _ ->
{:ok, %UnspentOutputList{unspent_outputs: []}}

_, %GetLastTransactionAddress{address: address}, _ ->
{:ok, %LastTransactionAddress{address: address}}
end)

me = self()

MockDB
|> expect(:write_transaction, fn tx ->
send(me, {:transaction, tx})
:ok
end)

P2P.add_and_connect_node(%Node{
first_public_key: Crypto.last_node_public_key(),
last_public_key: Crypto.last_node_public_key(),
ip: {127, 0, 0, 1},
port: 3000,
available?: true,
enrollment_date: DateTime.utc_now(),
network_patch: "AAA",
authorization_date: DateTime.utc_now(),
authorized?: true,
reward_address: <<0::8, :crypto.strong_rand_bytes(32)::binary>>
})

Crypto.generate_deterministic_keypair("daily_nonce_seed")
|> elem(0)
|> NetworkLookup.set_daily_nonce_public_key(DateTime.utc_now() |> DateTime.add(-10))

assert :ok = NetworkInit.init_software_origin_shared_secrets_chain()

assert 1 == SharedSecrets.list_origin_public_keys() |> Enum.count()

assert_receive {:transaction,
%Transaction{
type: :origin_shared_secrets,
data: %TransactionData{
ownerships: [
%Ownership{
authorized_keys: authorized_keys
}
]
}
}}

assert Map.keys(authorized_keys) == @genesis_origin_public_keys
end
end
3 changes: 3 additions & 0 deletions test/archethic/bootstrap/sync_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ defmodule ArchEthic.Bootstrap.SyncTest do
alias ArchEthic.P2P.Message.UnspentOutputList
alias ArchEthic.P2P.Node

alias ArchEthic.SharedSecrets
alias ArchEthic.SharedSecrets.NodeRenewalScheduler

alias ArchEthic.TransactionChain
Expand Down Expand Up @@ -271,6 +272,8 @@ defmodule ArchEthic.Bootstrap.SyncTest do
assert %Node{authorized?: true} = P2P.get_node_info()
assert 1 == Crypto.number_of_node_shared_secrets_keys()

assert 2 == SharedSecrets.list_origin_public_keys() |> Enum.count()

Application.get_env(:archethic, ArchEthic.Bootstrap.NetworkInit)[:genesis_pools]
|> Enum.each(fn %{address: address, amount: amount} ->
assert %{uco: amount, nft: %{}} == Account.get_balance(address)
Expand Down
3 changes: 3 additions & 0 deletions test/archethic/bootstrap_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ defmodule ArchEthic.BootstrapTest do

alias ArchEthic.SelfRepair.Scheduler, as: SelfRepairScheduler

alias ArchEthic.SharedSecrets
alias ArchEthic.SharedSecrets.NodeRenewalScheduler

alias ArchEthic.TransactionChain
Expand Down Expand Up @@ -133,6 +134,8 @@ defmodule ArchEthic.BootstrapTest do
P2P.list_nodes()

assert 1 == Crypto.number_of_node_shared_secrets_keys()

assert 2 == SharedSecrets.list_origin_public_keys() |> Enum.count()
end
end

Expand Down