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

Add geopatch to node transactions #1605

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
363 changes: 136 additions & 227 deletions lib/archethic/bootstrap.ex

Large diffs are not rendered by default.

88 changes: 34 additions & 54 deletions lib/archethic/bootstrap/sync.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ defmodule Archethic.Bootstrap.Sync do
alias Archethic.P2P.Message.GetStorageNonce
alias Archethic.P2P.Message.NotifyEndOfNodeSync
alias Archethic.P2P.Node
alias Archethic.P2P.NodeConfig

alias Archethic.SharedSecrets

Expand All @@ -36,68 +37,53 @@ defmodule Archethic.Bootstrap.Sync do
@doc """
Determines if network should be initialized
"""
@spec should_initialize_network?(list(Node.t())) :: boolean()
def should_initialize_network?([]) do
@spec should_initialize_network?(boostrapping_nodes :: list(Node.t()), node_key :: Crypto.key()) ::
boolean()
def should_initialize_network?([], _) do
TransactionChain.count_transactions_by_type(:node_shared_secrets) == 0
end

def should_initialize_network?([%Node{first_public_key: node_key} | _]) do
node_key == Crypto.first_node_public_key() and
TransactionChain.count_transactions_by_type(:node_shared_secrets) == 0
def should_initialize_network?([%Node{first_public_key: bootstrapping_node_key} | _], node_key)
when bootstrapping_node_key == node_key do
TransactionChain.count_transactions_by_type(:node_shared_secrets) == 0
end

def should_initialize_network?(_), do: false
def should_initialize_network?(_, _), do: false

@doc """
Determines if the node requires an update
"""
@spec require_update?(
:inet.ip_address(),
:inet.port_number(),
:inet.port_number(),
P2P.supported_transport(),
DateTime.t() | nil
) :: boolean()
def require_update?(_ip, _port, _http_port, _transport, nil), do: false

def require_update?(ip, port, http_port, transport, last_sync_date) do
first_node_public_key = Crypto.first_node_public_key()

case P2P.authorized_and_available_nodes() do
[%Node{first_public_key: ^first_node_public_key}] ->
false

_ ->
diff_sync = DateTime.diff(DateTime.utc_now(), last_sync_date, :second)

case P2P.get_node_info(first_node_public_key) do
{:ok,
%Node{
ip: prev_ip,
port: prev_port,
http_port: prev_http_port,
transport: prev_transport
}}
when ip != prev_ip or port != prev_port or http_port != prev_http_port or
diff_sync > @out_of_sync_date_threshold or
prev_transport != transport ->
true

_ ->
false
end
@spec require_update?(node_conig :: NodeConfig.t(), last_sync_date :: DateTime.t()) :: boolean()
def require_update?(_, nil), do: false

def require_update?(
node_config = %NodeConfig{first_public_key: first_public_key},
last_sync_date
) do
current_config = P2P.get_node_info() |> NodeConfig.from_node()
diff_sync = DateTime.diff(DateTime.utc_now(), last_sync_date, :second)

cond do
first_node?(first_public_key) -> false
diff_sync > @out_of_sync_date_threshold -> true
true -> NodeConfig.different?(node_config, current_config)
end
end

defp first_node?(first_node_public_key) do
nodes = P2P.authorized_and_available_nodes()
match?([%Node{first_public_key: ^first_node_public_key}], nodes)
end

@doc """
Initialize the network by predefining the storage nonce, the first node transaction and the first node shared secrets and the genesis fund allocations
"""
@spec initialize_network(Transaction.t()) :: :ok
def initialize_network(node_tx = %Transaction{}) do
def initialize_network(node_tx = %Transaction{previous_public_key: first_public_key}) do
NetworkInit.create_storage_nonce()

secret_key = :crypto.strong_rand_bytes(32)
encrypted_secret_key = Crypto.ec_encrypt(secret_key, Crypto.last_node_public_key())
encrypted_secret_key = Crypto.ec_encrypt(secret_key, first_public_key)

encrypted_daily_nonce_seed = Crypto.aes_encrypt(@genesis_daily_nonce_seed, secret_key)
encrypted_transaction_seed = Crypto.aes_encrypt(:crypto.strong_rand_bytes(32), secret_key)
Expand All @@ -108,19 +94,13 @@ defmodule Archethic.Bootstrap.Sync do
encrypted_reward_seed::binary>>

