Skip to content

Commit 685ba96

Browse files
authored
fix: update aex9 balance on any call and invalidate it on fork (#630)
1 parent b238c0f commit 685ba96

File tree

13 files changed

+102
-376
lines changed

13 files changed

+102
-376
lines changed

lib/ae_mdw/db/model.ex

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -220,14 +220,16 @@ defmodule AeMdw.Db.Model do
220220
)
221221

222222
# AEX9 balance:
223-
# index = {contract_pk, account_pk}
224-
# amounts: float
223+
# index: {contract_pk, account_pk}
224+
# block_index: {kbi, mbi},
225+
# amount: float
225226
@type aex9_balance ::
226227
record(:aex9_balance,
227228
index: {Db.pubkey(), Db.pubkey()},
229+
block_index: {Blocks.height(), Blocks.mbi()},
228230
amount: float()
229231
)
230-
@aex9_balance_defaults [index: {<<>>, <<>>}, amount: nil]
232+
@aex9_balance_defaults [index: {<<>>, <<>>}, block_index: {-1, -1}, amount: nil]
231233
defrecord :aex9_balance, @aex9_balance_defaults
232234

233235
# AEX9 contract:

lib/ae_mdw/db/mutations/aex9_account_balance_mutation.ex

Lines changed: 0 additions & 83 deletions
This file was deleted.

lib/ae_mdw/db/mutations/contract_call_mutation.ex

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ defmodule AeMdw.Db.ContractCallMutation do
6868
with true <- Contract.is_aex9?(contract_pk),
6969
{:ok, method_name, method_args} <- Contract.extract_successful_function(fun_arg_res),
7070
false <- Contract.is_non_stateful_aex9_function?(method_name) do
71+
# writes already known presence
7172
update_aex9_presence(
7273
contract_pk,
7374
caller_pk,
@@ -77,18 +78,17 @@ defmodule AeMdw.Db.ContractCallMutation do
7778
)
7879
end
7980

81+
# update balance on any call
82+
AsyncTasks.Producer.enqueue(:update_aex9_state, [contract_pk])
83+
8084
:ok
8185
end
8286

8387
#
8488
# Private functions
8589
#
8690
defp update_aex9_presence(contract_pk, caller_pk, txi, method_name, method_args) do
87-
updated? = write_aex9_presence(method_name, method_args, contract_pk, caller_pk, txi)
88-
89-
if not updated? do
90-
AsyncTasks.Producer.enqueue(:update_aex9_presence, [contract_pk])
91-
end
91+
:ok = write_aex9_presence(method_name, method_args, contract_pk, caller_pk, txi)
9292
end
9393

9494
defp write_aex9_presence(
@@ -99,12 +99,10 @@ defmodule AeMdw.Db.ContractCallMutation do
9999
txi
100100
) do
101101
DBContract.aex9_write_presence(contract_pk, txi, caller_pk)
102-
true
103102
end
104103

105104
defp write_aex9_presence("swap", [], contract_pk, caller_pk, txi) do
106105
DBContract.aex9_write_presence(contract_pk, txi, caller_pk)
107-
true
108106
end
109107

110108
defp write_aex9_presence(
@@ -119,18 +117,16 @@ defmodule AeMdw.Db.ContractCallMutation do
119117
) do
120118
to_pk = Validate.id!(to_account_id)
121119
DBContract.aex9_write_presence(contract_pk, txi, to_pk)
122-
true
123120
end
124121

125122
defp write_aex9_presence(method_name, method_args, contract_pk, caller_pk, txi) do
126123
case Contract.get_aex9_transfer(caller_pk, method_name, method_args) do
127124
{from_pk, to_pk, _value} ->
128125
DBContract.aex9_write_presence(contract_pk, txi, from_pk)
129126
DBContract.aex9_write_presence(contract_pk, txi, to_pk)
130-
true
131127

132128
nil ->
133-
false
129+
:ok
134130
end
135131
end
136132
end

lib/ae_mdw/db/sync/invalidate.ex

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ defmodule AeMdw.Db.Sync.Invalidate do
22
# credo:disable-for-this-file
33
alias AeMdw.Blocks
44
alias AeMdw.Contract
5+
alias AeMdw.Collection
56
alias AeMdw.Database
67
alias AeMdw.Db.Stream, as: DBS
78
alias AeMdw.Db.Format
@@ -12,6 +13,7 @@ defmodule AeMdw.Db.Sync.Invalidate do
1213
alias AeMdw.Db.OracleInvalidationMutation
1314
alias AeMdw.Log
1415
alias AeMdw.Node, as: AE
16+
alias AeMdw.Sync.AsyncTasks
1517
alias AeMdw.Validate
1618

