Skip to content

Commit 54590f2

Browse files
committed
make balance search use async
1 parent ef562ac commit 54590f2

File tree

2 files changed

+93
-3
lines changed

2 files changed

+93
-3
lines changed

lib/proof_of_reserves.ex

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,98 @@ defmodule ProofOfReserves do
119119
end)
120120
end
121121

122+
@doc """
123+
async_find_balances_for_accounts is the async version of find_balances_for_accounts.
124+
It is a recursive function that splits the leaves into two lists, finds the balances
125+
for each account in each list, and then merges the results of the two lists.
126+
"""
127+
@spec async_find_balances_for_accounts(
128+
list(MerkleSumTree.Node.t()),
129+
non_neg_integer(),
130+
list(%{
131+
account_id: non_neg_integer(),
132+
account_subkey: binary()
133+
})
134+
) ::
135+
list(%{
136+
account_id: non_neg_integer(),
137+
balance: non_neg_integer(),
138+
attestation_key: binary()
139+
})
140+
def async_find_balances_for_accounts(leaves, block_height, accounts) do
141+
account_balances =
142+
Enum.map(accounts, fn %{account_id: account_id, account_subkey: subkey} ->
143+
%{
144+
account_id: account_id,
145+
balance: 0,
146+
attestation_key: Util.calculate_attestation_key(subkey, block_height, account_id)
147+
}
148+
end)
149+
150+
async_find_balances(leaves, 0, account_balances)
151+
end
152+
153+
# helper function for async_find_balances_for_accounts
154+
@spec async_find_balances(
155+
list(MerkleSumTree.Node.t()),
156+
non_neg_integer(),
157+
list(%{
158+
account_id: non_neg_integer(),
159+
balance: non_neg_integer(),
160+
attestation_key: binary()
161+
})
162+
) ::
163+
list(%{
164+
account_id: non_neg_integer(),
165+
balance: non_neg_integer(),
166+
attestation_key: binary()
167+
})
168+
defp async_find_balances([], _leaf_idx, account_balances), do: account_balances
169+
170+
defp async_find_balances([leaf], leaf_idx, account_balances) do
171+
# when there is only one leaf, we can simply check if the leaf hash matches the leaf hash
172+
# for each account in the list. If it does, we add the value to the balance.
173+
Enum.map(account_balances, fn %{balance: balance, attestation_key: attestation_key} =
174+
account_balance ->
175+
# TODO: only a single account should match the leaf hash at most, so we can return early
176+
if Util.leaf_hash(leaf.value, attestation_key, leaf_idx) == leaf.hash do
177+
# if the leaf hash matches, we add the value to the balance
178+
Map.put(account_balance, :balance, balance + leaf.value)
179+
else
180+
account_balance
181+
end
182+
end)
183+
end
184+
185+
defp async_find_balances(leaves, leaf_idx, account_balances) do
186+
# Split the leaves into two lists
187+
leaf_ct = length(leaves)
188+
mid_idx = div(leaf_ct, 2)
189+
190+
{left_leaves, right_leaves} = Enum.split(leaves, mid_idx)
191+
192+
# Recursively & asynchronously find the balances for each account in both lists
193+
left_task = Task.async(fn -> async_find_balances(left_leaves, leaf_idx, account_balances) end)
194+
195+
right_task =
196+
Task.async(fn -> async_find_balances(right_leaves, leaf_idx + mid_idx, account_balances) end)
197+
198+
left_balances = Task.await(left_task, :infinity)
199+
right_balances = Task.await(right_task, :infinity)
200+
201+
# Merge the results of the two lists
202+
Enum.zip_with(left_balances, right_balances, fn
203+
# the matching on account_id and attestation_key ensures that the balances are added to the correct account
204+
%{account_id: account_id, balance: left_balance, attestation_key: attestation_key},
205+
%{account_id: account_id, balance: right_balance, attestation_key: attestation_key} ->
206+
%{
207+
account_id: account_id,
208+
balance: left_balance + right_balance,
209+
attestation_key: attestation_key
210+
}
211+
end)
212+
end
213+
122214
@doc """
123215
get_tree_root returns the root node of a Merkle Sum Tree.
124216
"""

verify_liabilities.exs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,12 +157,10 @@ defmodule VerifyLiabilities do
157157
account_uids =
158158
Map.new(accounts, fn %{account_id: id, account_uid: account_uid} -> {id, account_uid} end)
159159

160-
IO.puts("Verifying balances in Merkle Tree...")
161-
162160
balances =
163161
tree
164162
|> ProofOfReserves.MerkleSumTree.get_leaves()
165-
|> ProofOfReserves.find_balances_for_accounts(block_height, accounts)
163+
|> ProofOfReserves.async_find_balances_for_accounts(block_height, accounts)
166164
|> Enum.map(fn %{balance: balance, account_id: account_id} ->
167165
%{
168166
account_uid: Map.fetch!(account_uids, account_id),

0 commit comments

Comments
 (0)