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

Initialize miner reward pool #393

Merged
20 commits merged into from
Jun 23, 2022
Merged
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
3 changes: 2 additions & 1 deletion config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ config :archethic, Archethic.Bootstrap.NetworkInit,
genesis_origin_public_keys: [
"010104AB41291F847A601055AEDD1AF24FF76FA970D6441E2DCA3818A8319B004C96B27B8FEB1DA31A044BA0A4800B4353359735719EBB3A05F98393A9CC599C3FAFD6"
|> Base.decode16!(case: :mixed)
]
],
genesis_network_pool_amount: 3_400_000_000_000_000

config :archethic, Archethic.P2P.BootstrappingSeeds,
backup_file: "p2p/seeds",
Expand Down
2 changes: 1 addition & 1 deletion config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ config :logger, level: :error
config :archethic, :mut_dir, "data_test"

config :archethic, Archethic.Account.MemTablesLoader, enabled: false
config :archethic, Archethic.Account.MemTables.NFTLedger, enabled: false
config :archethic, Archethic.Account.MemTables.TokenLedger, enabled: false
config :archethic, Archethic.Account.MemTables.UCOLedger, enabled: false

config :archethic, Archethic.BeaconChain.Subset, enabled: false
Expand Down
4 changes: 2 additions & 2 deletions lib/archethic/account/supervisor.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ defmodule Archethic.Account.Supervisor do

use Supervisor

alias Archethic.Account.MemTables.NFTLedger
alias Archethic.Account.MemTables.TokenLedger
alias Archethic.Account.MemTables.UCOLedger
alias Archethic.Account.MemTablesLoader

Expand All @@ -15,7 +15,7 @@ defmodule Archethic.Account.Supervisor do

def init(_args) do
children = [
NFTLedger,
TokenLedger,
UCOLedger,
MemTablesLoader
]
Expand Down
27 changes: 18 additions & 9 deletions lib/archethic/bootstrap/network_init.ex
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ defmodule Archethic.Bootstrap.NetworkInit do

alias Archethic.SharedSecrets

alias Archethic.Reward

alias Archethic.TransactionChain.Transaction
alias Archethic.TransactionChain.Transaction.CrossValidationStamp
alias Archethic.TransactionChain.Transaction.ValidationStamp
Expand All @@ -43,6 +45,11 @@ defmodule Archethic.Bootstrap.NetworkInit do
[__MODULE__, :genesis_origin_public_keys]
)

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

defp get_genesis_pools do
Application.get_env(:archethic, __MODULE__) |> Keyword.get(:genesis_pools, [])
end
Expand Down Expand Up @@ -115,12 +122,11 @@ defmodule Archethic.Bootstrap.NetworkInit do
"""
@spec init_genesis_wallets() :: :ok
def init_genesis_wallets do
network_pool_address = SharedSecrets.get_network_pool_address()
Logger.info("Create UCO distribution genesis transaction")

tx =
network_pool_address
|> genesis_transfers()
get_genesis_pools()
|> Enum.map(&%Transfer{to: &1.address, amount: &1.amount})
|> create_genesis_transaction()

genesis_transfers_amount =
Expand All @@ -139,6 +145,15 @@ defmodule Archethic.Bootstrap.NetworkInit do
|> self_replication()
end

@spec init_network_reward_pool() :: :ok
def init_network_reward_pool() do
Logger.info("Create mining reward pool")

Reward.new_rewards_mint(@genesis_network_pool_amount)
|> self_validation()
|> self_replication()
end

defp create_genesis_transaction(genesis_transfers) do
Transaction.new(
:transfer,
Expand All @@ -154,12 +169,6 @@ defmodule Archethic.Bootstrap.NetworkInit do
)
end

defp genesis_transfers(network_pool_address) do
get_genesis_pools()
|> Enum.map(&%Transfer{to: &1.address, amount: &1.amount})
|> Enum.concat([%Transfer{to: network_pool_address, amount: 146_000_000_000_000_000}])
end

@spec self_validation(Transaction.t(), list(UnspentOutput.t())) :: Transaction.t()
def self_validation(tx = %Transaction{}, unspent_outputs \\ []) do
operations =
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 @@ -122,6 +122,7 @@ defmodule Archethic.Bootstrap.Sync do
NetworkInit.init_software_origin_chain()
NetworkInit.init_node_shared_secrets_chain()
NetworkInit.init_genesis_wallets()
NetworkInit.init_network_reward_pool()
end

@doc """
Expand Down
5 changes: 3 additions & 2 deletions lib/archethic/crypto.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1047,13 +1047,14 @@ defmodule Archethic.Crypto do
end
end

def load_transaction(%Transaction{type: :node_rewards, address: address}) do
def load_transaction(%Transaction{type: type, address: address})
when type in [:node_rewards, :mint_rewards] do
nb_transactions = TransactionChain.size(address)
SharedSecretsKeystore.set_network_pool_key_index(nb_transactions)

