@@ -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 """
0 commit comments