:ok = Crypto.unwrap_secrets(secrets, encrypted_secret_key, ~U[1970-01-01 00:00:00Z])
:ok = node_tx |> NetworkInit.self_validation() |> NetworkInit.self_replication()

:ok =
node_tx
|> NetworkInit.self_validation()
|> NetworkInit.self_replication()

P2P.set_node_globally_available(Crypto.first_node_public_key(), DateTime.utc_now())
P2P.set_node_globally_synced(Crypto.first_node_public_key())
now = DateTime.utc_now()

P2P.authorize_node(
Crypto.last_node_public_key(),
SharedSecrets.next_application_date(DateTime.utc_now())
)
P2P.set_node_globally_available(first_public_key, now)
P2P.set_node_globally_synced(first_public_key)
P2P.authorize_node(first_public_key, SharedSecrets.next_application_date(now))

NetworkInit.init_software_origin_chain()
NetworkInit.init_node_shared_secrets_chain()
Expand Down
31 changes: 4 additions & 27 deletions lib/archethic/bootstrap/transaction_handler.ex
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
defmodule Archethic.Bootstrap.TransactionHandler do
@moduledoc false

alias Archethic.Crypto

alias Archethic.P2P
alias Archethic.P2P.Message.Ok
alias Archethic.P2P.Message.NewTransaction
alias Archethic.P2P.Node
alias Archethic.P2P.NodeConfig

alias Archethic.TransactionChain.Transaction
alias Archethic.TransactionChain.TransactionData
Expand Down Expand Up @@ -68,20 +67,8 @@ defmodule Archethic.Bootstrap.TransactionHandler do
@doc """
Create a new node transaction
"""
@spec create_node_transaction(
ip_address :: :inet.ip_address(),
p2p_port :: :inet.port_number(),
http_port :: :inet.port_number(),
transport :: P2P.supported_transport(),
reward_address :: Crypto.versioned_hash()
) ::
Transaction.t()
def create_node_transaction(ip = {_, _, _, _}, port, http_port, transport, reward_address)
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()

@spec create_node_transaction(node_config :: NodeConfig.t()) :: Transaction.t()
def create_node_transaction(node_config) do
Transaction.new(:node, %TransactionData{
code: """
condition inherit: [
Expand All @@ -93,17 +80,7 @@ defmodule Archethic.Bootstrap.TransactionHandler do
token_transfers: true
]
""",
content:
Node.encode_transaction_content(
ip,
port,
http_port,
transport,
reward_address,
origin_public_key,
origin_public_key_certificate,
mining_public_key
)
content: Node.encode_transaction_content(node_config)
})
end
end
139 changes: 76 additions & 63 deletions lib/archethic/mining/pending_transaction_validation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ defmodule Archethic.Mining.PendingTransactionValidation do
alias Archethic.OracleChain

alias Archethic.P2P
alias Archethic.P2P.GeoPatch
alias Archethic.P2P.Message.FirstPublicKey
alias Archethic.P2P.Message.GetFirstPublicKey
alias Archethic.P2P.Node
alias Archethic.P2P.NodeConfig

alias Archethic.Reward

