Skip to content

Commit

Permalink
feat: sort active names by activation height (#760)
Browse files Browse the repository at this point in the history
  • Loading branch information
jyeshe committed Jul 1, 2022
1 parent e172833 commit a57cf3c
Show file tree
Hide file tree
Showing 9 changed files with 156 additions and 38 deletions.
12 changes: 11 additions & 1 deletion lib/ae_mdw/db/model.ex
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,14 @@ defmodule AeMdw.Db.Model do
bids: [Blocks.block_index_txi()]
)

# activation:
# index = {height, plain_name}, value: any
@activation_defaults [index: {nil, nil}, value: nil]
defrecord :activation, @activation_defaults

@type activation ::
record(:activation, index: {Blocks.height(), String.t()}, value: nil)

# in 3 tables: auction_expiration, name_expiration, inactive_name_expiration
#
# expiration:
Expand All @@ -143,7 +151,7 @@ defmodule AeMdw.Db.Model do
defrecord :expiration, @expiration_defaults

@type expiration ::
record(:expiration, index: {pos_integer(), String.t() | pubkey()}, value: nil)
record(:expiration, index: {Blocks.height(), String.t() | pubkey()}, value: nil)

# in 2 tables: active_name, inactive_name
#
Expand Down Expand Up @@ -682,6 +690,7 @@ defmodule AeMdw.Db.Model do
AeMdw.Db.Model.AuctionBid,
AeMdw.Db.Model.Pointee,
AeMdw.Db.Model.AuctionExpiration,
AeMdw.Db.Model.ActiveNameActivation,
AeMdw.Db.Model.ActiveNameExpiration,
AeMdw.Db.Model.InactiveNameExpiration,
AeMdw.Db.Model.ActiveName,
Expand Down Expand Up @@ -761,6 +770,7 @@ defmodule AeMdw.Db.Model do
def record(AeMdw.Db.Model.AuctionBid), do: :auction_bid
def record(AeMdw.Db.Model.Pointee), do: :pointee
def record(AeMdw.Db.Model.AuctionExpiration), do: :expiration
def record(AeMdw.Db.Model.ActiveNameActivation), do: :activation
def record(AeMdw.Db.Model.ActiveNameExpiration), do: :expiration
def record(AeMdw.Db.Model.InactiveNameExpiration), do: :expiration
def record(AeMdw.Db.Model.ActiveName), do: :name
Expand Down
2 changes: 2 additions & 0 deletions lib/ae_mdw/db/mutations/name_claim_mutation.ex
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,14 @@ defmodule AeMdw.Db.NameClaimMutation do
previous: previous
)

m_name_activation = Model.activation(index: {height, plain_name})
m_name_exp = Model.expiration(index: {expire, plain_name})
lock_amount = (is_lima? && name_fee) || :aec_governance.name_claim_locked_fee()

state2
|> cache_through_write(Model.ActiveName, m_name)
|> cache_through_write(Model.ActiveNameOwner, m_owner)
|> cache_through_write(Model.ActiveNameActivation, m_name_activation)
|> cache_through_write(Model.ActiveNameExpiration, m_name_exp)
|> cache_through_delete_inactive(previous)
|> IntTransfer.fee({height, txi}, :lock_name, owner_pk, txi, lock_amount)
Expand Down
7 changes: 6 additions & 1 deletion lib/ae_mdw/db/name.ex
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ defmodule AeMdw.Db.Name do
@typep table ::
Model.ActiveName
| Model.InactiveName
| Model.ActiveNameActivation
| Model.ActiveNameExpiration
| Model.InactiveNameExpiration
| Model.AuctionExpiration
Expand All @@ -51,6 +52,7 @@ defmodule AeMdw.Db.Name do
| Model.Pointee
@typep name_record() ::
Model.name()
| Model.activation()
| Model.expiration()
| Model.plain_name()
| Model.owner()
Expand Down Expand Up @@ -128,13 +130,15 @@ defmodule AeMdw.Db.Name do
previous: previous
)

m_name_activation = Model.activation(index: {height, plain_name})
m_name_exp = Model.expiration(index: {expire, plain_name})
m_owner = Model.owner(index: {owner, plain_name})
%{tx: winning_tx} = read_raw_tx!(state, txi)

