/* This file is part of DarkFi (https://dark.fi) * * Copyright (C) 2020-2024 Dyne.org foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ use std::time::Instant; use rayon::prelude::*; use tfhe::integer::ciphertext::RadixCiphertext; use tfhe::integer::ServerKey; use crate::NUMBER_OF_BLOCKS; // Calculate the element sum of the given vector in parallel fn vector_sum(server_key: &ServerKey, orders: Vec) -> RadixCiphertext { orders.into_par_iter().reduce( || server_key.create_trivial_zero_radix(NUMBER_OF_BLOCKS), |mut acc: RadixCiphertext, mut ele: RadixCiphertext| { server_key.smart_add_parallelized(&mut acc, &mut ele) }, ) } fn fill_orders( server_key: &ServerKey, orders: &mut [RadixCiphertext], total_volume: RadixCiphertext, ) { let mut volume_left_to_transact = total_volume; for order in orders { let mut filled_amount = server_key.smart_min_parallelized(&mut volume_left_to_transact, order); server_key.smart_sub_assign_parallelized(&mut volume_left_to_transact, &mut filled_amount); *order = filled_amount; } } /// FHE implementation of the volume matching algorithm. /// /// This version of the algorithm utilizes parallelization to speed up the computation. /// /// Matches the given encrypted [sell_orders] with encrypted [buy_orders] using the given /// [server_key]. The amount of the orders that are successfully filled is written over the original /// order count. pub fn volume_match( sell_orders: &mut [RadixCiphertext], buy_orders: &mut [RadixCiphertext], server_key: &ServerKey, ) { println!("Calculating total sell and buy volumes..."); let time = Instant::now(); // Total sell and buy volumes can be calculated in parallel because they have no dependency on // each other. let (mut total_sell_volume, mut total_buy_volume) = rayon::join( || vector_sum(server_key, sell_orders.to_owned()), || vector_sum(server_key, buy_orders.to_owned()), ); println!( "Total sell and buy volumes are calculated in {:?}", time.elapsed() ); println!("Calculating total volume to be matched..."); let time = Instant::now(); let total_volume = server_key.smart_min_parallelized(&mut total_sell_volume, &mut total_buy_volume); println!( "Calculated total volume to be matched in {:?}", time.elapsed() ); println!("Filling orders..."); let time = Instant::now(); rayon::join( || fill_orders(server_key, sell_orders, total_volume.clone()), || fill_orders(server_key, buy_orders, total_volume.clone()), ); println!("Filled orders in {:?}", time.elapsed()); }