@@ -91,6 +91,19 @@ fn main() {
9191 ) ) ;
9292 } ) ;
9393
94+ group. throughput ( Throughput :: Elements ( num_elems) ) ;
95+ let bench_id = format ! (
96+ "{bench_name}::throughput::dependent_whitepaper::FHEUint64::{num_elems}_elems::{bench_optimization_target}"
97+ ) ;
98+ group. bench_with_input ( bench_id. clone ( ) , & num_elems, move |b, & num_elems| {
99+ let _ = Runtime :: new ( )
100+ . unwrap ( )
101+ . block_on ( schedule_dependent_erc20_whitepaper (
102+ b,
103+ num_elems as usize ,
104+ bench_id. clone ( ) ,
105+ ) ) ;
106+ } ) ;
94107 group. throughput ( Throughput :: Elements ( num_elems) ) ;
95108 let bench_id = format ! (
96109 "{bench_name}::throughput::dependent_no_cmux::FHEUint64::{num_elems}_elems::{bench_optimization_target}"
@@ -466,6 +479,208 @@ async fn schedule_erc20_no_cmux(
466479 Ok ( ( ) )
467480}
468481
482+ async fn schedule_dependent_erc20_whitepaper (
483+ bencher : & mut Bencher < ' _ , WallTime > ,
484+ num_tx : usize ,
485+ bench_id : String ,
486+ ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
487+ let app = setup_test_app ( ) . await ?;
488+ let pool = sqlx:: postgres:: PgPoolOptions :: new ( )
489+ . max_connections ( 2 )
490+ . connect ( app. db_url ( ) )
491+ . await ?;
492+ let mut client = FhevmCoprocessorClient :: connect ( app. app_url ( ) . to_string ( ) ) . await ?;
493+
494+ let mut handle_counter: u64 = random_handle ( ) ;
495+ let mut next_handle = || {
496+ let out: u64 = handle_counter;
497+ handle_counter += 1 ;
498+ out. to_be_bytes ( ) . to_vec ( )
499+ } ;
500+ let api_key_header = format ! ( "bearer {}" , default_api_key( ) ) ;
501+
502+ let mut output_handles = vec ! [ ] ;
503+ let mut async_computations = vec ! [ ] ;
504+ let mut num_samples: usize = num_tx;
505+ let samples = std:: env:: var ( "FHEVM_TEST_NUM_SAMPLES" ) ;
506+ if let Ok ( samples) = samples {
507+ num_samples = samples. parse :: < usize > ( ) . unwrap ( ) ;
508+ }
509+
510+ let keys = query_tenant_keys ( vec ! [ default_tenant_id( ) ] , & pool)
511+ . await
512+ . map_err ( |e| {
513+ let e: Box < dyn std:: error:: Error > = e;
514+ e
515+ } ) ?;
516+ let keys = & keys[ 0 ] ;
517+
518+ let mut builder = tfhe:: ProvenCompactCiphertextList :: builder ( & keys. pks ) ;
519+ let the_list = builder
520+ . push ( 20_u64 ) // Initial balance destination
521+ . build_with_proof_packed ( & keys. public_params , & [ ] , tfhe:: zk:: ZkComputeLoad :: Proof )
522+ . unwrap ( ) ;
523+ let serialized = safe_serialize ( & the_list) ;
524+ let mut input_request = tonic:: Request :: new ( InputUploadBatch {
525+ input_ciphertexts : vec ! [ InputToUpload {
526+ input_payload: serialized,
527+ signatures: Vec :: new( ) ,
528+ user_address: test_random_user_address( ) ,
529+ contract_address: test_random_contract_address( ) ,
530+ } ] ,
531+ } ) ;
532+ input_request. metadata_mut ( ) . append (
533+ "authorization" ,
534+ MetadataValue :: from_str ( & api_key_header) . unwrap ( ) ,
535+ ) ;
536+ let resp = client. upload_inputs ( input_request) . await ?;
537+ let resp = resp. get_ref ( ) ;
538+ assert_eq ! ( resp. upload_responses. len( ) , 1 ) ;
539+ let first_resp = & resp. upload_responses [ 0 ] ;
540+ assert_eq ! ( first_resp. input_handles. len( ) , 1 ) ;
541+ let handle_bald = first_resp. input_handles [ 0 ] . handle . clone ( ) ;
542+ let mut bald = AsyncComputationInput {
543+ input : Some ( Input :: InputHandle ( handle_bald. clone ( ) ) ) ,
544+ } ;
545+
546+ let mut builder = tfhe:: ProvenCompactCiphertextList :: builder ( & keys. pks ) ;
547+ let the_list = builder
548+ . push ( 100_u64 ) // Balance source
549+ . push ( 10_u64 ) // Transfer amount
550+ . build_with_proof_packed ( & keys. public_params , & [ ] , tfhe:: zk:: ZkComputeLoad :: Proof )
551+ . unwrap ( ) ;
552+
553+ let serialized = safe_serialize ( & the_list) ;
554+ let mut input_request = tonic:: Request :: new ( InputUploadBatch {
555+ input_ciphertexts : vec ! [ InputToUpload {
556+ input_payload: serialized,
557+ signatures: Vec :: new( ) ,
558+ user_address: test_random_user_address( ) ,
559+ contract_address: test_random_contract_address( ) ,
560+ } ] ,
561+ } ) ;
562+ input_request. metadata_mut ( ) . append (
563+ "authorization" ,
564+ MetadataValue :: from_str ( & api_key_header) . unwrap ( ) ,
565+ ) ;
566+ let resp = client. upload_inputs ( input_request) . await ?;
567+ let resp = resp. get_ref ( ) ;
568+ assert_eq ! ( resp. upload_responses. len( ) , 1 ) ;
569+ let first_resp = & resp. upload_responses [ 0 ] ;
570+ assert_eq ! ( first_resp. input_handles. len( ) , 2 ) ;
571+
572+ for _ in 0 ..=( num_samples - 1 ) as u32 {
573+ let handle_bals = first_resp. input_handles [ 0 ] . handle . clone ( ) ;
574+ let bals = AsyncComputationInput {
575+ input : Some ( Input :: InputHandle ( handle_bals. clone ( ) ) ) ,
576+ } ;
577+ let handle_trxa = first_resp. input_handles [ 1 ] . handle . clone ( ) ;
578+ let trxa = AsyncComputationInput {
579+ input : Some ( Input :: InputHandle ( handle_trxa. clone ( ) ) ) ,
580+ } ;
581+
582+ let has_enough_funds_handle = next_handle ( ) ;
583+ output_handles. push ( has_enough_funds_handle. clone ( ) ) ;
584+ let new_to_amount_target_handle = next_handle ( ) ;
585+ output_handles. push ( new_to_amount_target_handle. clone ( ) ) ;
586+ let new_to_amount_handle = next_handle ( ) ;
587+ output_handles. push ( new_to_amount_handle. clone ( ) ) ;
588+ let new_from_amount_target_handle = next_handle ( ) ;
589+ output_handles. push ( new_from_amount_target_handle. clone ( ) ) ;
590+ let new_from_amount_handle = next_handle ( ) ;
591+ output_handles. push ( new_from_amount_handle. clone ( ) ) ;
592+
593+ async_computations. push ( AsyncComputation {
594+ operation : FheOperation :: FheGe . into ( ) ,
595+ output_handle : has_enough_funds_handle. clone ( ) ,
596+ inputs : vec ! [ bals. clone( ) , trxa. clone( ) ] ,
597+ } ) ;
598+ async_computations. push ( AsyncComputation {
599+ operation : FheOperation :: FheAdd . into ( ) ,
600+ output_handle : new_to_amount_target_handle. clone ( ) ,
601+ inputs : vec ! [ bald. clone( ) , trxa. clone( ) ] ,
602+ } ) ;
603+ async_computations. push ( AsyncComputation {
604+ operation : FheOperation :: FheIfThenElse . into ( ) ,
605+ output_handle : new_to_amount_handle. clone ( ) ,
606+ inputs : vec ! [
607+ AsyncComputationInput {
608+ input: Some ( Input :: InputHandle ( has_enough_funds_handle. clone( ) ) ) ,
609+ } ,
610+ AsyncComputationInput {
611+ input: Some ( Input :: InputHandle ( new_to_amount_target_handle. clone( ) ) ) ,
612+ } ,
613+ bald. clone( ) ,
614+ ] ,
615+ } ) ;
616+ async_computations. push ( AsyncComputation {
617+ operation : FheOperation :: FheSub . into ( ) ,
618+ output_handle : new_from_amount_target_handle. clone ( ) ,
619+ inputs : vec ! [ bals. clone( ) , trxa. clone( ) ] ,
620+ } ) ;
621+ async_computations. push ( AsyncComputation {
622+ operation : FheOperation :: FheIfThenElse . into ( ) ,
623+ output_handle : new_from_amount_handle. clone ( ) ,
624+ inputs : vec ! [
625+ AsyncComputationInput {
626+ input: Some ( Input :: InputHandle ( has_enough_funds_handle. clone( ) ) ) ,
627+ } ,
628+ AsyncComputationInput {
629+ input: Some ( Input :: InputHandle ( new_from_amount_target_handle. clone( ) ) ) ,
630+ } ,
631+ bals. clone( ) ,
632+ ] ,
633+ } ) ;
634+
635+ bald = AsyncComputationInput {
636+ input : Some ( Input :: InputHandle ( new_to_amount_handle. clone ( ) ) ) ,
637+ } ;
638+ }
639+
640+ let mut compute_request = tonic:: Request :: new ( AsyncComputeRequest {
641+ computations : async_computations,
642+ } ) ;
643+ compute_request. metadata_mut ( ) . append (
644+ "authorization" ,
645+ MetadataValue :: from_str ( & api_key_header) . unwrap ( ) ,
646+ ) ;
647+ let _resp = client. clone ( ) . async_compute ( compute_request) . await . unwrap ( ) ;
648+ let app_ref = & app;
649+ bencher
650+ . to_async ( FuturesExecutor )
651+ . iter_custom ( |iters| async move {
652+ let db_url = app_ref. db_url ( ) . to_string ( ) ;
653+ let now = SystemTime :: now ( ) ;
654+ let _ = tokio:: task:: spawn_blocking ( move || {
655+ Runtime :: new ( )
656+ . unwrap ( )
657+ . block_on ( async { wait_until_all_ciphertexts_computed ( db_url) . await . unwrap ( ) } ) ;
658+ println ! (
659+ "Execution time: {} -- {}" ,
660+ now. elapsed( ) . unwrap( ) . as_millis( ) ,
661+ TIMING . load( std:: sync:: atomic:: Ordering :: SeqCst ) / 1000
662+ ) ;
663+ } )
664+ . await ;
665+ std:: time:: Duration :: from_micros (
666+ TIMING . swap ( 0 , std:: sync:: atomic:: Ordering :: SeqCst ) * iters,
667+ )
668+ } ) ;
669+
670+ let params = keys. cks . computation_parameters ( ) ;
671+ write_to_json :: < u64 , _ > (
672+ & bench_id,
673+ params,
674+ "" ,
675+ "erc20-transfer" ,
676+ & OperatorType :: Atomic ,
677+ 64 ,
678+ vec ! [ ] ,
679+ ) ;
680+
681+ Ok ( ( ) )
682+ }
683+
469684async fn schedule_dependent_erc20_no_cmux (
470685 bencher : & mut Bencher < ' _ , WallTime > ,
471686 num_tx : usize ,
0 commit comments