Skip to content

Commit

Permalink
feat: mark aex9 as invalid (#1799)
Browse files Browse the repository at this point in the history
  • Loading branch information
vatanasov committed Jun 10, 2024
1 parent 4d5949e commit f6e1e5b
Show file tree
Hide file tree
Showing 17 changed files with 312 additions and 69 deletions.
10 changes: 10 additions & 0 deletions docs/swagger_v3/aexn.spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ schemas:
nft_owners: 1
nfts_amount: 36
symbol: "ASITM-T"
invalid: false
properties:
base_url:
description: Metadata base url
Expand All @@ -40,6 +41,9 @@ schemas:
limits:
description: Limit for amount of tokens or templates
type: object
invalid:
description: True if the contract is invalid
type: boolean
required:
- base_url
- contract_id
Expand All @@ -51,6 +55,7 @@ schemas:
- nft_owners
- nfts_amount
- symbol
- invalid
title: Aex141Response
type: object
Aex141TokenResponse:
Expand Down Expand Up @@ -155,6 +160,7 @@ schemas:
holders: 12
name: testnetAE
symbol: TTAE
invalid: false
properties:
contract_id:
description: Id of the contract of the AEX9 tokens
Expand Down Expand Up @@ -185,6 +191,9 @@ schemas:
symbol:
description: Symbol of AEX9 token
type: string
invalid:
description: True if the contract is invalid
type: boolean
required:
- contract_id
- contract_tx_hash
Expand All @@ -194,6 +203,7 @@ schemas:
- holders
- initial_supply
- event_supply
- invalid
title: Aex9Response
type: object
Aex9TransferResponse:
Expand Down
43 changes: 38 additions & 5 deletions lib/ae_mdw/aex9.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ defmodule AeMdw.Aex9 do
"""

alias :aeser_api_encoder, as: Enc
alias AeMdw.AexnContracts
alias AeMdw.Collection
alias AeMdw.Db.Model
alias AeMdw.Db.State
Expand Down Expand Up @@ -39,6 +40,16 @@ defmodule AeMdw.Aex9 do

@type amounts :: map()

@spec invalid_number_of_holders_reason() :: String.t()
def invalid_number_of_holders_reason() do
"Invalid number of holders"
end

@spec invalid_holder_balance_reason() :: String.t()
def invalid_holder_balance_reason() do
"Invalid holder balance"
end

@spec fetch_balances(State.t(), State.t(), pubkey(), boolean()) ::
{:ok, amounts()} | {:error, Error.t()}
def fetch_balances(state, async_state, contract_pk, top?) do
Expand Down Expand Up @@ -82,12 +93,14 @@ defmodule AeMdw.Aex9 do
{:ok, {balances_cursor() | nil, [{pubkey(), pubkey()}], balances_cursor() | nil}}
| {:error, Error.t()}
def fetch_event_balances(state, contract_id, pagination, cursor, :pubkey, query) do
with {:ok, contract_pk} <- Validate.id(contract_id, [:contract_pubkey]),
with {:ok, contract_pk} <- AexnContracts.validate_aex9(contract_id, state),
{:ok, cursor} <- deserialize_event_balances_cursor(cursor),
{:ok, filters} <- Util.convert_params(query, &convert_param/1),
{:ok, creation_txi} <- get_aex9_contract(state, contract_pk),
{:ok, streamer} <-
event_balances_streamer(state, contract_pk, creation_txi, cursor, filters) do
contract_id = encode_contract(contract_pk)

paginated_balances =
Collection.paginate(
streamer,
Expand All @@ -104,9 +117,10 @@ defmodule AeMdw.Aex9 do
end

def fetch_event_balances(state, contract_id, pagination, cursor, :amount, _query) do
with {:ok, contract_pk} <- Validate.id(contract_id, [:contract_pubkey]),
with {:ok, contract_pk} <- AexnContracts.validate_aex9(contract_id, state),
{:ok, cursor_key} <- deserialize_balance_account_cursor(contract_pk, cursor) do
key_boundary = {{contract_pk, -1, <<>>}, {contract_pk, nil, <<>>}}
contract_id = encode_contract(contract_pk)

paginated_balances =
(&Collection.stream(state, Model.Aex9BalanceAccount, &1, key_boundary, cursor_key))
Expand Down Expand Up @@ -153,7 +167,18 @@ defmodule AeMdw.Aex9 do
key_boundary = {{contract_pk, <<>>}, {contract_pk, Util.max_256bit_bin()}}
cursor = if cursor, do: {contract_pk, cursor}, else: nil

{:ok, &Collection.stream(state, Model.Aex9EventBalance, &1, key_boundary, cursor)}
{:ok,
fn direction ->
state
|> Collection.stream(Model.Aex9EventBalance, direction, key_boundary, cursor)
|> Stream.reject(fn {_contract_pk, account_pk} -> account_pk == <<>> end)
|> Stream.reject(fn key ->
Model.aex9_event_balance(amount: amount) =
State.fetch!(state, Model.Aex9EventBalance, key)

amount == 0
end)
end}
end

@spec fetch_holders_count(State.t(), pubkey()) :: non_neg_integer()
Expand All @@ -163,7 +188,9 @@ defmodule AeMdw.Aex9 do
state
|> Collection.stream(Model.Aex9EventBalance, :forward, key_boundary, nil)
|> Stream.map(&State.fetch!(state, Model.Aex9EventBalance, &1))
|> Enum.count(fn Model.aex9_event_balance(amount: amount) -> amount > 0 end)
|> Enum.count(fn Model.aex9_event_balance(index: {_contract_pk, account_pk}, amount: amount) ->
amount > 0 && account_pk != <<>>
end)
end

@spec fetch_balance(pubkey(), pubkey(), height_hash() | nil) ::
Expand Down Expand Up @@ -221,7 +248,13 @@ defmodule AeMdw.Aex9 do
scope = {{account_pk, <<>>}, {account_pk, Util.max_256bit_bin()}}

paginated_account_balances =
(&Collection.stream(state, Model.Aex9AccountPresence, &1, scope, cursor))
fn direction ->
state
|> Collection.stream(Model.Aex9AccountPresence, direction, scope, cursor)
|> Stream.reject(fn {_account_pk, contract_pk} ->
State.exists?(state, Model.AexnInvalidContract, {:aex9, contract_pk})
end)
end
|> Collection.paginate(
pagination,
&render_account_balance(state, type_height_hash, &1),
Expand Down
23 changes: 23 additions & 0 deletions lib/ae_mdw/aexn_contracts.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@ defmodule AeMdw.AexnContracts do

alias AeMdw.Contract
alias AeMdw.Db.Model
alias AeMdw.Db.State
alias AeMdw.DryRun.Runner
alias AeMdw.Error
alias AeMdw.Error.Input, as: ErrInput
alias AeMdw.Node
alias AeMdw.Log
alias AeMdw.Validate

import AeMdw.Util.Encoding, only: [encode_contract: 1]

Expand Down Expand Up @@ -37,6 +41,25 @@ defmodule AeMdw.AexnContracts do

def is_aex9?(_no_fcode), do: false

@spec validate_aex9(String.t(), State.t()) :: {:ok, pubkey()} | {:error, Error.t()}
def validate_aex9(contract_id, state) do
with {:ok, contract_pk} <- Validate.id(contract_id, [:contract_pubkey]),
{:not_aex9, true} <- {:not_aex9, is_aex9?(contract_pk)},
{:invalid, false} <-
{:invalid, State.exists?(state, Model.AexnInvalidContract, {:aex9, contract_pk})} do
{:ok, contract_pk}
else
{:error, reason} ->
{:error, reason}

{:not_aex9, false} ->
{:error, ErrInput.NotAex9.exception(value: contract_id)}

{:invalid, true} ->
{:error, ErrInput.AexnContractInvalid.exception(value: contract_id)}
end
end

@spec has_aex141_signatures?(height(), type_info()) :: boolean()
def has_aex141_signatures?(height, {:fcode, functions, _hash_names, _code}) do
signatures = :aec_governance.get_network_id() |> get_aex141_signatures(height)
Expand Down
20 changes: 12 additions & 8 deletions lib/ae_mdw/aexn_tokens.ex
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,16 @@ defmodule AeMdw.AexnTokens do
@spec fetch_contract(State.t(), {aexn_type(), Db.pubkey()}) ::
{:ok, Model.aexn_contract()} | {:error, Error.t()}
def fetch_contract(state, {aexn_type, contract_pk}) do
case State.get(state, Model.AexnContract, {aexn_type, contract_pk}) do
{:ok, m_aexn} ->
{:ok, m_aexn}

with {:ok, m_aexn} <- State.get(state, Model.AexnContract, {aexn_type, contract_pk}),
{:invalid, false} <-
{:invalid, State.exists?(state, Model.AexnInvalidContract, {aexn_type, contract_pk})} do
{:ok, m_aexn}
else
:not_found ->
{:error, ErrInput.NotFound.exception(value: encode_contract(contract_pk))}

{:invalid, true} ->
{:error, ErrInput.AexnContractInvalid.exception(value: encode_contract(contract_pk))}
end
end

Expand Down Expand Up @@ -76,10 +80,10 @@ defmodule AeMdw.AexnTokens do
state
|> Collection.stream(@aexn_creation_table, direction, key_boundary, cursor)
|> Stream.map(fn {aexn_type, txi_idx} ->
Model.aexn_contract_creation(contract_pk: pubkey) =
State.fetch!(state, @aexn_creation_table, {aexn_type, txi_idx})

State.fetch!(state, @aexn_table, {aexn_type, pubkey})
State.fetch!(state, @aexn_creation_table, {aexn_type, txi_idx})
end)
|> Stream.map(fn Model.aexn_contract_creation(contract_pk: pubkey) ->
State.fetch!(state, @aexn_table, {type, pubkey})
end)
end
end
Expand Down
23 changes: 18 additions & 5 deletions lib/ae_mdw/db/contract.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ defmodule AeMdw.Db.Contract do
Data access to read and write Contract related models.
"""

alias AeMdw.Aex9
alias AeMdw.AexnContracts
alias AeMdw.Collection
alias AeMdw.Contract
Expand Down Expand Up @@ -101,10 +102,22 @@ defmodule AeMdw.Db.Contract do

@spec aex9_burn_update_holders(state(), pubkey(), integer()) :: state()
def aex9_burn_update_holders(state, contract_pk, balance) do
if balance <= 0 do
SyncStats.decrement_aex9_holders(state, contract_pk)
else
state
case balance do
0 ->
SyncStats.decrement_aex9_holders(state, contract_pk)

balance when balance < 0 ->
State.put(
state,
Model.AexnInvalidContract,
Model.aexn_invalid_contract(
index: {:aex9, contract_pk},
reason: Aex9.invalid_holder_balance_reason()
)
)

_larger_than_zero ->
state
end
end

Expand Down Expand Up @@ -844,7 +857,7 @@ defmodule AeMdw.Db.Contract do
|> State.put(Model.Aex9EventBalance, m_from)
|> State.put(Model.AexnTransfer, m_transfer)
|> aex9_update_balance_account(contract_pk, from_amount, new_amount, from_pk, txi, log_idx)
|> aex9_burn_update_holders(contract_pk, from_amount - burn_value)
|> aex9_burn_update_holders(contract_pk, new_amount)
|> aex9_update_contract_balance(contract_pk, -burn_value)
|> aex9_write_presence(contract_pk, txi, from_pk)
end
Expand Down
15 changes: 15 additions & 0 deletions lib/ae_mdw/db/model.ex
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,19 @@ defmodule AeMdw.Db.Model do
]
defrecord :aex9_contract_balance, @aex9_contract_balance_defaults

