Skip to content

Commit

Permalink
chore: add aex9 validation to v1 range endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
jyeshe committed Jul 11, 2022
1 parent 62e7c75 commit 87a8a14
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 59 deletions.
93 changes: 59 additions & 34 deletions lib/ae_mdw_web/controllers/aex9_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,19 @@ defmodule AeMdwWeb.Aex9Controller do
alias AeMdw.Node.Db, as: DBN
alias AeMdw.Validate

alias AeMdwWeb.DataStreamPlug, as: DSPlug
alias AeMdwWeb.FallbackController
alias AeMdwWeb.Plugs.PaginatedPlug

alias Plug.Conn

import AeMdwWeb.Util, only: [handle_input: 2, paginate: 4, presence?: 2]
import AeMdwWeb.Util,
only: [
handle_input: 2,
paginate: 4,
parse_range: 1,
presence?: 2
]

import AeMdwWeb.Helpers.AexnHelper
import AeMdwWeb.AexnView

Expand Down Expand Up @@ -60,23 +66,28 @@ defmodule AeMdwWeb.Aex9Controller do
)

@spec balance_range(Plug.Conn.t(), map()) :: Plug.Conn.t()
def balance_range(conn, %{
def balance_range(%Conn{assigns: %{state: state}} = conn, %{
"range" => range,
"contract_id" => contract_id,
"account_id" => account_id
}),
do:
handle_input(
conn,
fn ->
balance_range_reply(
conn,
ensure_aex9_contract_pk!(contract_id),
Validate.id!(account_id, [:account_pubkey]),
parse_range!(range)
)
end
)
}) do
with {:ok, first..last} <- validate_range(range),
{:ok, contract_pk} <-
ensure_aex9_contract_at_block(state, contract_id, {min(first, last), -1}),
{:ok, account_pk} <- Validate.id(account_id, [:account_pubkey]) do
handle_input(
conn,
fn ->
balance_range_reply(
conn,
contract_pk,
account_pk,
first..last
)
end
)
end
end

