@@ -11,8 +11,22 @@ use libra_types::{
1111 move_resource:: { gas_coin:: SlowWalletBalance , txschedule:: TxSchedule } ,
1212 type_extensions:: client_ext:: { entry_function_id, ClientExt } ,
1313} ;
14+ use serde:: { Deserialize , Serialize } ;
1415use serde_json:: { json, Value } ;
1516
17+ /// Structured data for account vouch report
18+ #[ derive( Debug , Serialize , Deserialize ) ]
19+ pub struct AccountVouchReportData {
20+ pub account : String ,
21+ pub cached_score : Option < u64 > ,
22+ pub fresh_score : Option < u64 > ,
23+ pub max_depth_reached : Option < u64 > ,
24+ pub accounts_processed : Option < u64 > ,
25+ pub max_vouches_by_score : Option < u64 > ,
26+ pub remaining_vouches_available : Option < u64 > ,
27+ pub errors : Vec < String > ,
28+ }
29+
1630/// helper to get libra balance at a SlowWalletBalance type which shows
1731/// total balance and the unlocked balance.
1832pub async fn get_account_balance_libra (
@@ -134,3 +148,271 @@ pub async fn multi_auth_ballots(
134148
135149 Ok ( r. data )
136150}
151+
152+ /// Calculates a fresh page rank trust score for an account without updating the cache.
153+ /// Returns (score, max_depth_reached, accounts_processed) as a tuple.
154+ pub async fn page_rank_calculate_score (
155+ client : & Client ,
156+ account : AccountAddress ,
157+ ) -> anyhow:: Result < ( u64 , u64 , u64 ) > {
158+ let calculate_score_id = entry_function_id ( "page_rank_lazy" , "calculate_score" ) ?;
159+ let request = ViewRequest {
160+ function : calculate_score_id,
161+ type_arguments : vec ! [ ] ,
162+ arguments : vec ! [ account. to_string( ) . into( ) ] ,
163+ } ;
164+
165+ let res = client. view ( & request, None ) . await ?. into_inner ( ) ;
166+
167+ // Parse the tuple response (score, max_depth_reached, accounts_processed)
168+ if res. len ( ) != 3 {
169+ return Err ( anyhow:: anyhow!(
170+ "Expected 3 values from calculate_score, got {}" ,
171+ res. len( )
172+ ) ) ;
173+ }
174+
175+ // Handle string or numeric responses from the Move VM
176+ let score: u64 = match & res[ 0 ] {
177+ serde_json:: Value :: String ( s) => s. parse ( ) ?,
178+ serde_json:: Value :: Number ( n) => n
179+ . as_u64 ( )
180+ . ok_or_else ( || anyhow:: anyhow!( "Invalid number format for score" ) ) ?,
181+ _ => return Err ( anyhow:: anyhow!( "Unexpected response type for score" ) ) ,
182+ } ;
183+
184+ let max_depth_reached: u64 = match & res[ 1 ] {
185+ serde_json:: Value :: String ( s) => s. parse ( ) ?,
186+ serde_json:: Value :: Number ( n) => n
187+ . as_u64 ( )
188+ . ok_or_else ( || anyhow:: anyhow!( "Invalid number format for max_depth" ) ) ?,
189+ _ => return Err ( anyhow:: anyhow!( "Unexpected response type for max_depth" ) ) ,
190+ } ;
191+
192+ let accounts_processed: u64 = match & res[ 2 ] {
193+ serde_json:: Value :: String ( s) => s. parse ( ) ?,
194+ serde_json:: Value :: Number ( n) => n
195+ . as_u64 ( )
196+ . ok_or_else ( || anyhow:: anyhow!( "Invalid number format for accounts_processed" ) ) ?,
197+ _ => {
198+ return Err ( anyhow:: anyhow!(
199+ "Unexpected response type for accounts_processed"
200+ ) )
201+ }
202+ } ;
203+
204+ Ok ( ( score, max_depth_reached, accounts_processed) )
205+ }
206+
207+ /// Retrieves the cached page rank trust score for an account.
208+ /// This returns the previously computed score without recalculation.
209+ pub async fn page_rank_get_cached_score (
210+ client : & Client ,
211+ account : AccountAddress ,
212+ ) -> anyhow:: Result < u64 > {
213+ let get_cached_score_id = entry_function_id ( "page_rank_lazy" , "get_cached_score" ) ?;
214+ let request = ViewRequest {
215+ function : get_cached_score_id,
216+ type_arguments : vec ! [ ] ,
217+ arguments : vec ! [ account. to_string( ) . into( ) ] ,
218+ } ;
219+
220+ let res = client. view ( & request, None ) . await ?. into_inner ( ) ;
221+
222+ // Parse the single u64 response
223+ if res. is_empty ( ) {
224+ return Err ( anyhow:: anyhow!( "No values returned from get_cached_score" ) ) ;
225+ }
226+
227+ // Handle both string and numeric responses from the Move VM
228+ let score: u64 = match & res[ 0 ] {
229+ serde_json:: Value :: String ( s) => s. parse ( ) ?,
230+ serde_json:: Value :: Number ( n) => n
231+ . as_u64 ( )
232+ . ok_or_else ( || anyhow:: anyhow!( "Invalid number format" ) ) ?,
233+ _ => return Err ( anyhow:: anyhow!( "Unexpected response type for cached score" ) ) ,
234+ } ;
235+ Ok ( score)
236+ }
237+
238+ /// Calculates the maximum number of vouches a user should be able to give based on their trust score.
239+ /// This is determined by the user's page rank trust score relative to the maximum score in the system.
240+ pub async fn vouch_limits_calculate_score_limit (
241+ client : & Client ,
242+ account : AccountAddress ,
243+ ) -> anyhow:: Result < u64 > {
244+ let calculate_score_limit_id = entry_function_id ( "vouch_limits" , "calculate_score_limit" ) ?;
245+ let request = ViewRequest {
246+ function : calculate_score_limit_id,
247+ type_arguments : vec ! [ ] ,
248+ arguments : vec ! [ account. to_string( ) . into( ) ] ,
249+ } ;
250+
251+ let res = client. view ( & request, None ) . await ?. into_inner ( ) ;
252+
253+ // Parse the single u64 response
254+ if res. is_empty ( ) {
255+ return Err ( anyhow:: anyhow!(
256+ "No values returned from calculate_score_limit"
257+ ) ) ;
258+ }
259+
260+ // Handle both string and numeric responses from the Move VM
261+ let limit: u64 = match & res[ 0 ] {
262+ serde_json:: Value :: String ( s) => s. parse ( ) ?,
263+ serde_json:: Value :: Number ( n) => n
264+ . as_u64 ( )
265+ . ok_or_else ( || anyhow:: anyhow!( "Invalid number format" ) ) ?,
266+ _ => return Err ( anyhow:: anyhow!( "Unexpected response type for score limit" ) ) ,
267+ } ;
268+ Ok ( limit)
269+ }
270+
271+ /// Returns the number of vouches a user can still give based on system limits.
272+ /// This takes into account all constraints: base maximum limit, score-based limit,
273+ /// received vouches + 1 limit, and per-epoch limit.
274+ pub async fn vouch_limits_get_vouch_limit (
275+ client : & Client ,
276+ account : AccountAddress ,
277+ ) -> anyhow:: Result < u64 > {
278+ let get_vouch_limit_id = entry_function_id ( "vouch_limits" , "get_vouch_limit" ) ?;
279+ let request = ViewRequest {
280+ function : get_vouch_limit_id,
281+ type_arguments : vec ! [ ] ,
282+ arguments : vec ! [ account. to_string( ) . into( ) ] ,
283+ } ;
284+
285+ let res = client. view ( & request, None ) . await ?. into_inner ( ) ;
286+
287+ // Parse the single u64 response
288+ if res. is_empty ( ) {
289+ return Err ( anyhow:: anyhow!( "No values returned from get_vouch_limit" ) ) ;
290+ }
291+
292+ // Handle both string and numeric responses from the Move VM
293+ let limit: u64 = match & res[ 0 ] {
294+ serde_json:: Value :: String ( s) => s. parse ( ) ?,
295+ serde_json:: Value :: Number ( n) => n
296+ . as_u64 ( )
297+ . ok_or_else ( || anyhow:: anyhow!( "Invalid number format" ) ) ?,
298+ _ => return Err ( anyhow:: anyhow!( "Unexpected response type for vouch limit" ) ) ,
299+ } ;
300+ Ok ( limit)
301+ }
302+
303+ /// Creates a comprehensive vouch report for an account, combining page rank scores and vouch limits.
304+ /// This function returns structured data that can be used for JSON output or further processing.
305+ pub async fn account_vouch_report (
306+ client : & Client ,
307+ account : AccountAddress ,
308+ ) -> anyhow:: Result < AccountVouchReportData > {
309+ let mut errors = Vec :: new ( ) ;
310+
311+ // Get page rank scores
312+ let cached_score = match page_rank_get_cached_score ( client, account) . await {
313+ Ok ( score) => Some ( score) ,
314+ Err ( e) => {
315+ errors. push ( format ! ( "cached_score: {}" , e) ) ;
316+ None
317+ }
318+ } ;
319+
320+ let ( fresh_score, max_depth_reached, accounts_processed) =
321+ match page_rank_calculate_score ( client, account) . await {
322+ Ok ( ( score, max_depth_reached, accounts_processed) ) => (
323+ Some ( score) ,
324+ Some ( max_depth_reached) ,
325+ Some ( accounts_processed) ,
326+ ) ,
327+ Err ( e) => {
328+ errors. push ( format ! ( "fresh_score: {}" , e) ) ;
329+ ( None , None , None )
330+ }
331+ } ;
332+
333+ // Get vouch limits
334+ let max_vouches_by_score = match vouch_limits_calculate_score_limit ( client, account) . await {
335+ Ok ( limit) => Some ( limit) ,
336+ Err ( e) => {
337+ errors. push ( format ! ( "max_vouches_by_score: {}" , e) ) ;
338+ None
339+ }
340+ } ;
341+
342+ let remaining_vouches_available = match vouch_limits_get_vouch_limit ( client, account) . await {
343+ Ok ( limit) => Some ( limit) ,
344+ Err ( e) => {
345+ errors. push ( format ! ( "remaining_vouches_available: {}" , e) ) ;
346+ None
347+ }
348+ } ;
349+
350+ Ok ( AccountVouchReportData {
351+ account : account. to_string ( ) ,
352+ cached_score,
353+ fresh_score,
354+ max_depth_reached,
355+ accounts_processed,
356+ max_vouches_by_score,
357+ remaining_vouches_available,
358+ errors,
359+ } )
360+ }
361+
362+ /// Prints a comprehensive vouch report for an account to the console.
363+ /// This function provides a readable summary of an account's trust metrics and vouching capabilities.
364+ pub async fn account_vouch_report_console (
365+ client : & Client ,
366+ account : AccountAddress ,
367+ ) -> anyhow:: Result < ( ) > {
368+ let report = account_vouch_report ( client, account) . await ?;
369+
370+ println ! ( "=== Account Vouch Report for {} ===\n " , account) ;
371+
372+ // Page rank scores
373+ println ! ( "Page Rank Trust Scores:" ) ;
374+
375+ match report. cached_score {
376+ Some ( score) => println ! ( " • Cached Trust Score: {}" , score) ,
377+ None => println ! ( " • Cached Trust Score: Not available" ) ,
378+ }
379+
380+ match (
381+ report. fresh_score ,
382+ report. max_depth_reached ,
383+ report. accounts_processed ,
384+ ) {
385+ ( Some ( score) , Some ( max_depth) , Some ( accounts_processed) ) => {
386+ println ! ( " • Fresh Trust Score: {}" , score) ;
387+ println ! ( " • Max Depth Reached: {}" , max_depth) ;
388+ println ! ( " • Accounts Processed: {}" , accounts_processed) ;
389+ }
390+ _ => println ! ( " • Fresh Trust Score: Not available" ) ,
391+ }
392+
393+ println ! ( ) ;
394+
395+ // Vouch limits
396+ println ! ( "Vouching Limits:" ) ;
397+
398+ match report. max_vouches_by_score {
399+ Some ( limit) => println ! ( " • Max Vouches (based on trust score): {}" , limit) ,
400+ None => println ! ( " • Max Vouches (based on trust score): Not available" ) ,
401+ }
402+
403+ match report. remaining_vouches_available {
404+ Some ( limit) => println ! ( " • Remaining Vouches Available: {}" , limit) ,
405+ None => println ! ( " • Remaining Vouches Available: Not available" ) ,
406+ }
407+
408+ // Show errors if any
409+ if !report. errors . is_empty ( ) {
410+ println ! ( "\n Errors encountered:" ) ;
411+ for error in & report. errors {
412+ println ! ( " • {}" , error) ;
413+ }
414+ }
415+
416+ println ! ( "\n === End of Report ===" ) ;
417+ Ok ( ( ) )
418+ }
0 commit comments