state
|> cache_through_write(Model.ActiveName, m_name)
|> cache_through_write(Model.ActiveNameOwner, m_owner)
|> cache_through_write(Model.ActiveNameActivation, m_name_activation)
|> cache_through_write(Model.ActiveNameExpiration, m_name_exp)
|> cache_through_delete(Model.AuctionExpiration, {height, plain_name})
|> cache_through_delete(Model.AuctionOwner, {owner, plain_name})
Expand Down Expand Up @@ -439,11 +443,12 @@ defmodule AeMdw.Db.Name do
defp cache_through_delete_active(
state,
expiration,
Model.name(index: plain_name, owner: owner_pk)
Model.name(index: plain_name, active: active_from, owner: owner_pk)
) do
state
|> cache_through_delete(Model.ActiveName, plain_name)
|> cache_through_delete(Model.ActiveNameOwner, {owner_pk, plain_name})
|> cache_through_delete(Model.ActiveNameActivation, {active_from, plain_name})
|> cache_through_delete(Model.ActiveNameExpiration, {expiration, plain_name})
end

Expand Down
53 changes: 34 additions & 19 deletions lib/ae_mdw/names.ex
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,14 @@ defmodule AeMdw.Names do
@typep prefix() :: plain_name()

@table_active Model.ActiveName
@table_activation Model.ActiveNameActivation
@table_active_expiration Model.ActiveNameExpiration
@table_active_owner Model.ActiveNameOwner
@table_inactive Model.InactiveName
@table_inactive_expiration Model.InactiveNameExpiration
@table_inactive_owner Model.InactiveNameowner

@pagination_params ~w(limit cursor rev direction by scope)
@pagination_params ~w(limit cursor rev direction scope)
@states ~w(active inactive)
@all_lifecycles ~w(active inactive auction)a

Expand All @@ -59,20 +60,20 @@ defmodule AeMdw.Names do
) ::
{:ok, cursor() | nil, [name()], cursor() | nil} | {:error, reason()}
def fetch_names(state, pagination, range, order_by, query, cursor, expand?)
when order_by in [:expiration, :deactivation] do
cursor = deserialize_expiration_cursor(cursor)
when order_by in [:activation, :expiration, :deactivation] do
cursor = deserialize_height_cursor(cursor)
scope = deserialize_scope(range)

try do
{prev_cursor, expiration_keys, next_cursor} =
{prev_cursor, height_keys, next_cursor} =
query
|> Map.drop(@pagination_params)
|> Enum.into(%{}, &convert_param/1)
|> build_expiration_streamer(state, scope, cursor)
|> build_height_streamer(state, scope, cursor)
|> Collection.paginate(pagination)

{:ok, serialize_expiration_cursor(prev_cursor),
render_exp_list(state, expiration_keys, expand?), serialize_expiration_cursor(next_cursor)}
{:ok, serialize_height_cursor(prev_cursor), render_height_list(state, height_keys, expand?),
serialize_height_cursor(next_cursor)}
rescue
e in ErrInput ->
{:error, e.message}
Expand Down Expand Up @@ -161,27 +162,39 @@ defmodule AeMdw.Names do
end
end

defp build_expiration_streamer(%{owned_by: _owner_pk}, _state, _scope, _cursor) do
defp build_height_streamer(%{owned_by: _owner_pk}, _state, _scope, _cursor) do
raise(ErrInput.Query, value: "can't order by expiration when filtering by owner")
end

defp build_expiration_streamer(%{state: "active"}, state, scope, cursor) do
defp build_height_streamer(%{state: "active", by: "activation"}, state, scope, cursor) do
fn direction ->
state
|> Collection.stream(@table_activation, direction, scope, cursor)
|> Stream.map(&{&1, :active})
end
end

defp build_height_streamer(%{state: "active"}, state, scope, cursor) do
fn direction ->
state
|> Collection.stream(@table_active_expiration, direction, scope, cursor)
|> Stream.map(&{&1, :active})
end
end

defp build_expiration_streamer(%{state: "inactive"}, state, scope, cursor) do
defp build_height_streamer(%{state: "inactive"}, state, scope, cursor) do
fn direction ->
state
|> Collection.stream(@table_inactive_expiration, direction, scope, cursor)
|> Stream.map(&{&1, :inactive})
end
end

defp build_expiration_streamer(_query, state, scope, cursor) do
defp build_height_streamer(%{by: "activation"}, _state, _scope, _cursor) do
raise(ErrInput.Query, value: "can only order by activation when filtering active names")
end

