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

Fix beacon date projections #653

Merged
merged 5 commits into from
Oct 28, 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
44 changes: 19 additions & 25 deletions lib/archethic/beacon_chain/slot_timer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -37,38 +37,20 @@ 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")
|> Utils.next_date(date_from)
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")
|> Utils.previous_date(date_from)
end

@doc """
Expand Down Expand Up @@ -113,28 +95,33 @@ defmodule Archethic.BeaconChain.SlotTimer do

:up ->
Logger.info("Slot Timer: Starting...")
next_time = next_slot(DateTime.utc_now())

{: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()))

{:noreply, new_server_data, :hibernate}
end

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)

Expand All @@ -153,7 +140,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
Expand Down
25 changes: 14 additions & 11 deletions lib/archethic/beacon_chain/subset.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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
"""
Expand All @@ -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())
},
subscribed_nodes: [],
postponed: %{end_of_sync: [], transaction_attestations: []}
}}
Expand Down Expand Up @@ -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)
)

Expand Down Expand Up @@ -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: time}

if summary_time?(time) do
SummaryCache.add_slot(subset, current_slot)
Expand Down Expand Up @@ -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
32 changes: 6 additions & 26 deletions lib/archethic/beacon_chain/summary_timer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,39 +24,19 @@ 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
cron_expression
|> CronScheduler.get_next_run_dates(naive_date_from)
|> Enum.at(1)
|> DateTime.from_naive!("Etc/UTC")
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 """
Returns the list of previous summaries times from the given date
"""
@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
cron_expression
|> CronScheduler.get_previous_run_dates(naive_date_from)
|> Enum.at(1)
|> DateTime.from_naive!("Etc/UTC")
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 """
Expand Down
5 changes: 3 additions & 2 deletions lib/archethic/mining/distributed_workflow.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
2 changes: 1 addition & 1 deletion lib/archethic/mining/pending_transaction_validation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,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" => _}} ->
Expand Down
2 changes: 1 addition & 1 deletion lib/archethic/mining/standalone_workflow.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
45 changes: 14 additions & 31 deletions lib/archethic/oracle_chain.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 """
Expand Down Expand Up @@ -145,50 +146,32 @@ 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 """
Return the previous oracle summary date
"""
@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 """
Expand Down
20 changes: 6 additions & 14 deletions lib/archethic/oracle_chain/scheduler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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]

Expand Down Expand Up @@ -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
Expand Down
Loading