1719
require Model
@@ -39,6 +41,7 @@ defmodule AeMdw.Db.Sync.Invalidate do
3941
aex9_transfer_key_dels = aex9_transfer_key_dels(from_txi)
4042
aex9_account_presence_key_dels = aex9_account_presence_key_dels(from_txi)
4143
aex9_account_presence_key_writes = aex9_account_presence_key_writes(from_txi)
44+
aex9_balance_key_dels = aex9_balance_key_dels(fork_height)
4245

4346
int_contract_call_key_dels = int_contract_call_key_dels(from_txi)
4447
int_transfer_tx_key_dels = int_transfer_tx_key_dels(prev_kbi)
@@ -55,6 +58,7 @@ defmodule AeMdw.Db.Sync.Invalidate do
5558
DeleteKeysMutation.new(aex9_key_dels),
5659
DeleteKeysMutation.new(aex9_transfer_key_dels),
5760
DeleteKeysMutation.new(aex9_account_presence_key_dels),
61+
DeleteKeysMutation.new(aex9_balance_key_dels),
5862
DeleteKeysMutation.new(contract_log_key_dels),
5963
DeleteKeysMutation.new(contract_call_key_dels),
6064
DeleteKeysMutation.new(int_contract_call_key_dels),
@@ -63,6 +67,15 @@ defmodule AeMdw.Db.Sync.Invalidate do
6367
UpdateIdsCountsMutation.new(fields_counts)
6468
])
6569

70+
aex9_balance_key_dels
71+
|> Enum.map(fn {contract_pk, _account_pk} -> contract_pk end)
72+
|> Enum.uniq()
73+
|> Enum.each(fn contract_pk ->
74+
AsyncTasks.Producer.enqueue(:update_aex9_state, [contract_pk])
75+
end)
76+
77+
AsyncTasks.Producer.commit_enqueued()
78+
6679
# only ets writes
6780
do_writes(aex9_account_presence_key_writes, &AeMdw.Db.Contract.aex9_presence_cache_write/2)
6881
end
@@ -295,6 +308,19 @@ defmodule AeMdw.Db.Sync.Invalidate do
295308
}
296309
end
297310

311+
defp aex9_balance_key_dels(height) do
312+
aex9_balance_keys =
313+
Model.Aex9Balance
314+
|> Collection.stream({<<>>, <<>>})
315+
|> Stream.filter(fn key ->
316+
Model.aex9_balance(block_index: {kbi, _mbi}) = Database.fetch!(Model.Aex9Balance, key)
317+
kbi >= height
318+
end)
319+
|> Enum.to_list()
320+
321+
%{Model.Aex9Balance => aex9_balance_keys}
322+
end
323+
298324
# computes records to be written to cache, flushed at microblock boundary
299325
def aex9_account_presence_key_writes(from_txi) do
300326
bi = Model.tx(read_tx!(from_txi), :block_index)

lib/ae_mdw/db/sync/transaction.ex

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ defmodule AeMdw.Db.Sync.Transaction do
66
alias AeMdw.Blocks
77
alias AeMdw.Contract
88
alias AeMdw.Db.Model
9-
alias AeMdw.Db.Aex9AccountBalanceMutation
109
alias AeMdw.Db.Aex9CreateContractMutation
1110
alias AeMdw.Db.ContractCallMutation
1211
alias AeMdw.Db.ContractCreateMutation
@@ -187,20 +186,10 @@ defmodule AeMdw.Db.Sync.Transaction do
187186
create_txi
188187
)
189188

