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

feat: query channel reserve at a hash #1106

Merged
merged 2 commits into from Jan 4, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
95 changes: 68 additions & 27 deletions lib/ae_mdw/channels.ex
Expand Up @@ -7,8 +7,10 @@ defmodule AeMdw.Channels do
alias AeMdw.Collection
alias AeMdw.Db.Model
alias AeMdw.Db.State
alias AeMdw.Db.Util, as: DbUtil
alias AeMdw.Error
alias AeMdw.Error.Input, as: ErrInput
alias AeMdw.Node.Db
alias AeMdw.Txs
alias AeMdw.Util
alias AeMdw.Validate
Expand All @@ -18,7 +20,8 @@ defmodule AeMdw.Channels do
require Model

@typep state() :: State.t()
@typep pubkey() :: AeMdw.Node.Db.pubkey()
@typep pubkey() :: Db.pubkey()
@typep type_block_hash() :: {Db.hash_type(), Db.hash()}
@typep pagination() :: Collection.direction_limit()
@typep range() :: {:gen, Range.t()} | nil
@typep cursor() :: Collection.pagination_cursor()
Expand Down Expand Up @@ -63,15 +66,13 @@ defmodule AeMdw.Channels do
type_count(state, :channel_settle_tx, from_txi, next_txi)
end

@spec fetch_channel(state(), pubkey()) :: {:ok, channel()} | {:error, Error.t()}
def fetch_channel(state, channel_pk) do
@spec fetch_channel(state(), pubkey(), type_block_hash() | nil) ::
{:ok, channel()} | {:error, Error.t()}
def fetch_channel(state, channel_pk, type_block_hash \\ nil) do
case locate(state, channel_pk) do
{:ok, channel, source} ->
{:ok,
render_channel(state, channel,
is_active?: source == Model.ActiveChannel,
node_details?: true
)}
{:ok, m_channel, source} ->
is_active? = source == Model.ActiveChannel
{:ok, render_channel(state, m_channel, is_active?, type_block_hash)}

:not_found ->
{:error, ErrInput.NotFound.exception(value: encode(:channel, channel_pk))}
Expand Down Expand Up @@ -103,10 +104,8 @@ defmodule AeMdw.Channels do

defp render_active_channels(state, keys) do
Enum.map(keys, fn {_exp, channel_pk} ->
render_channel(state, State.fetch!(state, @table_active, channel_pk),
is_active?: true,
node_details?: false
)
m_channel = State.fetch!(state, @table_active, channel_pk)
render_channel(state, m_channel, true)
end)
end

Expand All @@ -118,12 +117,14 @@ defmodule AeMdw.Channels do
responder: responder_pk,
state_hash: state_hash,
amount: amount,
updates: [{{last_updated_height, _mbi}, last_updated_txi} | _rest] = updates
updates:
[{{last_updated_height, _mbi} = update_block_index, last_updated_txi} | _rest] =
updates
),
is_active?: is_active?,
node_details?: node_details?
is_active?,
type_block_hash \\ nil
) do
%{"block_hash" => block_hash, "hash" => tx_hash, "tx" => %{"type" => tx_type}} =
%{"block_hash" => update_block_hash, "hash" => tx_hash, "tx" => %{"type" => tx_type}} =
Txs.fetch!(state, last_updated_txi)

channel = %{
Expand All @@ -138,17 +139,24 @@ defmodule AeMdw.Channels do
active: is_active?
}

with true <- node_details?,
{:ok, node_channel} <-
:aec_chain.get_channel_at_hash(channel_pk, Validate.id!(block_hash)) do
node_details = :aesc_channels.serialize_for_client(node_channel)
block_hash =
get_oldest_block_hash(
state,
type_block_hash,
Validate.id!(update_block_hash),
update_block_index
)

case :aec_chain.get_channel_at_hash(channel_pk, block_hash) do
{:ok, node_channel} ->
node_details = :aesc_channels.serialize_for_client(node_channel)

channel
|> Map.merge(node_details)
|> Map.drop(~w(id initiator_id responder_id channel_amount))
|> Map.put(:amount, node_details["channel_amount"])
else
_no_details ->
channel
|> Map.merge(node_details)
|> Map.drop(~w(id initiator_id responder_id channel_amount))
|> Map.put(:amount, node_details["channel_amount"])

{:error, _reason} ->
Map.put(channel, :amount, amount)
end
end
Expand All @@ -175,4 +183,37 @@ defmodule AeMdw.Channels do
end

defp deserialize_scope(_nil_or_txis_scope), do: nil

# Gets from the oldest block state tree since some channels might be absent from newer blocks
defp get_oldest_block_hash(_state, nil, update_block_hash, _update_block_index),
do: update_block_hash

defp get_oldest_block_hash(
state,
{block_type, block_hash},
update_block_hash,
update_block_index
) do
case get_block_index(state, block_type, block_hash) do
{:ok, height, mbi} ->
if update_block_index < {height, mbi} do
update_block_hash
else
block_hash
end

{:error, _reason} ->
update_block_hash
end
end

defp get_block_index(state, :key, block_hash) do
with {:ok, height} <- DbUtil.key_block_height(state, block_hash) do
{:ok, height, -1}
end
end

defp get_block_index(state, :micro, block_hash) do
DbUtil.micro_block_height_index(state, block_hash)
end
end
1 change: 1 addition & 0 deletions lib/ae_mdw/node/db.ex
Expand Up @@ -14,6 +14,7 @@ defmodule AeMdw.Node.Db do

@type pubkey() :: <<_::256>>
@type hash_type() :: nil | :key | :micro
@type hash() :: <<_::256>>
@type height_hash() :: {hash_type(), pos_integer(), binary()}
@type balances_map() :: %{{:address, pubkey()} => integer()}
@type account_balance() :: {integer() | nil, height_hash()}
Expand Down
19 changes: 17 additions & 2 deletions lib/ae_mdw_web/controllers/channel_controller.ex
Expand Up @@ -22,10 +22,25 @@ defmodule AeMdwWeb.ChannelController do
end

@spec channel(Conn.t(), map()) :: Conn.t()
def channel(%Conn{assigns: %{state: state}} = conn, %{"id" => id}) do
def channel(%Conn{assigns: %{state: state}} = conn, %{"id" => id} = params) do
block_hash = params["block_hash"]

with {:ok, channel_pk} <- Validate.id(id, [:channel]),
{:ok, channel} <- Channels.fetch_channel(state, channel_pk) do
{:ok, type_block_hash} <- valid_optional_block_hash?(block_hash),
{:ok, channel} <- Channels.fetch_channel(state, channel_pk, type_block_hash) do
json(conn, channel)
end
end

defp valid_optional_block_hash?(nil), do: {:ok, nil}

defp valid_optional_block_hash?(block_hash) do
with {:ok, hash} <- Validate.id(block_hash) do
if String.starts_with?(block_hash, "kh") do
{:ok, {:key, hash}}
else
{:ok, {:micro, hash}}
end
end
end
end