Logger.info("Network pool chain positioned at#{nb_transactions}",
transaction_address: Base.encode16(address),
transaction_type: :node_rewards
transaction_type: type
)
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ defmodule Archethic.Crypto.SharedSecretsKeystore.SoftwareImpl do

Logger.info("Node shared secrets keys positioned at #{nb_node_shared_secrets_keys}")

nb_network_pool_keys = TransactionChain.count_transactions_by_type(:node_rewards)
nb_network_pool_keys =
TransactionChain.count_transactions_by_type(:node_rewards) +
TransactionChain.count_transactions_by_type(:mint_rewards)

Logger.info("Network pool keys positioned at #{nb_network_pool_keys}")

:ets.insert(@keystore_table, {:shared_secrets_index, nb_node_shared_secrets_keys})
Expand Down
13 changes: 7 additions & 6 deletions lib/archethic/mining/pending_transaction_validation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ defmodule Archethic.Mining.PendingTransactionValidation do
alias Archethic.TransactionChain.TransactionData
alias Archethic.TransactionChain.TransactionData.Ledger
alias Archethic.TransactionChain.TransactionData.Ownership
alias Archethic.TransactionChain.TransactionData.UCOLedger
alias Archethic.TransactionChain.TransactionData.TokenLedger

alias Archethic.Utils

Expand Down Expand Up @@ -104,12 +104,12 @@ defmodule Archethic.Mining.PendingTransactionValidation do
type: :node_rewards,
data: %TransactionData{
ledger: %Ledger{
uco: %UCOLedger{transfers: uco_transfers}
token: %TokenLedger{transfers: token_transfers}
}
}
}) do
case Reward.get_transfers_for_in_need_validation_nodes(Reward.last_scheduling_date()) do
^uco_transfers ->
case Reward.get_transfers(Reward.last_scheduling_date()) do
^token_transfers ->
:ok

_ ->
Expand Down Expand Up @@ -304,9 +304,10 @@ defmodule Archethic.Mining.PendingTransactionValidation do
end

defp do_accept_transaction(%Transaction{
type: :token,
type: type,
data: %TransactionData{content: content}
}) do
})
when type in [:token, :mint_rewards] do
schema =
:archethic
|> Application.app_dir("priv/json-schemas/token-core.json")
Expand Down
5 changes: 2 additions & 3 deletions lib/archethic/p2p/message/replicate_transaction_chain.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ defmodule Archethic.P2P.Message.ReplicateTransactionChain do
Represents a message to initiate the replication of the transaction chain related to the given transaction
"""
@enforce_keys [:transaction]
defstruct [:transaction, ack_storage?: false]
defstruct [:transaction]

alias Archethic.TransactionChain.Transaction

@type t :: %__MODULE__{
transaction: Transaction.t(),
ack_storage?: boolean()
transaction: Transaction.t()
}
end
3 changes: 0 additions & 3 deletions lib/archethic/replication.ex
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ defmodule Archethic.Replication do

alias Archethic.OracleChain

alias Archethic.Reward

alias Archethic.SharedSecrets

alias __MODULE__.TransactionContext
Expand Down Expand Up @@ -553,7 +551,6 @@ defmodule Archethic.Replication do
Contracts.load_transaction(tx)
BeaconChain.load_transaction(tx)
OracleChain.load_transaction(tx)
Reward.load_transaction(tx)
:ok
end
end
98 changes: 23 additions & 75 deletions lib/archethic/reward.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,12 @@ defmodule Archethic.Reward do
Module which handles the rewards and transfer scheduling
"""

alias Archethic.Election

alias Archethic.OracleChain

alias Archethic.P2P
alias Archethic.P2P.Message.GetTransactionChain
alias Archethic.P2P.Message.GetUnspentOutputs
alias Archethic.P2P.Message.TransactionList
alias Archethic.P2P.Message.UnspentOutputList
alias Archethic.P2P.Node

alias __MODULE__.NetworkPoolScheduler

alias Archethic.TransactionChain
alias Archethic.TransactionChain.Transaction
alias Archethic.TransactionChain.TransactionData
alias Archethic.TransactionChain.TransactionData.UCOLedger.Transfer

@unit_uco 100_000_000
Expand All @@ -36,76 +27,33 @@ defmodule Archethic.Reward do
end

@doc """
Return the list of transfers to rewards the validation nodes which receive less than the minimum validation node reward

This will get and check all the unspent outputs after the last reward date and determine which were mining reward
and compare it with the minimum of rewards for a validation node
Create a transaction for minting new rewards
"""
@spec get_transfers_for_in_need_validation_nodes(last_reward_date :: DateTime.t()) ::
reward_transfers :: list(Transfer.t())
def get_transfers_for_in_need_validation_nodes(last_date = %DateTime{}) do
min_validation_nodes_reward = min_validation_nodes_reward()

