11//! FFI bindings for contested DPNS username queries
22
3+ use dash_sdk:: dpp:: data_contract:: accessors:: v0:: DataContractV0Getters ;
4+ use dash_sdk:: dpp:: document:: DocumentV0Getters ;
5+ use dash_sdk:: dpp:: system_data_contracts:: { load_system_data_contract, SystemDataContract } ;
36use std:: ffi:: { CStr , CString } ;
47use std:: os:: raw:: c_char;
58
@@ -488,6 +491,14 @@ pub unsafe extern "C" fn dash_sdk_dpns_get_non_resolved_contests_for_identity(
488491
489492 match result {
490493 Ok ( names_with_contest_info) => {
494+ // Baked-in system contract: no network round trip, and it is the
495+ // schema these documents were written against.
496+ let platform_version = sdk. version ( ) ;
497+ let dpns_domain_type =
498+ load_system_data_contract ( SystemDataContract :: DPNS , platform_version)
499+ . ok ( )
500+ . and_then ( |contract| contract. document_type_cloned_for_name ( "domain" ) . ok ( ) ) ;
501+
491502 let count = names_with_contest_info. len ( ) ;
492503 let mut names = Vec :: with_capacity ( count) ;
493504
@@ -512,12 +523,55 @@ pub unsafe extern "C" fn dash_sdk_dpns_get_non_resolved_contests_for_identity(
512523 // Extract actual vote tally from ContenderWithSerializedDocument
513524 let vote_count = votes. vote_tally ( ) . unwrap_or ( 0 ) ;
514525
526+ // The requester's own spelling ("pizza"), which the
527+ // normalized contest name ("p1zza") does not preserve.
528+ // Null when the document can't be decoded — callers fall
529+ // back to the normalized name rather than guessing.
530+ let c_label = dpns_domain_type
531+ . as_ref ( )
532+ . and_then ( |doc_type| {
533+ let decoded = votes
534+ . try_to_contender ( doc_type. as_ref ( ) , platform_version)
535+ . ok ( ) ?;
536+ let label = decoded. document ( ) . as_ref ( ) ?. get ( "label" ) ?. as_str ( ) ?;
537+ CString :: new ( label) . ok ( ) . map ( |s| s. into_raw ( ) )
538+ } )
539+ . unwrap_or ( std:: ptr:: null_mut ( ) ) ;
540+
515541 contenders. push ( DashSDKContender {
516542 identity_id : c_id,
517543 vote_count,
544+ label : c_label,
518545 } ) ;
519546 }
520547
548+ // Ordered, de-duplicated requested labels. Derived here so the
549+ // display policy lives in one place rather than being
550+ // re-implemented by each language binding.
551+ let mut requested_labels: Vec < * mut c_char > = Vec :: new ( ) ;
552+ let mut seen_labels: Vec < String > = Vec :: new ( ) ;
553+ for contender in & contenders {
554+ if contender. label . is_null ( ) {
555+ continue ;
556+ }
557+ let label = CStr :: from_ptr ( contender. label )
558+ . to_string_lossy ( )
559+ . into_owned ( ) ;
560+ if label. is_empty ( ) || seen_labels. contains ( & label) {
561+ continue ;
562+ }
563+ if let Ok ( c_label) = CString :: new ( label. clone ( ) ) {
564+ seen_labels. push ( label) ;
565+ requested_labels. push ( c_label. into_raw ( ) ) ;
566+ }
567+ }
568+ let requested_label_count = requested_labels. len ( ) ;
569+ let requested_labels_ptr = if requested_labels. is_empty ( ) {
570+ std:: ptr:: null_mut ( )
571+ } else {
572+ Box :: into_raw ( requested_labels. into_boxed_slice ( ) ) as * mut * mut c_char
573+ } ;
574+
521575 let contender_count = contenders. len ( ) ;
522576 let contenders_ptr = if contenders. is_empty ( ) {
523577 std:: ptr:: null_mut ( )
@@ -529,6 +583,8 @@ pub unsafe extern "C" fn dash_sdk_dpns_get_non_resolved_contests_for_identity(
529583 let contest_info_c = DashSDKContestInfo {
530584 contenders : contenders_ptr,
531585 contender_count,
586+ requested_labels : requested_labels_ptr,
587+ requested_label_count,
532588 abstain_votes : contest_info. contenders . abstain_vote_tally . unwrap_or ( 0 ) ,
533589 lock_votes : contest_info. contenders . lock_vote_tally . unwrap_or ( 0 ) ,
534590 end_time : contest_info. end_time ,
@@ -587,6 +643,14 @@ pub unsafe extern "C" fn dash_sdk_dpns_get_contested_non_resolved_usernames(
587643
588644 match result {
589645 Ok ( names_with_contest_info) => {
646+ // Baked-in system contract: no network round trip, and it is the
647+ // schema these documents were written against.
648+ let platform_version = sdk. version ( ) ;
649+ let dpns_domain_type =
650+ load_system_data_contract ( SystemDataContract :: DPNS , platform_version)
651+ . ok ( )
652+ . and_then ( |contract| contract. document_type_cloned_for_name ( "domain" ) . ok ( ) ) ;
653+
590654 let count = names_with_contest_info. len ( ) ;
591655 let mut names = Vec :: with_capacity ( count) ;
592656
@@ -611,12 +675,55 @@ pub unsafe extern "C" fn dash_sdk_dpns_get_contested_non_resolved_usernames(
611675 // Extract actual vote tally from ContenderWithSerializedDocument
612676 let vote_count = votes. vote_tally ( ) . unwrap_or ( 0 ) ;
613677
678+ // The requester's own spelling ("pizza"), which the
679+ // normalized contest name ("p1zza") does not preserve.
680+ // Null when the document can't be decoded — callers fall
681+ // back to the normalized name rather than guessing.
682+ let c_label = dpns_domain_type
683+ . as_ref ( )
684+ . and_then ( |doc_type| {
685+ let decoded = votes
686+ . try_to_contender ( doc_type. as_ref ( ) , platform_version)
687+ . ok ( ) ?;
688+ let label = decoded. document ( ) . as_ref ( ) ?. get ( "label" ) ?. as_str ( ) ?;
689+ CString :: new ( label) . ok ( ) . map ( |s| s. into_raw ( ) )
690+ } )
691+ . unwrap_or ( std:: ptr:: null_mut ( ) ) ;
692+
614693 contenders. push ( DashSDKContender {
615694 identity_id : c_id,
616695 vote_count,
696+ label : c_label,
617697 } ) ;
618698 }
619699
700+ // Ordered, de-duplicated requested labels. Derived here so the
701+ // display policy lives in one place rather than being
702+ // re-implemented by each language binding.
703+ let mut requested_labels: Vec < * mut c_char > = Vec :: new ( ) ;
704+ let mut seen_labels: Vec < String > = Vec :: new ( ) ;
705+ for contender in & contenders {
706+ if contender. label . is_null ( ) {
707+ continue ;
708+ }
709+ let label = CStr :: from_ptr ( contender. label )
710+ . to_string_lossy ( )
711+ . into_owned ( ) ;
712+ if label. is_empty ( ) || seen_labels. contains ( & label) {
713+ continue ;
714+ }
715+ if let Ok ( c_label) = CString :: new ( label. clone ( ) ) {
716+ seen_labels. push ( label) ;
717+ requested_labels. push ( c_label. into_raw ( ) ) ;
718+ }
719+ }
720+ let requested_label_count = requested_labels. len ( ) ;
721+ let requested_labels_ptr = if requested_labels. is_empty ( ) {
722+ std:: ptr:: null_mut ( )
723+ } else {
724+ Box :: into_raw ( requested_labels. into_boxed_slice ( ) ) as * mut * mut c_char
725+ } ;
726+
620727 let contender_count = contenders. len ( ) ;
621728 let contenders_ptr = if contenders. is_empty ( ) {
622729 std:: ptr:: null_mut ( )
@@ -628,6 +735,8 @@ pub unsafe extern "C" fn dash_sdk_dpns_get_contested_non_resolved_usernames(
628735 let contest_info_c = DashSDKContestInfo {
629736 contenders : contenders_ptr,
630737 contender_count,
738+ requested_labels : requested_labels_ptr,
739+ requested_label_count,
631740 abstain_votes : contest_info. contenders . abstain_vote_tally . unwrap_or ( 0 ) ,
632741 lock_votes : contest_info. contenders . lock_vote_tally . unwrap_or ( 0 ) ,
633742 end_time : contest_info. end_time ,
0 commit comments