From 4f9eae73a4b48085ec7303a2f8041808487d3177 Mon Sep 17 00:00:00 2001 From: Stephen Ausman Date: Fri, 11 Mar 2022 06:50:00 -0600 Subject: [PATCH 01/41] wip1 --- lib/ckd/application.ex | 8 +- lib/ckd/{ => services/dero}/dero_rpc.ex | 0 lib/ckd/{ => services/dero}/dero_wallet.ex | 0 .../{ => services/dero}/dero_wallet_worker.ex | 89 ++++++++++++------- lib/ckd/services/m/m.ex | 42 +++++++++ lib/ckd/services/m/m_worker.ex | 48 ++++++++++ lib/ckd_web/channels/socket.ex | 2 +- lib/ckd_web/channels/v0/m_channel.ex | 7 ++ lib/ckd_web/live/home_live.html.leex | 17 +++- packages/ckd-web/src/channels/v0/MChannel.res | 42 +++------ packages/phux/src/phux.ts | 2 +- priv/contracts/v0/m.bas | 67 ++++++++++++++ priv/contracts/v0/m_app.bas | 57 ++++++++++++ 13 files changed, 314 insertions(+), 67 deletions(-) rename lib/ckd/{ => services/dero}/dero_rpc.ex (100%) rename lib/ckd/{ => services/dero}/dero_wallet.ex (100%) rename lib/ckd/{ => services/dero}/dero_wallet_worker.ex (50%) create mode 100644 lib/ckd/services/m/m.ex create mode 100644 lib/ckd/services/m/m_worker.ex create mode 100644 lib/ckd_web/channels/v0/m_channel.ex create mode 100644 priv/contracts/v0/m.bas create mode 100644 priv/contracts/v0/m_app.bas diff --git a/lib/ckd/application.ex b/lib/ckd/application.ex index 9d486dc..6c00e23 100644 --- a/lib/ckd/application.ex +++ b/lib/ckd/application.ex @@ -6,6 +6,7 @@ defmodule Ckd.Application do use Application @dero_wallet_worker_registry :dero_wallet_worker_registry + @m_worker_registry :m_worker_registry def start(_type, _args) do children = [ @@ -18,8 +19,10 @@ defmodule Ckd.Application do # State store {Ckd.State, [statefile: statefile_path()]}, # Services - Ckd.DeroWallet, {Registry, [keys: :unique, name: @dero_wallet_worker_registry]}, + Ckd.DeroWallet, + {Registry, [keys: :unique, name: @m_worker_registry]}, + Ckd.M, # Start the Endpoint (http/https) CkdWeb.Endpoint # Start a worker by calling: Ckd.Worker.start_link(arg) @@ -42,6 +45,9 @@ defmodule Ckd.Application do @spec dero_wallet_worker_registry :: :dero_wallet_worker_registry def dero_wallet_worker_registry, do: @dero_wallet_worker_registry + @spec m_worker_registry :: :m_worker_registry + def m_worker_registry, do: @m_worker_registry + # Tell Phoenix to update the endpoint configuration # whenever the application is updated. def config_change(changed, _new, removed) do diff --git a/lib/ckd/dero_rpc.ex b/lib/ckd/services/dero/dero_rpc.ex similarity index 100% rename from lib/ckd/dero_rpc.ex rename to lib/ckd/services/dero/dero_rpc.ex diff --git a/lib/ckd/dero_wallet.ex b/lib/ckd/services/dero/dero_wallet.ex similarity index 100% rename from lib/ckd/dero_wallet.ex rename to lib/ckd/services/dero/dero_wallet.ex diff --git a/lib/ckd/dero_wallet_worker.ex b/lib/ckd/services/dero/dero_wallet_worker.ex similarity index 50% rename from lib/ckd/dero_wallet_worker.ex rename to lib/ckd/services/dero/dero_wallet_worker.ex index 3c94f16..f6bcff1 100644 --- a/lib/ckd/dero_wallet_worker.ex +++ b/lib/ckd/services/dero/dero_wallet_worker.ex @@ -5,7 +5,8 @@ defmodule Ckd.DeroWalletWorker do require Logger @registry Ckd.Application.dero_wallet_worker_registry() - @fetch_info_interval 1_000 + @block_time 18_000 + @fetch_interval 400 @dero_divisor 100_000 def start_link(aid) when is_binary(aid), @@ -13,7 +14,7 @@ defmodule Ckd.DeroWalletWorker do def init(aid) when is_binary(aid) do {:ok, _existing_account = %{}} = Ckd.State.subscribe_info({:account, aid}) - {:ok, %{aid: aid, subs: [], dero: nil, info: nil}} + {:ok, %{aid: aid, subs: [], dero: nil, info: %{height: -1}}} end def handle_info({{:account, _aid}, nil}, _state), @@ -35,50 +36,42 @@ defmodule Ckd.DeroWalletWorker do def handle_info( msg = {:fetch_info, dero: dero}, - state = %{aid: aid, dero: dero, info: old_info, subs: subs} + state = %{ + aid: aid, + dero: dero, + info: info_was, + subs: subs + } ) do - with {:ok, %{"address" => address}} <- Ckd.DeroRPC.get_address(dero), - {:ok, %{"height" => height}} <- Ckd.DeroRPC.get_height(dero), - {:ok, %{"balance" => balance, "unlocked_balance" => unlocked_balance}} <- - Ckd.DeroRPC.get_balance(dero) do - min_height = calc_min_height(old_info) - - new_transfers = - case Ckd.DeroRPC.get_transfers(dero, min_height: min_height) do - {:ok, %{"entries" => new_transfers}} -> new_transfers - {:ok, _} -> [] - end - + with {:ok, height} <- get_height(dero), + {:ok, address} <- get_address(dero, info_was, new_height: height), + {:ok, balance: balance, unlocked_balance: unlocked_balance} <- + get_balance(dero, info_was, new_height: height) do info = %{ - address: address, height: height, - balance: balance / @dero_divisor, - unlocked_balance: unlocked_balance / @dero_divisor, - transfers: - case old_info do - nil -> new_transfers - %{transfers: old_transfers} -> old_transfers ++ new_transfers - end + address: address, + balance: balance, + unlocked_balance: unlocked_balance } - if info != old_info do + if info != info_was do Enum.each(subs, fn {pid, info_key = {:dero_wallet, ^aid}} -> send(pid, {info_key, info}) end) end - Process.send_after(self(), msg, @fetch_info_interval) + Process.send_after(self(), msg, @fetch_interval) - {:noreply, Map.put(state, :info, info)} + {:noreply, state |> Map.put(:info, info)} else {:error, message} -> Logger.error(message) - Process.send_after(self(), msg, @fetch_info_interval * 10) - {:noreply, state} + Process.send_after(self(), msg, @block_time) + {:noreply, Map.put(state, :last_fetch, :os.system_time(:millisecond))} end end - def handle_call({:fetch_info, dero: _stale_dero}, _from, state), do: {:noreply, state} + def handle_info({:fetch_info, dero: _stale_dero}, _from, state), do: {:noreply, state} def handle_call( {:subscribe_info, info_key = {:dero_wallet, aid}}, @@ -101,10 +94,42 @@ defmodule Ckd.DeroWalletWorker do Map.put(state, :subs, Enum.reject(subs, fn sub -> sub == {from_pid, info_key} end))} end - defp calc_min_height(_old_info = %{transfers: transfers}), - do: Enum.map(transfers, fn t -> t["height"] + 1 end) |> Enum.max(fn -> 0 end) + defp get_height(dero) do + case Ckd.DeroRPC.get_height(dero) do + {:ok, %{"height" => height}} -> {:ok, height} + e -> e + end + end + + defp get_address(dero, info_was = %{height: same_height}, new_height: same_height) + when is_map(dero) and is_number(same_height), + do: {:ok, info_was.address} - defp calc_min_height(_old_info = nil), do: 0 + defp get_address(dero, _info_was, new_height: _) do + case Ckd.DeroRPC.get_address(dero) do + {:ok, %{"address" => address}} -> + {:ok, address} + + e -> + e + end + end + + defp get_balance(dero, info_was = %{height: same_height}, new_height: same_height) + when is_map(dero) and is_number(same_height) do + {:ok, balance: info_was.balance, unlocked_balance: info_was.unlocked_balance} + end + + defp get_balance(dero, _info_was, new_height: _) do + case Ckd.DeroRPC.get_balance(dero) do + {:ok, %{"balance" => balance, "unlocked_balance" => unlocked_balance}} -> + {:ok, + balance: balance / @dero_divisor, unlocked_balance: unlocked_balance / @dero_divisor} + + e -> + e + end + end defp via_tuple(aid) when is_binary(aid), do: {:via, Registry, {@registry, aid}} diff --git a/lib/ckd/services/m/m.ex b/lib/ckd/services/m/m.ex new file mode 100644 index 0000000..ef82513 --- /dev/null +++ b/lib/ckd/services/m/m.ex @@ -0,0 +1,42 @@ +defmodule Ckd.M do + @moduledoc false + + use DynamicSupervisor + + alias Ckd.MWorker + + @spec start_link(any) :: :ignore | {:error, any} | {:ok, pid} + def start_link(_), do: DynamicSupervisor.start_link(__MODULE__, [], name: __MODULE__) + + @impl true + @spec init(any) :: + {:ok, + %{ + extra_arguments: list, + intensity: non_neg_integer, + max_children: :infinity | non_neg_integer, + period: pos_integer, + strategy: :one_for_one + }} + def init(_), do: DynamicSupervisor.init(strategy: :one_for_one) + + def foo({aid, scid}) when is_binary(aid) and is_binary(scid), + do: GenServer.call(worker_pid({aid, scid}), :FOO) + + defp worker_pid({aid, scid}) when is_binary(aid) and is_binary(scid), + do: + (case(maybe_worker({aid, scid})) do + [{pid, _}] -> + pid + + [] -> + {:ok, pid} = start_worker({aid, scid}) + pid + end) + + defp maybe_worker({aid, scid}) when is_binary(aid) and is_binary(scid), + do: Registry.lookup(Ckd.Application.m_worker_registry(), {aid, scid}) + + defp start_worker({aid, scid}) when is_binary(aid) and is_binary(scid), + do: DynamicSupervisor.start_child(__MODULE__, {MWorker, {aid, scid}}) +end diff --git a/lib/ckd/services/m/m_worker.ex b/lib/ckd/services/m/m_worker.ex new file mode 100644 index 0000000..4957d78 --- /dev/null +++ b/lib/ckd/services/m/m_worker.ex @@ -0,0 +1,48 @@ +defmodule Ckd.MWorker do + @moduledoc false + + use GenServer + require Logger + + @registry Ckd.Application.m_worker_registry() + # @block_time 18_000 + # @fetch_interval 400 + # @dero_divisor 100_000 + + def start_link({aid, scid}) when is_binary(aid) and is_binary(scid), + do: GenServer.start_link(__MODULE__, {aid, scid}, name: via_tuple({aid, scid})) + + def init({aid, scid}) when is_binary(aid) and is_binary(scid) do + {:ok, _existing_account = %{}} = Ckd.State.subscribe_info({:account, aid}) + {:ok, _existing_account = %{}} = Ckd.DeroWallet.subscribe_info({:dero_wallet, aid}) + {:ok, %{aid: aid, scid: scid, dero: nil, height: -1}} + end + + def handle_info({{:account, _aid}, nil}, _state), + do: Process.exit(self(), :normal) + + def handle_info( + {{:account, aid}, %{dero: same_dero}}, + state = %{aid: aid, dero: same_dero} + ), + do: {:no_reply, state} + + def handle_info( + {{:account, aid}, %{dero: new_dero}}, + state = %{aid: aid} + ) do + # send(self(), {:fetch_info, dero: new_dero}) + {:noreply, Map.put(state, :dero, new_dero)} + end + + def handle_info({{:dero_wallet, aid}, %{height: height}}, state) do + {:noreply, Map.put(state, :height, height)} + end + + def handle_call(:FOO, _from, state) do + {:reply, {:ok, state}, state} + end + + defp via_tuple({aid, scid}) when is_binary(aid) and is_binary(scid), + do: {:via, Registry, {@registry, {aid, scid}}} +end diff --git a/lib/ckd_web/channels/socket.ex b/lib/ckd_web/channels/socket.ex index 2aef29b..a03701e 100644 --- a/lib/ckd_web/channels/socket.ex +++ b/lib/ckd_web/channels/socket.ex @@ -8,7 +8,7 @@ defmodule CkdWeb.Socket do channel "v0:signIn", V0.SignInChannel channel "v0:account", V0.AccountChannel channel "v0:dero", V0.DeroChannel - # channel "v0:m:*", V0.MChannel + channel "v0:m:*", V0.MChannel # Socket params are passed from the client and can # be used to verify and authenticate a user. After diff --git a/lib/ckd_web/channels/v0/m_channel.ex b/lib/ckd_web/channels/v0/m_channel.ex new file mode 100644 index 0000000..74c2bd2 --- /dev/null +++ b/lib/ckd_web/channels/v0/m_channel.ex @@ -0,0 +1,7 @@ +defmodule CkdWeb.Channels.V0.MChannel do + @moduledoc false + + use Phoenix.Channel + + @api "v0:m:" +end diff --git a/lib/ckd_web/live/home_live.html.leex b/lib/ckd_web/live/home_live.html.leex index 87cdec2..b938968 100644 --- a/lib/ckd_web/live/home_live.html.leex +++ b/lib/ckd_web/live/home_live.html.leex @@ -113,6 +113,17 @@
<%= account.display_name %>