Skip to content

Commit

Permalink
Oracle live realtime feed (#89)
Browse files Browse the repository at this point in the history
* Add next_summary_date for OracleChain

* Display the next oracle date if not previous

* Display the oracle transaction in realtime

* Notify the timestamp in the PubSub of new transaction

* Use custom for template binding to avoid fetching data from DB

* Use next summary date when there are no previous oracle dates

* Improve date management to display the right number of transaction either summary or oracle tx

* Prevent to render transaction when you are on a passed date

* Update version
  • Loading branch information
Samuel authored and Samuel committed Sep 21, 2021
1 parent 3e6fadc commit fd60c0f
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 31 deletions.
2 changes: 1 addition & 1 deletion lib/archethic/contracts/worker.ex
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ defmodule ArchEthic.Contracts.Worker do
end

def handle_info(
{:new_transaction, tx_address, :oracle},
{:new_transaction, tx_address, :oracle, _timestamp},
state = %{
contract:
contract = %Contract{
Expand Down
11 changes: 11 additions & 0 deletions lib/archethic/oracle_chain.ex
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,15 @@ defmodule ArchEthic.OracleChain do
end)
|> Stream.map(&DateTime.from_naive!(&1, "Etc/UTC"))
end

@doc """
Return the next oracle summary date
"""
@spec next_summary_date(DateTime.t()) :: DateTime.t()
def next_summary_date(date_from = %DateTime{}) do
Scheduler.get_summary_interval()
|> CronParser.parse!(true)
|> CronScheduler.get_next_run_date!(DateTime.to_naive(date_from))
|> DateTime.from_naive!("Etc/UTC")
end
end
4 changes: 2 additions & 2 deletions lib/archethic/pub_sub.ex
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ defmodule ArchEthic.PubSub do
def notify_new_transaction(address, type, timestamp = %DateTime{})
when is_binary(address) and is_atom(type) do
dispatch(:new_transaction, {:new_transaction, address, type, timestamp})
dispatch({:new_transaction, address}, {:new_transaction, address})
dispatch({:new_transaction, type}, {:new_transaction, address, type})
dispatch({:new_transaction, address}, {:new_transaction, address, type, timestamp})
dispatch({:new_transaction, type}, {:new_transaction, address, type, timestamp})
end

def notify_new_transaction(address) when is_binary(address) do
Expand Down
78 changes: 55 additions & 23 deletions lib/archethic_web/live/chains/oracle_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ defmodule ArchEthicWeb.OracleChainLive do
PubSub.register_to_new_transaction_by_type(:oracle_summary)
end

next_summary_date = OracleChain.next_summary_date(DateTime.utc_now())

last_tx =
TransactionChain.list_transactions_by_type(:oracle,
data: [:content],
Expand All @@ -46,15 +48,22 @@ defmodule ArchEthicWeb.OracleChainLive do
{Jason.decode!(content), timestamp}
end

oracle_dates = get_oracle_dates() |> Enum.to_list()
oracle_dates =
case get_oracle_dates() |> Enum.to_list() do
[] ->
[next_summary_date]

dates ->
[next_summary_date | dates]
end

new_assign =
socket
|> assign(:last_oracle_data, last_oracle_data)
|> assign(:update_time, update_time)
|> assign(:update_time, update_time || next_summary_date)
|> assign(:dates, oracle_dates)
|> assign(:current_date_page, 1)
|> assign(:transactions, list_transactions_by_date(Enum.at(oracle_dates, 0)))
|> assign(:transactions, list_transactions_by_date(next_summary_date))

{:ok, new_assign}
end
Expand Down Expand Up @@ -92,15 +101,11 @@ defmodule ArchEthicWeb.OracleChainLive do
end

def handle_info(
{:new_transaction, address, :oracle},
socket
{:new_transaction, address, :oracle, timestamp},
socket = %{assigns: assigns = %{current_date_page: current_page}}
) do
{:ok,
%Transaction{
data: %TransactionData{content: content},
validation_stamp: %ValidationStamp{timestamp: timestamp}
}} =
TransactionChain.get_transaction(address, data: [:content], validation_stamp: [:timestamp])
{:ok, %Transaction{data: %TransactionData{content: content}}} =
TransactionChain.get_transaction(address, data: [:content])

last_oracle_data = Jason.decode!(content)

Expand All @@ -109,24 +114,43 @@ defmodule ArchEthicWeb.OracleChainLive do
|> assign(:last_oracle_data, last_oracle_data)
|> assign(:update_time, timestamp)

{:noreply, new_assign}
if current_page == 1 do
# Only update the transaction listed when you are on the first page
new_assign =
case Map.get(assigns, :summary_passed?) do
true ->
new_assign
|> assign(:transactions, [%{address: address, type: :oracle, timestamp: timestamp}])
|> assign(:summary_passed?, false)

_ ->
update(
new_assign,
:transactions,
&[%{address: address, type: :oracle, timestamp: timestamp} | &1]
)
end

{:noreply, new_assign}
else
{:noreply, new_assign}
end
end

def handle_info(
{:new_transaction, _address, :oracle_summary},
socket = %{assigns: %{current_date_page: page}}
{:new_transaction, address, :oracle_summary, timestamp},
socket
) do
dates = get_oracle_dates()

transactions =
dates
|> Enum.at(page - 1)
|> list_transactions_by_date()

new_assign =
socket
|> assign(:dates, dates)
|> assign(:transactions, transactions)
|> update(:dates, &[OracleChain.next_summary_date(timestamp) | &1])
|> update(
:transactions,
&[
%{address: address, type: :oracle_summary, timestamp: timestamp} | &1
]
)
|> assign(:summary_passed?, true)

{:noreply, new_assign}
end
Expand All @@ -145,6 +169,14 @@ defmodule ArchEthicWeb.OracleChainLive do
|> Crypto.derive_oracle_address(0)
|> TransactionChain.get_last_address()
|> TransactionChain.get([:address, :type, validation_stamp: [:timestamp]])
|> Stream.map(fn %Transaction{
address: address,
type: type,
validation_stamp: %ValidationStamp{timestamp: timestamp}
} ->
%{address: address, type: type, timestamp: timestamp}
end)
|> Enum.to_list()
end

defp list_transactions_by_date(nil), do: []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@
<div class="column is-3">
<div class="box has-text-centered">
<p class="heading">UCO Price</p>
<p class="title"><%= get_in(@last_oracle_data, ["uco", "usd"]) %> $</p>
<p class="title">
<%= if Enum.empty?(@last_oracle_data) do %>
N/A
<% else %>
<%= get_in(@last_oracle_data, ["uco", "usd"]) %> $</p>
<% end %>
</div>
</div>
</div>
Expand Down Expand Up @@ -69,7 +74,7 @@
<% end %>
</div>
<div class="column is-2-desktop">
<%= format_date(tx.validation_stamp.timestamp) %>
<%= format_date(tx.timestamp) %>
</div>
<div class="column is-1-desktop">
<span class="tag is-light is-info"><%= tx.type %></span>
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule ArchEthic.MixProject do
def project do
[
app: :archethic,
version: "0.11.4",
version: "0.11.5",
build_path: "_build",
config_path: "config/config.exs",
deps_path: "deps",
Expand Down
4 changes: 2 additions & 2 deletions test/archethic/pub_sub_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,15 @@ defmodule ArchEthic.PubSubTest do
timestamp = DateTime.utc_now()
assert :ok = PubSub.notify_new_transaction("@Alice2", :transfer, timestamp)

assert_receive {:new_transaction, "@Alice2"}
assert_receive {:new_transaction, "@Alice2", :transfer, ^timestamp}
end

test "should notify subscribers for a given type" do
{:ok, _} = PubSub.register_to_new_transaction_by_type(:transfer)
timestamp = DateTime.utc_now()
assert :ok = PubSub.notify_new_transaction("@Alice2", :transfer, timestamp)

assert_receive {:new_transaction, "@Alice2", :transfer}
assert_receive {:new_transaction, "@Alice2", :transfer, ^timestamp}
end
end

Expand Down

0 comments on commit fd60c0f

Please sign in to comment.