defp build_height_streamer(_query, state, scope, cursor) do
fn direction ->
active_stream =
state
Expand Down Expand Up @@ -280,7 +293,7 @@ defmodule AeMdw.Names do
end
end

defp render_exp_list(state, names_tables_keys, expand?) do
defp render_height_list(state, names_tables_keys, expand?) do
Enum.map(names_tables_keys, fn {{_exp, plain_name}, source} ->
render(state, plain_name, source == :active, expand?)
end)
Expand Down Expand Up @@ -397,17 +410,17 @@ defmodule AeMdw.Names do
end
end

defp serialize_expiration_cursor(nil), do: nil
defp serialize_height_cursor(nil), do: nil

defp serialize_expiration_cursor({{{exp_height, name}, _tab}, is_reversed?}),
do: serialize_expiration_cursor({{exp_height, name}, is_reversed?})
defp serialize_height_cursor({{{height, name}, _tab}, is_reversed?}),
do: serialize_height_cursor({{height, name}, is_reversed?})

defp serialize_expiration_cursor({{exp_height, name}, is_reversed?}),
do: {"#{exp_height}-#{name}", is_reversed?}
defp serialize_height_cursor({{height, name}, is_reversed?}),
do: {"#{height}-#{name}", is_reversed?}

defp deserialize_expiration_cursor(nil), do: nil
defp deserialize_height_cursor(nil), do: nil

defp deserialize_expiration_cursor(cursor_bin) do
defp deserialize_height_cursor(cursor_bin) do
case Regex.run(~r/\A(\d+)-([\w\.]+)\z/, cursor_bin) do
[_match0, exp_height, name] -> {String.to_integer(exp_height), name}
nil -> nil
Expand Down Expand Up @@ -437,6 +450,8 @@ defmodule AeMdw.Names do

defp convert_param({"state", state}) when state in @states, do: {:state, state}

defp convert_param({"by", by}), do: {:by, by}

defp convert_param(other_param),
do: raise(ErrInput.Query, value: other_param)
end
2 changes: 1 addition & 1 deletion lib/ae_mdw_web/controllers/name_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ defmodule AeMdwWeb.NameController do
import AeMdw.Util

plug PaginatedPlug,
[order_by: ~w(expiration deactivation name)a]
[order_by: ~w(expiration activation deactivation name)a]
when action in ~w(active_names inactive_names names auctions search search_v1)a

@lifecycles_map %{
Expand Down
10 changes: 7 additions & 3 deletions test/ae_mdw/db/name_claim_mutation_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ defmodule AeMdw.Db.NameClaimMutationTest do

alias AeMdw.Database
alias AeMdw.Db.Model
alias AeMdw.Db.State
alias AeMdw.Db.Sync

import Mock
Expand Down Expand Up @@ -62,6 +63,8 @@ defmodule AeMdw.Db.NameClaimMutationTest do
|> Database.commit()
end

state = State.new()

assert {:ok,
Model.name(
index: ^plain_name,
Expand All @@ -70,9 +73,10 @@ defmodule AeMdw.Db.NameClaimMutationTest do
expire: expire,
owner: ^new_owner_pk,
updates: []
)} = Database.fetch(Model.ActiveName, plain_name)
)} = State.get(state, Model.ActiveName, plain_name)

assert Database.exists?(Model.ActiveNameExpiration, {expire, plain_name})
assert Database.exists?(Model.ActiveNameOwner, {new_owner_pk, plain_name})
assert State.exists?(state, Model.ActiveNameActivation, {claim_height, plain_name})
assert State.exists?(state, Model.ActiveNameExpiration, {expire, plain_name})
assert State.exists?(state, Model.ActiveNameOwner, {new_owner_pk, plain_name})
end
end
27 changes: 21 additions & 6 deletions test/ae_mdw/db/name_revoke_mutation_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ defmodule AeMdw.Db.NameRevokeMutationTest do

alias AeMdw.Database
alias AeMdw.Db.Model
alias AeMdw.Db.State
alias AeMdw.Db.NameRevokeMutation

require Model
Expand All @@ -20,15 +21,16 @@ defmodule AeMdw.Db.NameRevokeMutationTest do
revoke_block_index = {revoke_height, 0}
revoke_txi = 124

active_from = 11
expire = 100
owner_pk = <<538_053::256>>