Task.async_stream(
P2P.authorized_and_available_nodes(),
fn node = %Node{reward_address: reward_address} ->
mining_rewards =
reward_address
|> get_transactions_after(last_date)
|> Task.async_stream(&get_reward_unspent_outputs/1, timeout: 500, on_exit: :kill_task)
|> Stream.filter(&match?({:ok, _}, &1))
|> Enum.flat_map(& &1)

{node, mining_rewards}
end
)
|> Enum.filter(fn {_, balance} -> balance < min_validation_nodes_reward end)
|> Enum.map(fn {%Node{reward_address: address}, amount} ->
%Transfer{to: address, amount: min_validation_nodes_reward - amount}
end)
end

defp get_transactions_after(address, date) do
{:ok, last_address} = TransactionChain.resolve_last_address(address, DateTime.utc_now())

last_address
|> Election.chain_storage_nodes(P2P.available_nodes())
|> P2P.nearest_nodes()
|> get_transaction_chain_after(address, date)
end

defp get_transaction_chain_after([node | rest], address, date) do
case P2P.send_message(node, %GetTransactionChain{address: address}) do
{:ok, %TransactionList{transactions: transactions, more?: false}} ->
transactions

{:error, _} ->
get_transaction_chain_after(rest, address, date)
end
@spec new_rewards_mint(amount :: non_neg_integer()) :: Transaction.t()
def new_rewards_mint(amount) do
data = %TransactionData{
content: """
{
"supply":#{amount},
"type":"fungible",
"name":"Mining UCO rewards",
"symbol":"MUCO"
}
"""
}

Transaction.new(:mint_rewards, data)
end

defp get_reward_unspent_outputs(%Transaction{address: address}) do
address
|> Election.chain_storage_nodes(P2P.available_nodes())
|> P2P.nearest_nodes()
|> get_unspent_outputs(address)
|> Enum.filter(&(&1.type == :reward))
end

defp get_unspent_outputs([node | rest], address) do
case P2P.send_message(node, %GetUnspentOutputs{address: address}) do
{:ok, %UnspentOutputList{unspent_outputs: unspent_outputs}} ->
unspent_outputs

{:error, _} ->
get_unspent_outputs(rest, address)
end
@doc """
Return the list of transfers to rewards the validation nodes for a specific date
"""
@spec get_transfers(last_reward_date :: DateTime.t()) :: reward_transfers :: list(Transfer.t())
def get_transfers(_last_date = %DateTime{}) do
# TODO
[]
end

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

def load_transaction(_), do: :ok

@doc """
Returns the last date of the rewards scheduling from the network pool
"""
Expand Down
48 changes: 24 additions & 24 deletions lib/archethic/reward/network_pool_scheduler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ defmodule Archethic.Reward.NetworkPoolScheduler do

alias Archethic.Reward

alias Archethic.TransactionChain.Transaction
alias Archethic.TransactionChain.TransactionData
alias Archethic.TransactionChain.TransactionData.Ledger
alias Archethic.TransactionChain.TransactionData.UCOLedger
# alias Archethic.TransactionChain.Transaction
# alias Archethic.TransactionChain.TransactionData
# alias Archethic.TransactionChain.TransactionData.Ledger
# alias Archethic.TransactionChain.TransactionData.UCOLedger

alias Archethic.Utils

Expand Down Expand Up @@ -84,7 +84,7 @@ defmodule Archethic.Reward.NetworkPoolScheduler do
if sender?() do
interval
|> get_last_date
|> Reward.get_transfers_for_in_need_validation_nodes()
|> Reward.get_transfers()
|> send_rewards()
end

Expand Down Expand Up @@ -140,25 +140,25 @@ defmodule Archethic.Reward.NetworkPoolScheduler do

defp send_rewards([]), do: :ok

defp send_rewards(transfers) do
Logger.debug("Sending node reward transaction")

Transaction.new(:node_rewards, %TransactionData{
code: """
condition inherit: [
# We need to ensure the transaction type keep consistent
# So we can apply specific rules during the transaction verification
type: node_rewards
]
""",
ledger: %Ledger{
uco: %UCOLedger{
transfers: transfers
}
}
})
|> Archethic.send_new_transaction()
end
# defp send_rewards(transfers) do
# Logger.debug("Sending node reward transaction")

# Transaction.new(:node_rewards, %TransactionData{
# code: """
# condition inherit: [
# # We need to ensure the transaction type keep consistent
# # So we can apply specific rules during the transaction verification
# type: node_rewards
# ]
# """,
# ledger: %Ledger{
# uco: %UCOLedger{
# transfers: transfers
# }
# }
# })
# |> Archethic.send_new_transaction()
# end

defp schedule(interval) do
Process.send_after(self(), :send_rewards, Utils.time_offset(interval) * 1000)
Expand Down
Loading