190-
aex9_balance_mutation =
191-
with :ok <- :aect_call.return_type(call_rec),
192-
true <- Contract.is_aex9?(contract_pk),
193-
{:ok, method_name, method_args} <- Contract.extract_successful_function(fun_arg_res) do
194-
Aex9AccountBalanceMutation.new(method_name, method_args, contract_pk, caller_pk)
195-
else
196-
_error_or_false -> nil
197-
end
198-
199189
Enum.concat([
200190
child_mutations,
201191
events_mutations,
202192
[
203-
aex9_balance_mutation,
204193
ContractCallMutation.new(
205194
contract_pk,
206195
caller_pk,

lib/ae_mdw/sync/async_tasks/consumer.ex

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ defmodule AeMdw.Sync.AsyncTasks.Consumer do
1010
alias AeMdw.Sync.AsyncTasks.Producer
1111
alias AeMdw.Sync.AsyncTasks.TaskSupervisor
1212
alias AeMdw.Sync.AsyncTasks.DeriveAex9Presence
13-
alias AeMdw.Sync.AsyncTasks.UpdateAex9Presence
13+
alias AeMdw.Sync.AsyncTasks.UpdateAex9State
1414

1515
require Model
1616
require Logger
@@ -23,7 +23,7 @@ defmodule AeMdw.Sync.AsyncTasks.Consumer do
2323

2424
@type_mod %{
2525
derive_aex9_presence: DeriveAex9Presence,
26-
update_aex9_presence: UpdateAex9Presence
26+
update_aex9_state: UpdateAex9State
2727
}
2828

2929
# @max_retries 2

lib/ae_mdw/sync/async_tasks/derive_aex9_presence.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ defmodule AeMdw.Sync.AsyncTasks.DeriveAex9Presence do
4141
m_balance =
4242
Model.aex9_balance(
4343
index: {contract_pk, account_pk},
44+
block_index: {kbi, mbi},
4445
amount: amount
4546
)
4647

lib/ae_mdw/sync/async_tasks/update_aex9_presence.ex

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
defmodule AeMdw.Sync.AsyncTasks.UpdateAex9Presence do
1+
defmodule AeMdw.Sync.AsyncTasks.UpdateAex9State do
22
@moduledoc """
3-
Async work to derive AEX9 presence from balance dry-running.
3+
Async work to update AEX9 presence and balance through dry-run.
44
"""
55
@behaviour AeMdw.Sync.AsyncTasks.Work
66

@@ -16,29 +16,34 @@ defmodule AeMdw.Sync.AsyncTasks.UpdateAex9Presence do
1616
require Logger
1717

1818
@microsecs 1_000_000
19+
@min_height 500_000
1920

2021
@spec process(args :: list()) :: :ok
2122
def process([contract_pk]) do
22-
Log.info("[update_aex9_presence] #{inspect(contract_pk)} ...")
23+
Log.info("[update_aex9_state] #{inspect(contract_pk)} ...")
24+
{_type, height, _hash} = height_hash = DBN.top_height_hash(false)
2325

24-
{time_delta, {balances, _last_block_tuple}} =
25-
:timer.tc(fn -> DBN.aex9_balances(contract_pk) end)
26+
if height > @min_height do
27+
{time_delta, {balances, _height_hash}} =
28+
:timer.tc(fn -> DBN.aex9_balances(contract_pk, height_hash) end)
2629

27-
Log.info("[update_aex9_presence] #{inspect(contract_pk)} after #{time_delta / @microsecs}s")
30+
Log.info("[update_aex9_state] #{inspect(contract_pk)} after #{time_delta / @microsecs}s")
2831

29-
create_txi = Origin.tx_index!({:contract, contract_pk})
32+
create_txi = Origin.tx_index!({:contract, contract_pk})
3033

31-
Enum.each(balances, fn {{:address, account_pk}, amount} ->
32-
Contract.aex9_write_new_presence(contract_pk, create_txi, account_pk)
34+
Enum.each(balances, fn {{:address, account_pk}, amount} ->
35+
Contract.aex9_write_new_presence(contract_pk, create_txi, account_pk)
3336

34-
m_balance =
35-
Model.aex9_balance(
36-
index: {contract_pk, account_pk},
37-
amount: amount
38-
)
37+
m_balance =
38+
Model.aex9_balance(
39+
index: {contract_pk, account_pk},
40+
block_index: {height, -1},
41+
amount: amount
42+
)
3943

40-
Database.dirty_write(Model.Aex9Balance, m_balance)
41-
end)
44+
Database.dirty_write(Model.Aex9Balance, m_balance)
45+
end)
46+
end
4247

4348
:ok
4449
end
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
defmodule AeMdw.Migrations.RecalculateAex9Balance do
2+
@moduledoc """
3+
Updates .
4+
"""
5+
alias AeMdw.Database
6+
alias AeMdw.Db.Model
7+
alias AeMdw.Log
8+
alias AeMdw.Sync.AsyncTasks
9+
10+
require Model
11+
12+
@spec run(boolean()) :: {:ok, {non_neg_integer(), pos_integer()}}
13+
def run(_from_start?) do
14+
begin = DateTime.utc_now()
15+
16+
all_keys = Database.all_keys(Model.Aex9Balance)
17+
18+
Database.commit([
19+
AeMdw.Db.DeleteKeysMutation.new(%{Model.Aex9Balance => all_keys})
20+
])
21+
22+
indexing_count =
23+
all_keys
24+
|> Enum.map(fn {contract_pk, _account_pk} -> contract_pk end)
25+
|> Enum.uniq()
26+
|> Enum.map(fn contract_pk ->
27+
AsyncTasks.Producer.enqueue(:update_aex9_state, [contract_pk])
28+
end)
29+
|> Enum.count()
30+
31+
AsyncTasks.Producer.commit_enqueued()
32+
33+
duration = DateTime.diff(DateTime.utc_now(), begin)
34+
Log.info("Async indexation scheduled for #{indexing_count} contracts in #{duration}s")
35+
36+
{:ok, {indexing_count, duration}}
37+
end
38+
end

0 commit comments

Comments
 (0)