active_name =
Model.name(
index: plain_name,
active: 1,
active: active_from,
expire: expire,
claims: [{{1, 0}, 123}],
claims: [{{active_from, 0}, 123}],
updates: [],
transfers: [],
revoke: nil,
Expand All @@ -39,24 +41,37 @@ defmodule AeMdw.Db.NameRevokeMutationTest do
Database.dirty_write(Model.PlainName, Model.plain_name(index: name_hash, value: plain_name))
Database.dirty_write(Model.ActiveName, active_name)

Database.dirty_write(
Model.ActiveNameActivation,
Model.activation(index: {active_from, plain_name})
)

Database.dirty_write(
Model.ActiveNameExpiration,
Model.expiration(index: {expire, plain_name})
)

Database.dirty_write(Model.ActiveNameOwner, Model.owner(index: {owner_pk, plain_name}))

Database.commit([NameRevokeMutation.new(name_hash, revoke_txi, revoke_block_index)])
state2 =
State.commit_mem(State.new(), [
NameRevokeMutation.new(name_hash, revoke_txi, revoke_block_index)
])

refute State.exists?(state2, Model.ActiveName, plain_name)
refute State.exists?(state2, Model.ActiveNameOwner, {owner_pk, plain_name})
refute State.exists?(state2, Model.ActiveNameActivation, {active_from, plain_name})
refute State.exists?(state2, Model.ActiveNameExpiration, {expire, plain_name})

assert {:ok,
Model.name(
index: ^plain_name,
expire: ^expire,
owner: ^owner_pk,
revoke: {^revoke_block_index, ^revoke_txi}
)} = Database.fetch(Model.InactiveName, plain_name)
)} = State.get(state2, Model.InactiveName, plain_name)

assert Database.exists?(Model.InactiveNameExpiration, {revoke_height, plain_name})
assert Database.exists?(Model.InactiveNameOwner, {owner_pk, plain_name})
assert State.exists?(state2, Model.InactiveNameExpiration, {revoke_height, plain_name})
assert State.exists?(state2, Model.InactiveNameOwner, {owner_pk, plain_name})
end
end
18 changes: 12 additions & 6 deletions test/ae_mdw/db/name_update_mutation_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ defmodule AeMdw.Db.NameUpdateMutationTest do

alias AeMdw.Database
alias AeMdw.Db.Model
alias AeMdw.Db.State
alias AeMdw.Db.NameUpdateMutation

require Model
Expand All @@ -15,15 +16,16 @@ defmodule AeMdw.Db.NameUpdateMutationTest do
plain_name = "update-tll-0.test"
tx = new_aens_update_tx(owner_pk, plain_name, 0)

active_from = 9
update_height = 10
expire = update_height + 100

active_name =
Model.name(
index: plain_name,
active: 1,
active: active_from,
expire: expire,
claims: [{{1, 0}, 123}],
claims: [{{active_from, 0}, 123}],
updates: [],
transfers: [],
revoke: nil,
Expand All @@ -47,7 +49,7 @@ defmodule AeMdw.Db.NameUpdateMutationTest do

block_index = {update_height, 0}
txi = 124
Database.commit([NameUpdateMutation.new(tx, txi, block_index)])
state2 = State.commit_mem(State.new(), [NameUpdateMutation.new(tx, txi, block_index)])

assert {:ok,
Model.name(
Expand All @@ -56,10 +58,14 @@ defmodule AeMdw.Db.NameUpdateMutationTest do
owner: ^owner_pk,
updates: [{^block_index, ^txi}],
revoke: nil
)} = Database.fetch(Model.InactiveName, plain_name)
)} = State.get(state2, Model.InactiveName, plain_name)

assert Database.exists?(Model.InactiveNameExpiration, {update_height, plain_name})
assert Database.exists?(Model.InactiveNameOwner, {owner_pk, plain_name})
refute State.exists?(state2, Model.ActiveName, plain_name)
refute State.exists?(state2, Model.ActiveNameOwner, {owner_pk, plain_name})
refute State.exists?(state2, Model.ActiveNameActivation, {active_from, plain_name})
refute State.exists?(state2, Model.ActiveNameExpiration, {expire, plain_name})
assert State.exists?(state2, Model.InactiveNameExpiration, {update_height, plain_name})
assert State.exists?(state2, Model.InactiveNameOwner, {owner_pk, plain_name})
end
end
end
Loading

0 comments on commit a57cf3c

Please sign in to comment.