Skip to content

Commit

Permalink
feat: index nft transfers by collection (#887)
Browse files Browse the repository at this point in the history
  • Loading branch information
jyeshe committed Sep 1, 2022
1 parent 89bf5d2 commit 322dac0
Show file tree
Hide file tree
Showing 9 changed files with 368 additions and 104 deletions.
11 changes: 0 additions & 11 deletions lib/ae_mdw/aex9.ex
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,6 @@ defmodule AeMdw.Aex9 do

@typep txi :: AeMdw.Txs.txi()

@type account_transfer_key ::
{pubkey(), txi(), pubkey(), pos_integer(), non_neg_integer()}
@type pair_transfer_key ::
{pubkey(), pubkey(), txi(), pos_integer(), non_neg_integer()}

@type cursor :: binary()
@type account_paginated_transfers ::
{cursor() | nil, [account_transfer_key()], {cursor() | nil, boolean()}}
@type pair_paginated_transfers ::
{cursor() | nil, [pair_transfer_key()], {cursor() | nil, boolean()}}

@typep pagination :: Collection.direction_limit()
@typep pubkey :: AeMdw.Node.Db.pubkey()

Expand Down
101 changes: 80 additions & 21 deletions lib/ae_mdw/aexn_transfers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ defmodule AeMdw.AexnTransfers do
alias AeMdw.Db.Model
alias AeMdw.Db.State
alias AeMdw.Error.Input, as: ErrInput
alias AeMdw.Util

require Model

Expand All @@ -21,20 +22,67 @@ defmodule AeMdw.AexnTransfers do
{:aex9 | :aex141, pubkey(), txi(), pubkey(), pos_integer(), non_neg_integer()}
@type pair_transfer_key ::
{:aex9 | :aex141, pubkey(), pubkey(), txi(), pos_integer(), non_neg_integer()}
@type contract_transfer_key ::
{pubkey(), pubkey(), txi(), pubkey(), pos_integer(), non_neg_integer()}

@type cursor :: binary()
@typep page_cursor :: Collection.pagination_cursor()
@type account_paginated_transfers ::
{cursor() | nil, [transfer_key()], {cursor() | nil, boolean()}}
{page_cursor(), [transfer_key()], page_cursor()}
@type pair_paginated_transfers ::
{cursor() | nil, [pair_transfer_key()], {cursor() | nil, boolean()}}

{page_cursor(), [pair_transfer_key()], page_cursor()}
@type contract_paginated_transfers ::
{page_cursor(), [contract_transfer_key()], page_cursor()}
@typep pagination :: Collection.direction_limit()
@typep pubkey :: AeMdw.Node.Db.pubkey()

@spec fetch_contract_transfers(
State.t(),
AeMdw.Txs.txi(),
{:from | :to, pubkey() | nil},
pagination(),
cursor() | nil
) ::
contract_paginated_transfers()
def fetch_contract_transfers(state, contract_txi, {filter_by, account_pk}, pagination, cursor) do
table =
if filter_by == :from,
do: Model.AexnContractFromTransfer,
else: Model.AexnContractToTransfer

paginate_transfers(
state,
contract_txi,
pagination,
table,
cursor,
account_pk
)
end

@spec fetch_contract_recipient_transfers(
State.t(),
AeMdw.Txs.txi(),
pubkey() | nil,
pagination(),
cursor() | nil
) ::
contract_paginated_transfers()
def fetch_contract_recipient_transfers(state, contract_txi, recipient_pk, pagination, cursor) do
paginate_transfers(
state,
contract_txi,
pagination,
Model.AexnContractToTransfer,
cursor,
recipient_pk
)
end

@spec fetch_sender_transfers(State.t(), aexn_type(), pubkey(), pagination(), cursor() | nil) ::
account_paginated_transfers()
def fetch_sender_transfers(state, aexn_type, sender_pk, pagination, cursor) do
paginate_account_transfers(
paginate_transfers(
state,
aexn_type,
pagination,
Expand All @@ -47,7 +95,7 @@ defmodule AeMdw.AexnTransfers do
@spec fetch_recipient_transfers(State.t(), aexn_type(), pubkey(), pagination(), cursor() | nil) ::
account_paginated_transfers()
def fetch_recipient_transfers(state, aexn_type, recipient_pk, pagination, cursor) do
paginate_account_transfers(
paginate_transfers(
state,
aexn_type,
pagination,
Expand All @@ -74,50 +122,48 @@ defmodule AeMdw.AexnTransfers do
pagination,
cursor
) do
cursor_key = deserialize_cursor(cursor)

paginate_transfers(
state,
aexn_type,
pagination,
Model.AexnPairTransfer,
cursor_key,
cursor,
{sender_pk, recipient_pk}
)
end

#
# Private functions
#
defp paginate_account_transfers(
defp paginate_transfers(
state,
aexn_type,
pagination,
table,
cursor,
account_pk
account_pk_or_pair_pks
) do
cursor_key = deserialize_cursor(cursor)

paginate_transfers(
do_paginate_transfers(
state,
aexn_type,
pagination,
table,
cursor_key,
account_pk
account_pk_or_pair_pks
)
end

defp paginate_transfers(
defp do_paginate_transfers(
state,
aexn_type,
aexn_type_or_txi,
pagination,
table,
cursor_key,
params
) do
key_boundary = key_boundary(aexn_type, params)
key_boundary = key_boundary(aexn_type_or_txi, params)

{prev_cursor_key, transfer_keys, next_cursor_key} =
state
Expand Down Expand Up @@ -148,9 +194,15 @@ defmodule AeMdw.AexnTransfers do
with {:ok, cursor_bin} <- Base.decode64(cursor_bin64),
cursor_term <- :erlang.binary_to_term(cursor_bin),
true <-
elem(cursor_term, 0) in [:aex9, :aex141] and
(match?({_type, <<_pk1::256>>, _txi, <<_pk2::256>>, _amount, _idx}, cursor_term) or
match?({_type, <<_pk1::256>>, <<_pk2::256>>, _txi, _amount, _idx}, cursor_term)) do
(elem(cursor_term, 0) in [:aex9, :aex141] or is_integer(elem(cursor_term, 0))) and
(match?(
{_type_or_pk, <<_pk1::256>>, _txi, <<_pk2::256>>, _amount, _idx},
cursor_term
) or
match?(
{_type_or_pk, <<_pk1::256>>, <<_pk2::256>>, _txi, _amount, _idx},
cursor_term
)) do
cursor_term
else
_invalid ->
Expand All @@ -165,10 +217,17 @@ defmodule AeMdw.AexnTransfers do
}
end

defp key_boundary(aexn_type, account_pk) do
defp key_boundary(type_or_txi, nil) do
{
{type_or_txi, nil, 0, nil, 0, 0},
{type_or_txi, Util.max_256bit_bin(), nil, nil, 0, 0}
}
end

defp key_boundary(type_or_txi, account_pk) do
{
{aexn_type, account_pk, 0, nil, 0, 0},
{aexn_type, account_pk, nil, nil, 0, 0}
{type_or_txi, account_pk, 0, nil, 0, 0},
{type_or_txi, account_pk, nil, nil, 0, 0}
}
end
end
114 changes: 80 additions & 34 deletions lib/ae_mdw/db/contract.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ defmodule AeMdw.Db.Contract do
alias AeMdw.Contract
alias AeMdw.Db.Model
alias AeMdw.Db.Origin
alias AeMdw.Db.Sync
alias AeMdw.Db.State
alias AeMdw.Log
alias AeMdw.Node
Expand Down Expand Up @@ -146,7 +145,7 @@ defmodule AeMdw.Db.Contract do

raw_logs
|> Enum.with_index()
|> Enum.reduce(state, fn {{addr, [evt_hash | args], data}, i}, state ->
|> Enum.reduce(state, fn {{addr, [evt_hash | args], data}, i} = log, state ->
m_log =
Model.contract_log(
index: {create_txi, txi, evt_hash, i},
Expand All @@ -165,40 +164,19 @@ defmodule AeMdw.Db.Contract do
|> State.put(Model.DataContractLog, m_data_log)
|> State.put(Model.EvtContractLog, m_evt_log)
|> State.put(Model.IdxContractLog, m_idx_log)

# if remote call then indexes also with the called contract
state3 =
if addr != contract_pk do
{remote_called_contract_txi, state3} = Sync.Contract.get_txi!(state2, addr)

# on caller log: ext_contract = called contract_pk
# on called log: ext_contract = {:parent_contract_pk, caller contract_pk}
m_log_remote =
Model.contract_log(
index: {remote_called_contract_txi, txi, evt_hash, i},
ext_contract: {:parent_contract_pk, contract_pk},
args: args,
data: data
)

state3
|> State.put(Model.ContractLog, m_log_remote)
|> update_aex9_state(addr, block_index, txi)
else
state2
end
|> maybe_index_remote_log(contract_pk, block_index, txi, log)

aex9_contract_pk = which_aex9_contract_pubkey(contract_pk, addr)

cond do
is_aexn_transfer?(evt_hash) and aex9_contract_pk != nil ->
write_aexn_transfer(state3, :aex9, aex9_contract_pk, txi, i, args)
write_aexn_transfer(state2, :aex9, aex9_contract_pk, txi, i, args)

is_aexn_transfer?(evt_hash) and State.exists?(state3, Model.AexnContract, {:aex141, addr}) ->
write_aex141_records(state3, addr, txi, i, args)
is_aexn_transfer?(evt_hash) and State.exists?(state2, Model.AexnContract, {:aex141, addr}) ->
write_aex141_records(state2, addr, txi, i, args)

true ->
state3
state2
end
end)
end
Expand Down Expand Up @@ -319,6 +297,46 @@ defmodule AeMdw.Db.Contract do
|> Stream.take_while(key_tester)
end

defp maybe_index_remote_log(
state,
contract_pk,
block_index,
txi,
{{log_pk, _event, _data}, _i} = log
) do
# if remote call then indexes also with the called contract
if log_pk != contract_pk do
do_index_remote_log(state, contract_pk, block_index, txi, log)
else
state
end
end

defp do_index_remote_log(
state,
contract_pk,
block_index,
txi,
{{log_pk, [evt_hash | args], data}, i}
) do
remote_contract_txi = Origin.tx_index!(state, {:contract, contract_pk})

# on caller log: ext_contract = called contract_pk
# on called log: ext_contract = {:parent_contract_pk, caller contract_pk}
m_log_remote =
Model.contract_log(
index: {remote_contract_txi, txi, evt_hash, i},
ext_contract: {:parent_contract_pk, contract_pk},
args: args,
data: data
)

state
|> State.put(Model.ContractLog, m_log_remote)
|> State.cache_put(:ct_create_sync_cache, contract_pk, remote_contract_txi)
|> update_aex9_state(log_pk, block_index, txi)
end

defp write_aex141_records(
state,
contract_pk,
Expand All @@ -342,11 +360,18 @@ defmodule AeMdw.Db.Contract do

defp write_aex141_records(state, _pk, _txi, _i, _args), do: state

defp write_aexn_transfer(state, aexn_type, contract_pk, txi, i, [
<<_pk1::256>> = from_pk,
<<_pk2::256>> = to_pk,
<<value::256>>
]) do
defp write_aexn_transfer(
state,
aexn_type,
contract_pk,
txi,
i,
[
<<_pk1::256>> = from_pk,
<<_pk2::256>> = to_pk,
<<value::256>>
] = transfer
) do
m_transfer =
Model.aexn_transfer(
index: {aexn_type, from_pk, txi, to_pk, value, i},
Expand All @@ -360,9 +385,30 @@ defmodule AeMdw.Db.Contract do
|> State.put(Model.AexnTransfer, m_transfer)
|> State.put(Model.RevAexnTransfer, m_rev_transfer)
|> State.put(Model.AexnPairTransfer, m_pair_transfer)
|> maybe_index_contract_transfer(aexn_type, contract_pk, txi, i, transfer)
end

defp write_aexn_transfer(state, _contract_pk, _type, _txi, _i, _args), do: state
defp write_aexn_transfer(state, _type, _pk, _txi, _i, _args), do: state

defp maybe_index_contract_transfer(state, :aex9, _pk, _txi, _i, _transfer), do: state

defp maybe_index_contract_transfer(state, :aex141, contract_pk, txi, i, [
from_pk,
to_pk,
<<token_id::256>>
]) do
create_txi = Origin.tx_index!(state, {:contract, contract_pk})

m_ct_from =
Model.aexn_contract_from_transfer(index: {create_txi, from_pk, txi, to_pk, token_id, i})

m_ct_to =
Model.aexn_contract_to_transfer(index: {create_txi, to_pk, txi, from_pk, token_id, i})

state
|> State.put(Model.AexnContractFromTransfer, m_ct_from)
|> State.put(Model.AexnContractToTransfer, m_ct_to)
end

defp fetch_aex9_balance_or_new(state, contract_pk, account_pk) do
case State.get(state, Model.Aex9Balance, {contract_pk, account_pk}) do
Expand Down
Loading

0 comments on commit 322dac0

Please sign in to comment.