From e4a80314b19f9b887512c9447758e999aeb1afc2 Mon Sep 17 00:00:00 2001 From: Samuel Manzanera Date: Tue, 25 Oct 2022 14:06:50 +0200 Subject: [PATCH 1/5] Fix slot/summary timer prev/next function --- lib/archethic/beacon_chain/slot_timer.ex | 86 ++++++++++++++------- lib/archethic/beacon_chain/subset.ex | 25 +++--- lib/archethic/beacon_chain/summary_timer.ex | 30 +++++-- lib/archethic/self_repair/sync.ex | 3 +- 4 files changed, 95 insertions(+), 49 deletions(-) diff --git a/lib/archethic/beacon_chain/slot_timer.ex b/lib/archethic/beacon_chain/slot_timer.ex index d1d5ad9a7..2d50464f2 100644 --- a/lib/archethic/beacon_chain/slot_timer.ex +++ b/lib/archethic/beacon_chain/slot_timer.ex @@ -37,38 +37,54 @@ defmodule Archethic.BeaconChain.SlotTimer do Give the next beacon chain slot using the `SlotTimer` interval """ @spec next_slot(DateTime.t()) :: DateTime.t() - def next_slot(date_from = %DateTime{microsecond: {0, 0}}) do - get_interval() - |> CronParser.parse!(true) - |> CronScheduler.get_next_run_dates(DateTime.to_naive(date_from)) - |> Enum.at(1) - |> DateTime.from_naive!("Etc/UTC") - end - def next_slot(date_from = %DateTime{}) do - get_interval() - |> CronParser.parse!(true) - |> CronScheduler.get_next_run_date!(DateTime.to_naive(date_from)) - |> DateTime.from_naive!("Etc/UTC") + cron_expression = CronParser.parse!(get_interval(), true) + naive_date_from = DateTime.to_naive(date_from) + + if Crontab.DateChecker.matches_date?(cron_expression, naive_date_from) do + case date_from do + %DateTime{microsecond: {0, _}} -> + cron_expression + |> CronScheduler.get_next_run_dates(naive_date_from) + |> Enum.at(1) + |> DateTime.from_naive!("Etc/UTC") + + _ -> + cron_expression + |> CronScheduler.get_next_run_date!(naive_date_from) + |> DateTime.from_naive!("Etc/UTC") + end + else + cron_expression + |> CronScheduler.get_next_run_date!(naive_date_from) + |> DateTime.from_naive!("Etc/UTC") + end end @doc """ Returns the previous slot from the given date """ @spec previous_slot(DateTime.t()) :: DateTime.t() - def previous_slot(date_from = %DateTime{microsecond: {0, 0}}) do - get_interval() - |> CronParser.parse!(true) - |> CronScheduler.get_previous_run_dates(DateTime.to_naive(date_from)) - |> Enum.at(1) - |> DateTime.from_naive!("Etc/UTC") - end - def previous_slot(date_from = %DateTime{}) do - get_interval() - |> CronParser.parse!(true) - |> CronScheduler.get_previous_run_date!(DateTime.to_naive(date_from)) - |> DateTime.from_naive!("Etc/UTC") + cron_expression = CronParser.parse!(get_interval(), true) + naive_date_from = DateTime.to_naive(date_from) + + if Crontab.DateChecker.matches_date?(cron_expression, naive_date_from) do + case date_from do + %DateTime{microsecond: {microsecond, _}} when microsecond > 0 -> + DateTime.truncate(date_from, :second) + + _ -> + cron_expression + |> CronScheduler.get_previous_run_dates(naive_date_from) + |> Enum.at(1) + |> DateTime.from_naive!("Etc/UTC") + end + else + cron_expression + |> CronScheduler.get_previous_run_date!(naive_date_from) + |> DateTime.from_naive!("Etc/UTC") + end end @doc """ @@ -113,15 +129,19 @@ defmodule Archethic.BeaconChain.SlotTimer do :up -> Logger.info("Slot Timer: Starting...") + next_time = next_slot(DateTime.utc_now() |> DateTime.truncate(:second)) - {:ok, %{interval: interval, timer: schedule_new_slot(interval)}} + {:ok, %{interval: interval, timer: schedule_new_slot(interval), next_time: next_time}} end end def handle_info(:node_up, server_data = %{interval: interval}) do Logger.info("Slot Timer: Starting...") - new_server_data = Map.put(server_data, :timer, schedule_new_slot(interval)) + new_server_data = + server_data + |> Map.put(:timer, schedule_new_slot(interval)) + |> Map.put(:next_time, next_slot(DateTime.utc_now() |> DateTime.truncate(:second))) {:noreply, new_server_data, :hibernate} end @@ -129,12 +149,13 @@ defmodule Archethic.BeaconChain.SlotTimer do def handle_info( :new_slot, state = %{ - interval: interval + interval: interval, + next_time: next_time } ) do timer = schedule_new_slot(interval) - slot_time = DateTime.utc_now() |> DateTime.truncate(:millisecond) + slot_time = next_time PubSub.notify_current_epoch_of_slot_timer(slot_time) @@ -153,7 +174,14 @@ defmodule Archethic.BeaconChain.SlotTimer do :skip end - {:noreply, Map.put(state, :timer, timer), :hibernate} + next_time = next_slot(next_time) + + new_state = + state + |> Map.put(:timer, timer) + |> Map.put(:next_time, next_time) + + {:noreply, new_state, :hibernate} end def handle_cast({:new_conf, conf}, state) do diff --git a/lib/archethic/beacon_chain/subset.ex b/lib/archethic/beacon_chain/subset.ex index 565555241..d0ba87592 100644 --- a/lib/archethic/beacon_chain/subset.ex +++ b/lib/archethic/beacon_chain/subset.ex @@ -58,6 +58,14 @@ defmodule Archethic.BeaconChain.Subset do GenServer.cast(via_tuple(subset), {:add_slot, slot, node_public_key, signature}) end + @doc """ + Add node public key to the corresponding subset for beacon updates + """ + @spec subscribe_for_beacon_updates(binary(), Crypto.key()) :: :ok + def subscribe_for_beacon_updates(subset, node_public_key) do + GenServer.cast(via_tuple(subset), {:subscribe_node_to_beacon_updates, node_public_key}) + end + @doc """ Get the current slot """ @@ -77,7 +85,10 @@ defmodule Archethic.BeaconChain.Subset do %{ node_public_key: Crypto.first_node_public_key(), subset: subset, - current_slot: %Slot{subset: subset, slot_time: SlotTimer.next_slot(DateTime.utc_now())}, + current_slot: %Slot{ + subset: subset, + slot_time: SlotTimer.next_slot(DateTime.utc_now() |> DateTime.truncate(:second)) + }, subscribed_nodes: [], postponed: %{end_of_sync: [], transaction_attestations: []} }} @@ -218,7 +229,7 @@ defmodule Archethic.BeaconChain.Subset do new_state = update_in(state, [:postponed, :transaction_attestations], &[attestation | &1]) Logger.info( - "Transaction #{type}@#{Base.encode16(address)} will be added to the next beacon chain (#{DateTime.to_string(next_slot_time)} slot)", + "Transaction #{type}@#{Base.encode16(address)} will be added to the next beacon chain (#{DateTime.to_string(next_slot_time)} slot) - tx timestamp: #{timestamp} - current slot time: #{slot_time}", beacon_subset: Base.encode16(subset) ) @@ -261,7 +272,7 @@ defmodule Archethic.BeaconChain.Subset do # Avoid to store or dispatch an empty beacon's slot unless Slot.empty?(current_slot) do - current_slot = %{current_slot | slot_time: SlotTimer.previous_slot(time)} + # current_slot = %{current_slot | slot_time: SlotTimer.previous_slot(time)} if summary_time?(time) do SummaryCache.add_slot(subset, current_slot) @@ -371,12 +382,4 @@ defmodule Archethic.BeaconChain.Subset do end defp ensure_p2p_view(slot = %Slot{}, _), do: slot - - @doc """ - Add node public key to the corresponding subset for beacon updates - """ - @spec subscribe_for_beacon_updates(binary(), Crypto.key()) :: :ok - def subscribe_for_beacon_updates(subset, node_public_key) do - GenServer.cast(via_tuple(subset), {:subscribe_node_to_beacon_updates, node_public_key}) - end end diff --git a/lib/archethic/beacon_chain/summary_timer.ex b/lib/archethic/beacon_chain/summary_timer.ex index d3663162d..99a16b641 100644 --- a/lib/archethic/beacon_chain/summary_timer.ex +++ b/lib/archethic/beacon_chain/summary_timer.ex @@ -28,10 +28,18 @@ defmodule Archethic.BeaconChain.SummaryTimer do naive_date_from = DateTime.to_naive(date_from) if Crontab.DateChecker.matches_date?(cron_expression, naive_date_from) do - cron_expression - |> CronScheduler.get_next_run_dates(naive_date_from) - |> Enum.at(1) - |> DateTime.from_naive!("Etc/UTC") + case date_from do + %DateTime{microsecond: {0, _}} -> + cron_expression + |> CronScheduler.get_next_run_dates(naive_date_from) + |> Enum.at(1) + |> DateTime.from_naive!("Etc/UTC") + + _ -> + cron_expression + |> CronScheduler.get_next_run_date!(naive_date_from) + |> DateTime.from_naive!("Etc/UTC") + end else cron_expression |> CronScheduler.get_next_run_date!(naive_date_from) @@ -48,10 +56,16 @@ defmodule Archethic.BeaconChain.SummaryTimer do naive_date_from = DateTime.to_naive(date_from) if Crontab.DateChecker.matches_date?(cron_expression, naive_date_from) do - cron_expression - |> CronScheduler.get_previous_run_dates(naive_date_from) - |> Enum.at(1) - |> DateTime.from_naive!("Etc/UTC") + case date_from do + %DateTime{microsecond: {microsecond, _}} when microsecond > 0 -> + DateTime.truncate(date_from, :second) + + _ -> + cron_expression + |> CronScheduler.get_previous_run_dates(naive_date_from) + |> Enum.at(1) + |> DateTime.from_naive!("Etc/UTC") + end else cron_expression |> CronScheduler.get_previous_run_date!(naive_date_from) diff --git a/lib/archethic/self_repair/sync.ex b/lib/archethic/self_repair/sync.ex index 41080b399..b8699bb0c 100644 --- a/lib/archethic/self_repair/sync.ex +++ b/lib/archethic/self_repair/sync.ex @@ -106,7 +106,8 @@ defmodule Archethic.SelfRepair.Sync do patch :: binary() ) :: :ok | {:error, :unreachable_nodes} def load_missed_transactions(last_sync_date = %DateTime{}, patch) when is_binary(patch) do - last_summary_time = BeaconChain.previous_summary_time(DateTime.utc_now()) + last_summary_time = + BeaconChain.previous_summary_time(DateTime.utc_now() |> DateTime.truncate(:second)) if last_summary_time > last_sync_date do Logger.info( From 6912aacbd4a38ebd07c62a50a3d6b1683980664c Mon Sep 17 00:00:00 2001 From: Samuel Manzanera Date: Wed, 26 Oct 2022 00:27:40 +0200 Subject: [PATCH 2/5] Requested changes --- lib/archethic/beacon_chain/slot_timer.ex | 50 ++--------- lib/archethic/beacon_chain/subset.ex | 2 +- lib/archethic/beacon_chain/summary_timer.ex | 46 ++-------- lib/archethic/mining/distributed_workflow.ex | 5 +- .../mining/pending_transaction_validation.ex | 8 +- lib/archethic/mining/standalone_workflow.ex | 2 +- lib/archethic/oracle_chain.ex | 45 ++++------ lib/archethic/oracle_chain/scheduler.ex | 20 ++--- lib/archethic/reward.ex | 43 +++------- lib/archethic/reward/scheduler.ex | 32 ------- lib/archethic/self_repair/sync.ex | 3 +- lib/archethic/shared_secrets.ex | 26 ++---- .../shared_secrets/node_renewal_scheduler.ex | 4 +- lib/archethic/utils.ex | 83 +++++++++++++++++++ 14 files changed, 151 insertions(+), 218 deletions(-) diff --git a/lib/archethic/beacon_chain/slot_timer.ex b/lib/archethic/beacon_chain/slot_timer.ex index 2d50464f2..bb242abbe 100644 --- a/lib/archethic/beacon_chain/slot_timer.ex +++ b/lib/archethic/beacon_chain/slot_timer.ex @@ -38,27 +38,9 @@ defmodule Archethic.BeaconChain.SlotTimer do """ @spec next_slot(DateTime.t()) :: DateTime.t() def next_slot(date_from = %DateTime{}) do - cron_expression = CronParser.parse!(get_interval(), true) - naive_date_from = DateTime.to_naive(date_from) - - if Crontab.DateChecker.matches_date?(cron_expression, naive_date_from) do - case date_from do - %DateTime{microsecond: {0, _}} -> - cron_expression - |> CronScheduler.get_next_run_dates(naive_date_from) - |> Enum.at(1) - |> DateTime.from_naive!("Etc/UTC") - - _ -> - cron_expression - |> CronScheduler.get_next_run_date!(naive_date_from) - |> DateTime.from_naive!("Etc/UTC") - end - else - cron_expression - |> CronScheduler.get_next_run_date!(naive_date_from) - |> DateTime.from_naive!("Etc/UTC") - end + get_interval() + |> CronParser.parse!(true) + |> Utils.next_date(date_from) end @doc """ @@ -66,25 +48,9 @@ defmodule Archethic.BeaconChain.SlotTimer do """ @spec previous_slot(DateTime.t()) :: DateTime.t() def previous_slot(date_from = %DateTime{}) do - cron_expression = CronParser.parse!(get_interval(), true) - naive_date_from = DateTime.to_naive(date_from) - - if Crontab.DateChecker.matches_date?(cron_expression, naive_date_from) do - case date_from do - %DateTime{microsecond: {microsecond, _}} when microsecond > 0 -> - DateTime.truncate(date_from, :second) - - _ -> - cron_expression - |> CronScheduler.get_previous_run_dates(naive_date_from) - |> Enum.at(1) - |> DateTime.from_naive!("Etc/UTC") - end - else - cron_expression - |> CronScheduler.get_previous_run_date!(naive_date_from) - |> DateTime.from_naive!("Etc/UTC") - end + get_interval() + |> CronParser.parse!(true) + |> Utils.previous_date(date_from) end @doc """ @@ -129,7 +95,7 @@ defmodule Archethic.BeaconChain.SlotTimer do :up -> Logger.info("Slot Timer: Starting...") - next_time = next_slot(DateTime.utc_now() |> DateTime.truncate(:second)) + next_time = next_slot(DateTime.utc_now()) {:ok, %{interval: interval, timer: schedule_new_slot(interval), next_time: next_time}} end @@ -141,7 +107,7 @@ defmodule Archethic.BeaconChain.SlotTimer do new_server_data = server_data |> Map.put(:timer, schedule_new_slot(interval)) - |> Map.put(:next_time, next_slot(DateTime.utc_now() |> DateTime.truncate(:second))) + |> Map.put(:next_time, next_slot(DateTime.utc_now())) {:noreply, new_server_data, :hibernate} end diff --git a/lib/archethic/beacon_chain/subset.ex b/lib/archethic/beacon_chain/subset.ex index d0ba87592..4b88c1f22 100644 --- a/lib/archethic/beacon_chain/subset.ex +++ b/lib/archethic/beacon_chain/subset.ex @@ -87,7 +87,7 @@ defmodule Archethic.BeaconChain.Subset do subset: subset, current_slot: %Slot{ subset: subset, - slot_time: SlotTimer.next_slot(DateTime.utc_now() |> DateTime.truncate(:second)) + slot_time: SlotTimer.next_slot(DateTime.utc_now()) }, subscribed_nodes: [], postponed: %{end_of_sync: [], transaction_attestations: []} diff --git a/lib/archethic/beacon_chain/summary_timer.ex b/lib/archethic/beacon_chain/summary_timer.ex index 99a16b641..c50a49953 100644 --- a/lib/archethic/beacon_chain/summary_timer.ex +++ b/lib/archethic/beacon_chain/summary_timer.ex @@ -24,27 +24,9 @@ defmodule Archethic.BeaconChain.SummaryTimer do """ @spec next_summary(DateTime.t()) :: DateTime.t() def next_summary(date_from = %DateTime{}) do - cron_expression = CronParser.parse!(get_interval(), true) - naive_date_from = DateTime.to_naive(date_from) - - if Crontab.DateChecker.matches_date?(cron_expression, naive_date_from) do - case date_from do - %DateTime{microsecond: {0, _}} -> - cron_expression - |> CronScheduler.get_next_run_dates(naive_date_from) - |> Enum.at(1) - |> DateTime.from_naive!("Etc/UTC") - - _ -> - cron_expression - |> CronScheduler.get_next_run_date!(naive_date_from) - |> DateTime.from_naive!("Etc/UTC") - end - else - cron_expression - |> CronScheduler.get_next_run_date!(naive_date_from) - |> DateTime.from_naive!("Etc/UTC") - end + get_interval() + |> CronParser.parse!(true) + |> Utils.next_date(date_from) end @doc """ @@ -52,25 +34,9 @@ defmodule Archethic.BeaconChain.SummaryTimer do """ @spec previous_summary(DateTime.t()) :: DateTime.t() def previous_summary(date_from = %DateTime{}) do - cron_expression = CronParser.parse!(get_interval(), true) - naive_date_from = DateTime.to_naive(date_from) - - if Crontab.DateChecker.matches_date?(cron_expression, naive_date_from) do - case date_from do - %DateTime{microsecond: {microsecond, _}} when microsecond > 0 -> - DateTime.truncate(date_from, :second) - - _ -> - cron_expression - |> CronScheduler.get_previous_run_dates(naive_date_from) - |> Enum.at(1) - |> DateTime.from_naive!("Etc/UTC") - end - else - cron_expression - |> CronScheduler.get_previous_run_date!(naive_date_from) - |> DateTime.from_naive!("Etc/UTC") - end + get_interval() + |> CronParser.parse!(true) + |> Utils.previous_date(date_from) end @doc """ diff --git a/lib/archethic/mining/distributed_workflow.ex b/lib/archethic/mining/distributed_workflow.ex index f14b14b8b..3750cd4bf 100644 --- a/lib/archethic/mining/distributed_workflow.ex +++ b/lib/archethic/mining/distributed_workflow.ex @@ -226,14 +226,15 @@ defmodule Archethic.Mining.DistributedWorkflow do context: context = %ValidationContext{ transaction: tx, - coordinator_node: %Node{last_public_key: coordinator_key} + coordinator_node: %Node{last_public_key: coordinator_key}, + validation_time: validation_time } } ) do role = if node_public_key == coordinator_key, do: :coordinator, else: :cross_validator new_context = - case PendingTransactionValidation.validate(tx) do + case PendingTransactionValidation.validate(tx, validation_time) do :ok -> Logger.debug("Pending transaction valid", transaction_address: Base.encode16(tx.address), diff --git a/lib/archethic/mining/pending_transaction_validation.ex b/lib/archethic/mining/pending_transaction_validation.ex index a39415632..6f0f0dc9b 100644 --- a/lib/archethic/mining/pending_transaction_validation.ex +++ b/lib/archethic/mining/pending_transaction_validation.ex @@ -241,7 +241,11 @@ defmodule Archethic.Mining.PendingTransactionValidation do {:ok, %Transaction{type: :mint_rewards}} -> :ok - _ -> + {last_address, last_time} -> + Logger.debug( + "Invalid address #{Base.encode16(last_address)} - expected #{Base.encode16(last_network_pool_address)} - at last scheduling time: #{last_scheduling_date} - last time #{last_time}" + ) + {:error, :time} end @@ -534,7 +538,7 @@ defmodule Archethic.Mining.PendingTransactionValidation do with :ok <- verify_token_creation(content), {:ok, %{"supply" => ^total_fee}} <- Jason.decode(content), {^last_address, _} <- - DB.get_last_chain_address(genesis_address, Reward.last_scheduling_date()) do + DB.get_last_chain_address(genesis_address, Reward.get_last_scheduling_date()) do :ok else {:ok, %{"supply" => _}} -> diff --git a/lib/archethic/mining/standalone_workflow.ex b/lib/archethic/mining/standalone_workflow.ex index e8efa8422..044709512 100644 --- a/lib/archethic/mining/standalone_workflow.ex +++ b/lib/archethic/mining/standalone_workflow.ex @@ -111,7 +111,7 @@ defmodule Archethic.Mining.StandaloneWorkflow do ) validation_context = - case PendingTransactionValidation.validate(tx) do + case PendingTransactionValidation.validate(tx, validation_time) do :ok -> ValidationContext.set_pending_transaction_validation(validation_context, true) diff --git a/lib/archethic/oracle_chain.ex b/lib/archethic/oracle_chain.ex index a65a44234..cf37fce99 100644 --- a/lib/archethic/oracle_chain.ex +++ b/lib/archethic/oracle_chain.ex @@ -5,6 +5,8 @@ defmodule Archethic.OracleChain do UCO Price is the first network Oracle and it's used for many algorithms such as: transaction fee, node rewards, smart contracts """ + alias Archethic.Crypto + alias __MODULE__.{ MemTable, MemTableLoader, @@ -14,12 +16,11 @@ defmodule Archethic.OracleChain do } alias Archethic.TransactionChain.Transaction + alias Archethic.Utils alias Crontab.CronExpression.Parser, as: CronParser alias Crontab.Scheduler, as: CronScheduler - alias Archethic.Crypto - require Logger @doc """ @@ -145,14 +146,10 @@ defmodule Archethic.OracleChain do """ @spec next_summary_date(DateTime.t()) :: DateTime.t() def next_summary_date(date_from = %DateTime{}) do - interval = - Application.get_env(:archethic, Scheduler) - |> Keyword.fetch!(:summary_interval) - - interval + Application.get_env(:archethic, Scheduler) + |> Keyword.fetch!(:summary_interval) |> CronParser.parse!(true) - |> CronScheduler.get_next_run_date!(DateTime.to_naive(date_from)) - |> DateTime.from_naive!("Etc/UTC") + |> Utils.next_date(date_from) end @doc """ @@ -160,35 +157,21 @@ defmodule Archethic.OracleChain do """ @spec previous_summary_date(DateTime.t()) :: DateTime.t() def previous_summary_date(date_from = %DateTime{}) do - interval = - Application.get_env(:archethic, Scheduler) - |> Keyword.fetch!(:summary_interval) - - interval + Application.get_env(:archethic, Scheduler) + |> Keyword.fetch!(:summary_interval) |> CronParser.parse!(true) - |> CronScheduler.get_previous_run_date!(DateTime.to_naive(date_from)) - |> DateTime.from_naive!("Etc/UTC") + |> Utils.previous_date(date_from) end @doc """ Get the previous polling date from the given date """ @spec get_last_scheduling_date(DateTime.t()) :: DateTime.t() - def get_last_scheduling_date(from_date = %DateTime{}) do - polling_interval = - Application.get_env(:archethic, Scheduler) - |> Keyword.fetch!(:polling_interval) - - cron_expression = Crontab.CronExpression.Parser.parse!(polling_interval, true) - naive_from_date = from_date |> DateTime.truncate(:second) |> DateTime.to_naive() - - if Crontab.DateChecker.matches_date?(cron_expression, naive_from_date) do - DateTime.truncate(from_date, :second) - else - cron_expression - |> Crontab.Scheduler.get_previous_run_date!(naive_from_date) - |> DateTime.from_naive!("Etc/UTC") - end + def get_last_scheduling_date(date_from = %DateTime{}) do + Application.get_env(:archethic, Scheduler) + |> Keyword.fetch!(:polling_interval) + |> CronParser.parse!(true) + |> Utils.previous_date(date_from) end @doc """ diff --git a/lib/archethic/oracle_chain/scheduler.ex b/lib/archethic/oracle_chain/scheduler.ex index 8842ad36b..59a5d308a 100644 --- a/lib/archethic/oracle_chain/scheduler.ex +++ b/lib/archethic/oracle_chain/scheduler.ex @@ -19,9 +19,11 @@ defmodule Archethic.OracleChain.Scheduler do alias Archethic.TransactionChain alias Archethic.TransactionChain.Transaction alias Archethic.TransactionChain.TransactionData + + alias Archethic.Utils alias Archethic.Utils.DetectNodeResponsiveness + alias Crontab.CronExpression.Parser, as: CronParser - alias Crontab.Scheduler, as: CronScheduler use GenStateMachine, callback_mode: [:handle_event_function] @@ -746,19 +748,9 @@ defmodule Archethic.OracleChain.Scheduler do end defp next_date(interval, from_date = %DateTime{}) do - cron_expression = CronParser.parse!(interval, true) - naive_from_date = from_date |> DateTime.truncate(:second) |> DateTime.to_naive() - - if Crontab.DateChecker.matches_date?(cron_expression, naive_from_date) do - cron_expression - |> CronScheduler.get_next_run_dates(naive_from_date) - |> Enum.at(1) - |> DateTime.from_naive!("Etc/UTC") - else - cron_expression - |> CronScheduler.get_next_run_date!(naive_from_date) - |> DateTime.from_naive!("Etc/UTC") - end + interval + |> CronParser.parse!(true) + |> Utils.next_date(from_date) end defp get_validation_nodes(summary_date, index) do diff --git a/lib/archethic/reward.ex b/lib/archethic/reward.ex index 861794fad..97064e3ae 100644 --- a/lib/archethic/reward.ex +++ b/lib/archethic/reward.ex @@ -3,18 +3,19 @@ defmodule Archethic.Reward do Module which handles the rewards and transfer scheduling """ - alias Archethic.OracleChain - alias Archethic.Crypto alias Archethic.Account - alias Archethic.SharedSecrets + alias Archethic.OracleChain alias Archethic.P2P alias Archethic.P2P.Node + alias __MODULE__.MemTables.RewardTokens + alias __MODULE__.MemTablesLoader alias __MODULE__.Scheduler + alias Archethic.SharedSecrets alias Archethic.TransactionChain @@ -26,8 +27,9 @@ defmodule Archethic.Reward do TransactionData.TokenLedger.Transfer } - alias Archethic.Reward.MemTables.RewardTokens - alias Archethic.Reward.MemTablesLoader + alias Archethic.Utils + + alias Crontab.CronExpression.Parser, as: CronParser require Logger @@ -179,12 +181,6 @@ defmodule Archethic.Reward do defp get_node_transfers(_, [], _, acc), do: {acc, []} - @doc """ - Returns the last date of the rewards scheduling from the network pool - """ - @spec last_scheduling_date() :: DateTime.t() - defdelegate last_scheduling_date, to: Scheduler, as: :last_date - def config_change(changed_conf) do changed_conf |> Keyword.get(Scheduler) @@ -213,25 +209,12 @@ defmodule Archethic.Reward do Return the last scheduling date """ @spec get_last_scheduling_date(DateTime.t()) :: DateTime.t() - def get_last_scheduling_date(date_from = %DateTime{}) do - interval = - Application.get_env(:archethic, Scheduler) - |> Keyword.fetch!(:interval) - - cron_expression = Crontab.CronExpression.Parser.parse!(interval, true) - - naive_date_from = - date_from - |> DateTime.truncate(:second) - |> DateTime.to_naive() - - if Crontab.DateChecker.matches_date?(cron_expression, naive_date_from) do - DateTime.truncate(date_from, :second) - else - cron_expression - |> Crontab.Scheduler.get_previous_run_date!(naive_date_from) - |> DateTime.from_naive!("Etc/UTC") - end + def get_last_scheduling_date(date_from = %DateTime{} \\ DateTime.utc_now()) do + :archethic + |> Application.get_env(Scheduler) + |> Keyword.fetch!(:interval) + |> CronParser.parse!(true) + |> Utils.previous_date(date_from) end @key :reward_gen_addr diff --git a/lib/archethic/reward/scheduler.ex b/lib/archethic/reward/scheduler.ex index 97bc52ae8..fd20ddaa2 100644 --- a/lib/archethic/reward/scheduler.ex +++ b/lib/archethic/reward/scheduler.ex @@ -3,9 +3,6 @@ defmodule Archethic.Reward.Scheduler do use GenStateMachine, callback_mode: [:handle_event_function] - alias Crontab.CronExpression.Parser, as: CronParser - alias Crontab.Scheduler, as: CronScheduler - alias Archethic.{ Crypto, PubSub, @@ -25,14 +22,6 @@ defmodule Archethic.Reward.Scheduler do GenStateMachine.start_link(__MODULE__, args, opts) end - @doc """ - Get the last node rewards scheduling date - """ - @spec last_date() :: DateTime.t() - def last_date do - GenStateMachine.call(__MODULE__, :last_date) - end - def init(args) do interval = Keyword.fetch!(args, :interval) state_data = Map.put(%{}, :interval, interval) @@ -384,10 +373,6 @@ defmodule Archethic.Reward.Scheduler do {:next_state, :scheduled, new_data} end - def handle_event({:call, from}, :last_date, _state, _data = %{interval: interval}) do - {:keep_state_and_data, {:reply, from, get_last_date(interval)}} - end - def handle_event(:cast, {:new_conf, conf}, _, data) do case Keyword.get(conf, :interval) do nil -> @@ -421,23 +406,6 @@ defmodule Archethic.Reward.Scheduler do Archethic.send_new_transaction(node_reward_tx) end - defp get_last_date(interval) do - cron_expression = CronParser.parse!(interval, true) - - case DateTime.utc_now() do - %DateTime{microsecond: {0, 0}} -> - cron_expression - |> CronScheduler.get_next_run_dates(DateTime.utc_now() |> DateTime.to_naive()) - |> Enum.at(1) - |> DateTime.from_naive!("Etc/UTC") - - _ -> - cron_expression - |> CronScheduler.get_previous_run_date!(DateTime.utc_now() |> DateTime.to_naive()) - |> DateTime.from_naive!("Etc/UTC") - end - end - defp schedule(interval) do Process.send_after(self(), :mint_rewards, Utils.time_offset(interval) * 1000) end diff --git a/lib/archethic/self_repair/sync.ex b/lib/archethic/self_repair/sync.ex index b8699bb0c..41080b399 100644 --- a/lib/archethic/self_repair/sync.ex +++ b/lib/archethic/self_repair/sync.ex @@ -106,8 +106,7 @@ defmodule Archethic.SelfRepair.Sync do patch :: binary() ) :: :ok | {:error, :unreachable_nodes} def load_missed_transactions(last_sync_date = %DateTime{}, patch) when is_binary(patch) do - last_summary_time = - BeaconChain.previous_summary_time(DateTime.utc_now() |> DateTime.truncate(:second)) + last_summary_time = BeaconChain.previous_summary_time(DateTime.utc_now()) if last_summary_time > last_sync_date do Logger.info( diff --git a/lib/archethic/shared_secrets.ex b/lib/archethic/shared_secrets.ex index 4ce24e18d..b122d40a6 100644 --- a/lib/archethic/shared_secrets.ex +++ b/lib/archethic/shared_secrets.ex @@ -12,6 +12,10 @@ defmodule Archethic.SharedSecrets do alias Archethic.TransactionChain.Transaction alias Archethic.TransactionChain + alias Archethic.Utils + + alias Crontab.CronExpression.Parser, as: CronParser + require Logger @type origin_family :: :software | :hardware | :biometric @@ -133,24 +137,10 @@ defmodule Archethic.SharedSecrets do """ @spec get_last_scheduling_date(DateTime.t()) :: DateTime.t() def get_last_scheduling_date(date_from = %DateTime{}) do - interval = - Application.get_env(:archethic, NodeRenewalScheduler) - |> Keyword.fetch!(:interval) - - cron_expression = Crontab.CronExpression.Parser.parse!(interval, true) - - naive_date_from = - date_from - |> DateTime.truncate(:second) - |> DateTime.to_naive() - - if Crontab.DateChecker.matches_date?(cron_expression, naive_date_from) do - DateTime.truncate(date_from, :second) - else - cron_expression - |> Crontab.Scheduler.get_previous_run_date!(naive_date_from) - |> DateTime.from_naive!("Etc/UTC") - end + Application.get_env(:archethic, NodeRenewalScheduler) + |> Keyword.fetch!(:interval) + |> CronParser.parse!(true) + |> Utils.previous_date(date_from) end @nss_gen_key :node_shared_secrets_gen_addr diff --git a/lib/archethic/shared_secrets/node_renewal_scheduler.ex b/lib/archethic/shared_secrets/node_renewal_scheduler.ex index f41e88d2b..c8eeef75b 100644 --- a/lib/archethic/shared_secrets/node_renewal_scheduler.ex +++ b/lib/archethic/shared_secrets/node_renewal_scheduler.ex @@ -8,7 +8,6 @@ defmodule Archethic.SharedSecrets.NodeRenewalScheduler do """ alias Crontab.CronExpression.Parser, as: CronParser - alias Crontab.Scheduler, as: CronScheduler alias Archethic @@ -334,8 +333,7 @@ defmodule Archethic.SharedSecrets.NodeRenewalScheduler do def next_application_date(date_from = %DateTime{}) do get_application_date_interval() |> CronParser.parse!(true) - |> CronScheduler.get_next_run_date!(DateTime.to_naive(date_from)) - |> DateTime.from_naive!("Etc/UTC") + |> Utils.next_date(date_from) end defp get_application_date_interval do diff --git a/lib/archethic/utils.ex b/lib/archethic/utils.ex index 7e7549ff6..9fb02c89c 100644 --- a/lib/archethic/utils.ex +++ b/lib/archethic/utils.ex @@ -728,4 +728,87 @@ defmodule Archethic.Utils do (m1 + m2) / 2 end end + + @doc """ + Return the next date with the cron expression from a given date + + ## Examples + + iex> Utils.next_date(%Crontab.CronExpression{second: [{:/, :*, 10}], extended: true}, ~U[2022-10-01 01:00:00Z]) + ~U[2022-10-01 01:00:10Z] + + iex> Utils.next_date(%Crontab.CronExpression{second: [{:/, :*, 10}], extended: true}, ~U[2022-10-01 01:00:00.100Z]) + ~U[2022-10-01 01:00:10Z] + + iex> Utils.next_date(%Crontab.CronExpression{second: [{:/, :*, 10}], extended: true}, ~U[2022-10-01 01:00:05Z]) + ~U[2022-10-01 01:00:10Z] + + iex> Utils.next_date(%Crontab.CronExpression{second: [{:/, :*, 10}], extended: true}, ~U[2022-10-01 01:00:05.139Z]) + ~U[2022-10-01 01:00:10Z] + """ + @spec next_date(Crontab.CronExpression.t(), DateTime.t()) :: DateTime.t() + def next_date(cron_expression, date_from = %DateTime{}) do + naive_date_from = DateTime.to_naive(date_from) + + if Crontab.DateChecker.matches_date?(cron_expression, naive_date_from) do + case date_from do + %DateTime{microsecond: {0, _}} -> + cron_expression + |> Crontab.Scheduler.get_next_run_dates(naive_date_from) + |> Enum.at(1) + |> DateTime.from_naive!("Etc/UTC") + + _ -> + cron_expression + |> Crontab.Scheduler.get_next_run_date!(naive_date_from) + |> DateTime.from_naive!("Etc/UTC") + end + else + cron_expression + |> Crontab.Scheduler.get_next_run_date!(naive_date_from) + |> DateTime.from_naive!("Etc/UTC") + end + end + + @doc """ + Return the previous date with the cron expression from a given date + + ## Examples + + iex> Utils.previous_date(%Crontab.CronExpression{second: [{:/, :*, 10}], extended: true}, ~U[2022-10-01 01:00:00Z]) + ~U[2022-10-01 00:59:50Z] + + iex> Utils.previous_date(%Crontab.CronExpression{second: [{:/, :*, 10}], extended: true}, ~U[2022-10-01 01:00:00.100Z]) + ~U[2022-10-01 01:00:00Z] + + iex> Utils.previous_date(%Crontab.CronExpression{second: [{:/, :*, 10}], extended: true}, ~U[2022-10-01 01:00:10Z]) + ~U[2022-10-01 01:00:00Z] + + iex> Utils.previous_date(%Crontab.CronExpression{second: [{:/, :*, 10}], extended: true}, ~U[2022-10-01 01:00:10.100Z]) + ~U[2022-10-01 01:00:10Z] + + iex> Utils.previous_date(%Crontab.CronExpression{second: [{:/, :*, 30}], extended: true}, ~U[2022-10-26 07:38:30.569648Z]) + ~U[2022-10-26 07:38:30Z] + """ + @spec previous_date(Crontab.CronExpression.t(), DateTime.t()) :: DateTime.t() + def previous_date(cron_expression, date_from = %DateTime{}) do + naive_date_from = DateTime.to_naive(date_from) + + if Crontab.DateChecker.matches_date?(cron_expression, naive_date_from) do + case date_from do + %DateTime{microsecond: {microsecond, _}} when microsecond > 0 -> + DateTime.truncate(date_from, :second) + + _ -> + cron_expression + |> Crontab.Scheduler.get_previous_run_dates(naive_date_from) + |> Enum.at(1) + |> DateTime.from_naive!("Etc/UTC") + end + else + cron_expression + |> Crontab.Scheduler.get_previous_run_date!(naive_date_from) + |> DateTime.from_naive!("Etc/UTC") + end + end end From 94072e50d35580b1efbe49438ad8076ee4e2de4e Mon Sep 17 00:00:00 2001 From: Samuel Manzanera Date: Thu, 27 Oct 2022 15:39:31 +0200 Subject: [PATCH 3/5] up --- lib/archethic/beacon_chain/subset.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/archethic/beacon_chain/subset.ex b/lib/archethic/beacon_chain/subset.ex index 4b88c1f22..6c1084d35 100644 --- a/lib/archethic/beacon_chain/subset.ex +++ b/lib/archethic/beacon_chain/subset.ex @@ -272,7 +272,7 @@ defmodule Archethic.BeaconChain.Subset do # Avoid to store or dispatch an empty beacon's slot unless Slot.empty?(current_slot) do - # current_slot = %{current_slot | slot_time: SlotTimer.previous_slot(time)} + current_slot = %{current_slot | slot_time: time} if summary_time?(time) do SummaryCache.add_slot(subset, current_slot) From 428fb878bf28785d1d2811f75255741b8ac5cb9d Mon Sep 17 00:00:00 2001 From: Samuel Manzanera Date: Thu, 27 Oct 2022 18:13:30 +0200 Subject: [PATCH 4/5] fix test --- .../crypto/keystore/shared_secrets/software_impl_test.exs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 b2a08623e..bcb5d491d 100644 --- a/test/archethic/crypto/keystore/shared_secrets/software_impl_test.exs +++ b/test/archethic/crypto/keystore/shared_secrets/software_impl_test.exs @@ -23,7 +23,7 @@ defmodule Archethic.Crypto.SharedSecrets.SoftwareImplTest do test "should initialize the node shared secrets index from the stored transactions" do %{secrets: secrets, daily_nonce_seed: daily_nonce_seed, aes_key: aes_key} = gen_secrets() - timestamp = ~U[2021-10-10 00:00:00Z] + timestamp = ~U[2021-10-09 23:55:00Z] MockDB |> stub(:count_transactions_by_type, fn @@ -50,7 +50,7 @@ defmodule Archethic.Crypto.SharedSecrets.SoftwareImplTest do daily_nonce_keypair = Crypto.generate_deterministic_keypair(daily_nonce_seed) - unix_timestamp = DateTime.to_unix(timestamp) + unix_timestamp = DateTime.to_unix(~U[2021-10-10 00:00:00Z]) assert [{_, 1}] = :ets.lookup(:archethic_shared_secrets_keystore, :shared_secrets_index) assert [{_, 2}] = :ets.lookup(:archethic_shared_secrets_keystore, :network_pool_index) From c2acb32b689d0e720bf494bd445a683dbb3ecb3e Mon Sep 17 00:00:00 2001 From: Samuel Manzanera Date: Thu, 27 Oct 2022 20:46:34 +0200 Subject: [PATCH 5/5] fix dialyzer --- lib/archethic/mining/pending_transaction_validation.ex | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/archethic/mining/pending_transaction_validation.ex b/lib/archethic/mining/pending_transaction_validation.ex index 6f0f0dc9b..f902c8d85 100644 --- a/lib/archethic/mining/pending_transaction_validation.ex +++ b/lib/archethic/mining/pending_transaction_validation.ex @@ -241,11 +241,7 @@ defmodule Archethic.Mining.PendingTransactionValidation do {:ok, %Transaction{type: :mint_rewards}} -> :ok - {last_address, last_time} -> - Logger.debug( - "Invalid address #{Base.encode16(last_address)} - expected #{Base.encode16(last_network_pool_address)} - at last scheduling time: #{last_scheduling_date} - last time #{last_time}" - ) - + _ -> {:error, :time} end