@spec balance_for_hash(Plug.Conn.t(), map()) :: Plug.Conn.t()
def balance_for_hash(%Conn{assigns: %{state: state}} = conn, %{
Expand Down Expand Up @@ -154,18 +165,21 @@ defmodule AeMdwWeb.Aex9Controller do
do: handle_input(conn, fn -> balances_reply(conn, ensure_aex9_contract_pk!(contract_id)) end)

@spec balances_range(Plug.Conn.t(), map()) :: Plug.Conn.t()
def balances_range(conn, %{"range" => range, "contract_id" => contract_id}),
do:
def balances_range(%Conn{assigns: %{state: state}} = conn, %{
"range" => range,
"contract_id" => contract_id
}) do
with {:ok, first..last} <- validate_range(range),
{:ok, contract_pk} <-
ensure_aex9_contract_at_block(state, contract_id, {min(first, last), -1}) do
handle_input(
conn,
fn ->
balances_range_reply(
conn,
ensure_aex9_contract_pk!(contract_id),
parse_range!(range)
)
balances_range_reply(conn, contract_pk, first..last)
end
)
end
end

@spec balances_for_hash(Plug.Conn.t(), map()) :: Plug.Conn.t()
def balances_for_hash(%Conn{assigns: %{state: state}} = conn, %{
Expand Down Expand Up @@ -408,21 +422,22 @@ defmodule AeMdwWeb.Aex9Controller do
json(conn, balances_to_map({amounts, height_hash}, contract_pk))
end

defp parse_range!(range) do
case DSPlug.parse_range(range) do
defp validate_range(range) do
case parse_range(range) do
{:ok, %Range{first: f, last: l}} ->
{:ok, top_kb} = :aec_chain.top_key_block()
first = max(0, f)
last = min(l, :aec_blocks.height(top_kb))

if last - first + 1 > @max_range_length do
raise ErrInput.RangeTooBig, value: "max range length is #{@max_range_length}"
{:error,
ErrInput.RangeTooBig.exception(value: "max range length is #{@max_range_length}")}
end

first..last
{:ok, first..last}

{:error, _detail} ->
raise ErrInput.NotAex9, value: range
{:error, ErrInput.NotAex9.exception(value: range)}
end
end

Expand All @@ -432,23 +447,33 @@ defmodule AeMdwWeb.Aex9Controller do
pk
end

defp ensure_aex9_contract_at_block(state, ct_id, block_hash) do
ct_pk = Validate.id!(ct_id, [:contract_pubkey])
defp ensure_aex9_contract_at_block(state, ct_id, block_hash) when is_binary(block_hash) do
case Util.block_hash_to_bi(state, block_hash) do
nil ->
{:error, ErrInput.NotFound.exception(value: block_hash)}

block_index ->
ensure_aex9_contract_at_block(state, ct_id, block_index)
end
end

with bi when bi != nil <- Util.block_hash_to_bi(state, block_hash),
defp ensure_aex9_contract_at_block(state, ct_id, block_index) do
with {:ok, ct_pk} <- Validate.id(ct_id, [:contract_pubkey]),
{:ok, Model.aexn_contract(txi: txi)} <-
State.get(state, Model.AexnContract, {:aex9, ct_pk}) do
if txi < Util.block_txi(state, bi) do
if txi < Util.block_txi(state, block_index) do
{:ok, ct_pk}
else
{:error, ErrInput.NotFound.exception(value: ct_id)}
end
else
nil ->
{:error, ErrInput.NotFound.exception(value: block_hash)}
{:error, {ErrInput.Id, id}} ->
{:error, ErrInput.Id.exception(value: id)}

:not_found ->
# if not yet synced by Mdw but present on Node
ct_pk = Validate.id!(ct_id)

if AexnContracts.is_aex9?(ct_pk) do
{:ok, ct_pk}
else
Expand Down
24 changes: 0 additions & 24 deletions lib/ae_mdw_web/data_stream_plug.ex

This file was deleted.

22 changes: 22 additions & 0 deletions lib/ae_mdw_web/util.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ defmodule AeMdwWeb.Util do
"""

alias AeMdw.Collection
alias AeMdw.Validate
alias AeMdw.Error.Input, as: ErrInput
alias Phoenix.Controller
alias Plug.Conn
Expand All @@ -23,6 +24,27 @@ defmodule AeMdwWeb.Util do
end
end

@spec parse_range(binary()) :: {:ok, Range.t()} | {:error, binary}
def parse_range(range) do
case String.split(range, "-") do
[from, to] ->
case {Validate.nonneg_int(from), Validate.nonneg_int(to)} do
{{:ok, from}, {:ok, to}} -> {:ok, from..to}
{{:ok, _}, {:error, {_, detail}}} -> {:error, detail}
{{:error, {_, detail}}, _} -> {:error, detail}
end

[x] ->
case Validate.nonneg_int(x) do
{:ok, x} -> {:ok, x..x}
{:error, {_, detail}} -> {:error, detail}
end

_invalid_range ->
{:error, range}
end
end

# credo:disable-for-next-line
def query_groups(query_string) do
query_string
Expand Down
38 changes: 37 additions & 1 deletion test/integration/ae_mdw_web/controllers/aex9_controller_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,25 @@ defmodule Integration.AeMdwWeb.Aex9ControllerTest do
assert String.starts_with?(hash, "kh_") and match?({:ok, _hash_bin}, Validate.id(hash))
end)
end

test "returns 404 if contract had not been created up to the block", %{conn: conn} do
contract_id = "ct_2t7TnocFw7oCYSS7g2yGutZMpGEJta6dq2DTX38SmuqmwtN6Ch"
account_id = "ak_psy8tRXPzGxh6975H7K6XQcMFVsdrxJMt7YkzMY8oUTevutzw"
first = 487_100
last = 487_101

path =
Routes.aex9_path(
conn,
:balance_range,
"#{first}-#{last}",
contract_id,
account_id
)

assert %{"error" => error} = conn |> get(path) |> json_response(404)
assert error == "not found: #{contract_id}"
end
end

describe "balances_range" do
Expand Down Expand Up @@ -228,6 +247,23 @@ defmodule Integration.AeMdwWeb.Aex9ControllerTest do
end)
end

test "returns 404 if contract had not been created up to the block", %{conn: conn} do
contract_id = "ct_2t7TnocFw7oCYSS7g2yGutZMpGEJta6dq2DTX38SmuqmwtN6Ch"
first = 487_100
last = 487_101

path =
Routes.aex9_path(
conn,
:balances_range,
"#{first}-#{last}",
contract_id
)

assert %{"error" => error} = conn |> get(path) |> json_response(404)
assert error == "not found: #{contract_id}"
end

@tag :iteration
test "gets balances on each contract for a range of generations", %{conn: conn} do
first = 500_001
Expand Down Expand Up @@ -325,7 +361,7 @@ defmodule Integration.AeMdwWeb.Aex9ControllerTest do
} = json_response(conn, 200)
end

test "returns 404 when contract had not been created up to the block", %{conn: conn} do
test "returns 404 if contract had not been created up to the block", %{conn: conn} do
hash = "kh_NM2cxdzg6mf4KMFMXw1kAzBJGwFoqiGHQtaKx3DvaAGM5CAkn"
contract_id = "ct_RDRJC5EySx4TcLtGRWYrXfNgyWzEDzssThJYPd9kdLeS5ECaA"
conn = get(conn, "/aex9/balances/hash/#{hash}/#{contract_id}")
Expand Down

0 comments on commit 87a8a14

Please sign in to comment.