| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155 |
- /* 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 <https://www.gnu.org/licenses/>.
- */
- use std::time::Instant;
- use rayon::prelude::*;
- use tfhe::integer::ciphertext::RadixCiphertext;
- use tfhe::integer::{IntegerCiphertext, ServerKey};
- use crate::NUMBER_OF_BLOCKS;
- fn compute_prefix_sum(server_key: &ServerKey, arr: &[RadixCiphertext]) -> Vec<RadixCiphertext> {
- if arr.is_empty() {
- return arr.to_vec();
- }
- let mut prefix_sum: Vec<RadixCiphertext> = (0..arr.len().next_power_of_two())
- .into_par_iter()
- .map(|i| {
- if i < arr.len() {
- arr[i].clone()
- } else {
- server_key.create_trivial_zero_radix(NUMBER_OF_BLOCKS)
- }
- })
- .collect();
- for d in 0..prefix_sum.len().ilog2() {
- prefix_sum
- .par_chunks_exact_mut(2_usize.pow(d + 1))
- .for_each(move |chunk| {
- let length = chunk.len();
- let mut left = chunk.get((length - 1) / 2).unwrap().clone();
- server_key.smart_add_assign_parallelized(chunk.last_mut().unwrap(), &mut left)
- });
- }
- let last = prefix_sum.last().unwrap().clone();
- *prefix_sum.last_mut().unwrap() = server_key.create_trivial_zero_radix(NUMBER_OF_BLOCKS);
- for d in (0..prefix_sum.len().ilog2()).rev() {
- prefix_sum
- .par_chunks_exact_mut(2_usize.pow(d + 1))
- .for_each(move |chunk| {
- let length = chunk.len();
- let temp = chunk.last().unwrap().clone();
- let mut mid = chunk.get((length - 1) / 2).unwrap().clone();
- server_key.smart_add_assign_parallelized(chunk.last_mut().unwrap(), &mut mid);
- chunk[(length - 1) / 2] = temp;
- });
- }
- prefix_sum.push(last);
- prefix_sum[1..=arr.len()].to_vec()
- }
- fn fill_orders(
- server_key: &ServerKey,
- total_orders: &RadixCiphertext,
- orders: &mut [RadixCiphertext],
- prefix_sum_arr: &[RadixCiphertext],
- ) {
- orders
- .into_par_iter()
- .enumerate()
- .for_each(move |(i, order)| {
- // (total_orders - previous_prefix_sum).max(0)
- let mut diff = if i == 0 {
- total_orders.clone()
- } else {
- let previous_prefix_sum = &prefix_sum_arr[i - 1];
- // total_orders - previous_prefix_sum
- let mut diff = server_key.smart_sub_parallelized(
- &mut total_orders.clone(),
- &mut previous_prefix_sum.clone(),
- );
- // total_orders > prefix_sum
- let mut cond = server_key
- .smart_gt_parallelized(
- &mut total_orders.clone(),
- &mut previous_prefix_sum.clone(),
- )
- .into_radix(diff.blocks().len(), server_key);
- // (total_orders - previous_prefix_sum) * (total_orders > previous_prefix_sum)
- // = (total_orders - previous_prefix_sum).max(0)
- server_key.smart_mul_parallelized(&mut cond, &mut diff)
- };
- // (total_orders - previous_prefix_sum).max(0).min(*order);
- *order = server_key.smart_min_parallelized(&mut diff, order);
- });
- }
- /// FHE implementation of the volume matching algorithm.
- ///
- /// In this function, the implemented algorithm is modified to utilize more concurrency.
- ///
- /// 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!("Creating prefix sum arrays...");
- let time = Instant::now();
- let (prefix_sum_sell_orders, prefix_sum_buy_orders) = rayon::join(
- || compute_prefix_sum(server_key, sell_orders),
- || compute_prefix_sum(server_key, buy_orders),
- );
- println!("Created prefix sum arrays in {:?}", time.elapsed());
- let zero = server_key.create_trivial_zero_radix(NUMBER_OF_BLOCKS);
- let total_buy_orders = prefix_sum_buy_orders.last().unwrap_or(&zero);
- let total_sell_orders = prefix_sum_sell_orders.last().unwrap_or(&zero);
- println!("Matching orders...");
- let time = Instant::now();
- rayon::join(
- || {
- fill_orders(
- server_key,
- total_sell_orders,
- buy_orders,
- &prefix_sum_buy_orders,
- )
- },
- || {
- fill_orders(
- server_key,
- total_buy_orders,
- sell_orders,
- &prefix_sum_sell_orders,
- )
- },
- );
- println!("Matched orders in {:?}", time.elapsed());
- }
|