Expand Down Expand Up @@ -340,61 +342,18 @@ defmodule Archethic.Mining.PendingTransactionValidation do
type: :node,
data: %TransactionData{
content: content,
ledger: %Ledger{
token: %TokenLedger{
transfers: token_transfers
}
}
ledger: %Ledger{token: %TokenLedger{transfers: token_transfers}}
},
previous_public_key: previous_public_key
},
_
) do
with {:ok, ip, port, _http_port, _, _, origin_public_key, key_certificate, mining_public_key} <-
Node.decode_transaction_content(content),
{:auth_origin, true} <-
{:auth_origin,
Crypto.authorized_key_origin?(origin_public_key, get_allowed_node_key_origins())},
root_ca_public_key <- Crypto.get_root_ca_public_key(origin_public_key),
{:auth_cert, true} <-
{:auth_cert,
Crypto.verify_key_certificate?(
origin_public_key,
key_certificate,
root_ca_public_key,
true
)},
{:conn, :ok} <-
{:conn, valid_connection(ip, port, previous_public_key)},
{:transfers, true} <-
{: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 ->
{:error, "Invalid node transaction's content"}

{:auth_cert, false} ->
{:error, "Invalid node transaction with invalid certificate"}

{:auth_origin, false} ->
{:error, "Invalid node transaction with invalid key origin"}

{:conn, {:error, :invalid_ip}} ->
{:error, "Invalid node's IP address"}

{:conn, {:error, :existing_node}} ->
{:error,
"Invalid node connection (IP/Port) for for the given public key - already existing"}

{:transfers, false} ->
{:error, "Invalid transfers, only mining rewards tokens are allowed"}

{:mining_public_key, false} ->
{:error, "Invalid mining public key"}
with {:ok, node_config} <- validate_node_tx_content(content),
:ok <- validate_node_tx_origin(node_config),
:ok <- validate_node_tx_connection(node_config, previous_public_key),
:ok <- validate_node_tx_transfers(token_transfers),
:ok <- vallidate_node_tx_mining_key(node_config) do
validate_node_tx_geopatch(node_config)
end
end

Expand Down Expand Up @@ -718,6 +677,73 @@ defmodule Archethic.Mining.PendingTransactionValidation do

def validate_type_rules(_, _), do: :ok

defp validate_node_tx_content(content) do
case Node.decode_transaction_content(content) do
{:ok, node_config} -> {:ok, node_config}
:error -> {:error, "Invalid node transaction's content"}
end
end

defp validate_node_tx_origin(%NodeConfig{
origin_public_key: origin_public_key,
origin_certificate: origin_certificate
}) do
root_ca_public_key = Crypto.get_root_ca_public_key(origin_public_key)

cond do
not Crypto.authorized_key_origin?(origin_public_key, get_allowed_node_key_origins()) ->
{:error, "Invalid node transaction with invalid key origin"}

not Crypto.verify_key_certificate?(
origin_public_key,
origin_certificate,
root_ca_public_key,
true
) ->
{:error, "Invalid node transaction with invalid certificate"}

true ->
:ok
end
end

defp validate_node_tx_connection(%NodeConfig{ip: ip, port: port}, previous_public_key) do
cond do
{:error, :invalid_ip} == Networking.validate_ip(ip) ->
{:error, "Invalid node's IP address"}

P2P.duplicating_node?(ip, port, previous_public_key) ->
{:error,
"Invalid node connection (IP/Port) for for the given public key - already existing"}

true ->
:ok
end
end

defp validate_node_tx_transfers(token_transfers) do
if Enum.all?(token_transfers, &Reward.is_reward_token?(&1.token_address)),
do: :ok,
else: {:error, "Invalid transfers, only mining rewards tokens are allowed"}
end

defp vallidate_node_tx_mining_key(%NodeConfig{mining_public_key: mining_public_key}) do
cond do
not Crypto.valid_public_key?(mining_public_key) ->
{:error, "Invalid mining public key"}

Crypto.get_public_key_curve(mining_public_key) != :bls ->
{:error, "Node mining public key should be BLS"}

true ->
:ok
end
end

defp validate_node_tx_geopatch(%NodeConfig{ip: ip, geo_patch: geo_patch}) do
if geo_patch == GeoPatch.from_ip(ip), do: :ok, else: {:error, "Invalid geo patch from IP"}
end

@doc """
Ensure network transactions are in the expected chain
"""
Expand Down Expand Up @@ -1027,17 +1053,4 @@ defmodule Archethic.Mining.PendingTransactionValidation do
end

defp get_first_public_key([], _), do: {:error, :network_issue}

defp valid_connection(ip, port, previous_public_key) do
with :ok <- Networking.validate_ip(ip),
false <- P2P.duplicating_node?(ip, port, previous_public_key) do
:ok
else
true ->
{:error, :existing_node}

{:error, :invalid_ip} ->
{:error, :invalid_ip}
end
end
end
9 changes: 4 additions & 5 deletions lib/archethic/mining/proof_of_work.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ defmodule Archethic.Mining.ProofOfWork do
alias Archethic.Crypto

alias Archethic.P2P.Node
alias Archethic.P2P.NodeConfig

alias Archethic.SharedSecrets

Expand Down Expand Up @@ -137,12 +138,10 @@ defmodule Archethic.Mining.ProofOfWork do

defp do_list_origin_public_keys_candidates(%Transaction{
type: :node,
data: %TransactionData{
content: content
}
data: %TransactionData{content: content}
}) do
{:ok, _ip, _p2p_port, _http_port, _transport, _reward_address, origin_public_key,
_origin_certificate, _mining_public_key} = Node.decode_transaction_content(content)
{:ok, %NodeConfig{origin_public_key: origin_public_key}} =
Node.decode_transaction_content(content)

[origin_public_key]
end
Expand Down
Loading
Loading