# AEXN invalid contracts:
@type aexn_invalid_contract_index() :: {aexn_type(), pubkey()}
@type aexn_invalid_contract ::
record(:aexn_invalid_contract,
index: aexn_invalid_contract_index(),
reason: binary()
)
@aexn_invalid_contract_defaults [
index: {},
reason: nil
]
defrecord :aexn_invalid_contract, @aexn_invalid_contract_defaults

# AEX-N contract:
# index: {type, pubkey} where type = :aex9, :aex141, ...
# txi: txi
Expand Down Expand Up @@ -1222,6 +1235,7 @@ defmodule AeMdw.Db.Model do
AeMdw.Db.Model.Aex9EventBalance,
AeMdw.Db.Model.Aex9InitialSupply,
AeMdw.Db.Model.Aex9ContractBalance,
AeMdw.Db.Model.AexnInvalidContract,
AeMdw.Db.Model.AexnContract,
AeMdw.Db.Model.AexnContractCreation,
AeMdw.Db.Model.AexnContractName,
Expand Down Expand Up @@ -1335,6 +1349,7 @@ defmodule AeMdw.Db.Model do
def record(AeMdw.Db.Model.Aex9EventBalance), do: :aex9_event_balance
def record(AeMdw.Db.Model.Aex9InitialSupply), do: :aex9_initial_supply
def record(AeMdw.Db.Model.Aex9ContractBalance), do: :aex9_contract_balance
def record(AeMdw.Db.Model.AexnInvalidContract), do: :aexn_invalid_contract
def record(AeMdw.Db.Model.AexnContract), do: :aexn_contract
def record(AeMdw.Db.Model.AexnContractName), do: :aexn_contract_name
def record(AeMdw.Db.Model.AexnContractSymbol), do: :aexn_contract_symbol
Expand Down
24 changes: 22 additions & 2 deletions lib/ae_mdw/db/sync/stats.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ defmodule AeMdw.Db.Sync.Stats do
Update general and per contract stats during the syncing process.
"""

alias AeMdw.Aex9
alias AeMdw.Blocks
alias AeMdw.Db.Model
alias AeMdw.Db.Mutation
Expand Down Expand Up @@ -46,8 +47,27 @@ defmodule AeMdw.Db.Sync.Stats do
do: update_stat_counter(state, Stats.aex9_holder_count_key(contract_pk))

@spec decrement_aex9_holders(State.t(), pubkey()) :: State.t()
def decrement_aex9_holders(state, contract_pk),
do: update_stat_counter(state, Stats.aex9_holder_count_key(contract_pk), &(&1 - 1))
def decrement_aex9_holders(state, contract_pk) do
update_fn = fn x ->
new_count = x - 1

_ignored_result =
if new_count < 0 do
State.put(
state,
Model.AexnInvalidContract,
Model.aexn_invalid_contract(
index: {:aex9, contract_pk},
reason: Aex9.invalid_number_of_holders_reason()
)
)
end

new_count
end

update_stat_counter(state, Stats.aex9_holder_count_key(contract_pk), update_fn)
end

@spec increment_aex9_logs(State.t(), pubkey()) :: State.t()
def increment_aex9_logs(state, contract_pk),
Expand Down
3 changes: 3 additions & 0 deletions lib/ae_mdw/error.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ defmodule AeMdw.Error do
def to_string(Input.Expired, x), do: concat("expired", x)
def to_string(Input.NotAex9, x), do: concat("not AEX9 contract", x)
def to_string(Input.NotAex141, x), do: concat("not AEX141 contract", x)
def to_string(Input.AexnContractInvalid, x), do: concat("invalid AEXN contract", x)
def to_string(Input.ContractReturn, x), do: concat("invalid contract return", x)
def to_string(Input.ContractDryRun, x), do: concat("error calling contract", x)
def to_string(Input.Aex9BalanceNotAvailable, x), do: concat("balance is not available", x)
Expand All @@ -50,6 +51,7 @@ defmodule AeMdw.Error do
| __MODULE__.Expired
| __MODULE__.NotAex9
| __MODULE__.NotAex141
| __MODULE__.AexnContractInvalid
| __MODULE__.ContractReturn
| __MODULE__.ContractDryRun
| __MODULE__.Aex9BalanceNotAvailable
Expand All @@ -74,6 +76,7 @@ defmodule AeMdw.Error do
defexception!(Expired)
defexception!(NotAex9)
defexception!(NotAex141)
defexception!(AexnContractInvalid)
defexception!(ContractReturn)
defexception!(ContractDryRun)
defexception!(Aex9BalanceNotAvailable)
Expand Down
Loading

0 comments on commit f6e1e5b

Please sign in to comment.