-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding an example for Financial Application! This application uses ck…
…ks-engine crate and perform calculated on encrypted financial transactions
- Loading branch information
1 parent
c6f1ede
commit 152b872
Showing
2 changed files
with
793 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,68 +1,114 @@ | ||
use ckks_engine::utils::{encode, decode, mod_reduce}; | ||
use ckks_engine::utils::{encode, decode}; | ||
use ckks_engine::{CKKSEncryptor, CKKSDecryptor, CkksParameters, Polynomial}; | ||
use std::error::Error; | ||
use csv::Reader; | ||
use chrono::NaiveDate; | ||
use chrono::Datelike; | ||
|
||
fn main() { | ||
|
||
fn main() -> Result<(), Box<dyn Error>> { | ||
// Initialize CKKS parameters | ||
let params = CkksParameters::new(4096, 1000000000000007); | ||
let keygen = ckks_engine::KeyGenerator::new(); // No arguments required | ||
let keygen = ckks_engine::KeyGenerator::new(); | ||
let (public_key, secret_key) = keygen.generate_keys(); | ||
|
||
let encryptor = CKKSEncryptor::new(public_key.clone(), params.clone()); | ||
let decryptor = CKKSDecryptor::new(secret_key.clone(), params.clone()); | ||
|
||
const SCALE: f64 = 1e7; // Scaling factor for encoding | ||
|
||
// Step 1: Define transactions (plaintext) | ||
let transactions = vec![500.69, -200.0, 300.0]; // Example transaction amounts | ||
let labels = vec!["Deposit: +500", "Withdrawal: -200", "Deposit: +300"]; // Corresponding labels | ||
println!("Current working directory: {:?}", std::env::current_dir()?); | ||
|
||
// Step 2: Encrypt transaction amounts | ||
let encrypted_transactions: Vec<Polynomial> = transactions | ||
.iter() | ||
.map(|&value| encode(&[value], SCALE)) | ||
.collect(); | ||
// File path to the dataset | ||
let file_path = "transactions_2024.csv"; | ||
|
||
|
||
// Specify the year and month to analyze | ||
let year = 2024; | ||
let month = 7; // For July | ||
|
||
// Step 1: Read and filter dataset | ||
let (filtered_amounts, filtered_types) = read_and_filter_csv(file_path, year, month)?; | ||
|
||
// Step 3: Encrypt transaction labels | ||
let encrypted_labels: Vec<Polynomial> = labels | ||
// Step 2: Encrypt amounts | ||
let encrypted_transactions: Vec<Polynomial> = filtered_amounts | ||
.iter() | ||
.map(|label| { | ||
// Encode the label string as bytes and then encode as a polynomial | ||
let bytes = label.as_bytes(); | ||
let float_vals: Vec<f64> = bytes.iter().map(|&b| b as f64).collect(); | ||
encode(&float_vals, SCALE) | ||
}) | ||
.map(|&amount| encryptor.encrypt_value(amount)) // Encrypt directly using `f64` | ||
.collect(); | ||
|
||
// Step 4: Compute the total balance using homomorphic addition | ||
let total_balance_encrypted = encrypted_transactions | ||
.iter() | ||
.cloned() | ||
.reduce(|acc, value| encryptor.homomorphic_add(&acc, &value)) | ||
.unwrap(); | ||
|
||
// Step 5: Round the total balance for reporting | ||
let rounded_balance_encrypted = encryptor.homomorphic_round(&total_balance_encrypted); | ||
// Step 3: Calculate total Debit and Credit sums | ||
let mut total_debit_encrypted = encode(&[0.0], SCALE); | ||
let mut total_credit_encrypted = encode(&[0.0], SCALE); | ||
|
||
// Step 6: Concatenate encrypted labels | ||
let concatenated_labels_encrypted = encrypted_labels | ||
.iter() | ||
.cloned() | ||
.reduce(|acc, label| encryptor.concatenate_encrypted_strings(&acc, &label)) | ||
.unwrap(); | ||
for (encrypted_amount, transaction_type) in encrypted_transactions.iter().zip(filtered_types.iter()) { | ||
if transaction_type == "Debit" { | ||
total_debit_encrypted = encryptor.homomorphic_add(&total_debit_encrypted, encrypted_amount); | ||
} else if transaction_type == "Credit" { | ||
total_credit_encrypted = encryptor.homomorphic_add(&total_credit_encrypted, encrypted_amount); | ||
} | ||
} | ||
|
||
// Step 7: Decrypt and decode results | ||
let total_balance = decode(&total_balance_encrypted, SCALE); | ||
let rounded_balance = decode(&rounded_balance_encrypted, SCALE); | ||
// Step 4: Decrypt results | ||
let total_debit: Vec<f64> = decryptor.decrypt(&total_debit_encrypted); // Decrypt | ||
let total_credit: Vec<f64> = decryptor.decrypt(&total_credit_encrypted); // Decrypt | ||
|
||
// Decode concatenated labels back into plaintext | ||
let concatenated_labels_plaintext: String = concatenated_labels_encrypted | ||
.coeffs | ||
.iter() | ||
.map(|&c| (c as u8) as char) | ||
.collect(); | ||
// Step 5: Count transactions | ||
let debit_count = filtered_types.iter().filter(|&t| t == "Debit").count(); | ||
let credit_count = filtered_types.iter().filter(|&t| t == "Credit").count(); | ||
|
||
// Step 6: Print results | ||
println!("Transaction Summary for {}/{}:", month, year); | ||
println!("Total Debit Transactions: {}, Sum: {:?}", debit_count, total_debit); | ||
println!("Total Credit Transactions: {}, Sum: {:?}", credit_count, total_credit); | ||
|
||
Ok(()) | ||
} | ||
|
||
// Function to read and filter CSV data for a given year and month | ||
// fn read_and_filter_csv(file_path: &str, year: i32, month: u32) -> Result<(Vec<f64>, Vec<String>), Box<dyn Error>> { | ||
// let mut reader = Reader::from_path(file_path)?; | ||
// let mut filtered_amounts = Vec::new(); | ||
// let mut filtered_types = Vec::new(); | ||
|
||
// for result in reader.records() { | ||
// let record = result?; | ||
// let date = NaiveDate::parse_from_str(&record[0], "%d/%m/%Y")?; | ||
// let transaction_type = record[1].to_string(); | ||
// let amount: f64 = record[2].parse()?; | ||
|
||
// if date.year() == year && date.month() == month { | ||
// filtered_amounts.push(amount); | ||
// filtered_types.push(transaction_type); | ||
// } | ||
// } | ||
|
||
// Ok((filtered_amounts, filtered_types)) | ||
// } | ||
|
||
|
||
fn read_and_filter_csv(file_path: &str, year: i32, month: u32) -> Result<(Vec<f64>, Vec<String>), Box<dyn Error>> { | ||
let mut reader = Reader::from_path(file_path)?; | ||
let mut filtered_amounts = Vec::new(); | ||
let mut filtered_types = Vec::new(); | ||
|
||
for result in reader.records() { | ||
let record = result?; | ||
println!("Processing record: {:?}", record); // Debug statement | ||
|
||
// Corrected date format | ||
if let (Ok(date), Ok(amount)) = ( | ||
NaiveDate::parse_from_str(&record[0], "%Y-%m-%d"), | ||
record[2].parse::<f64>(), | ||
) { | ||
if date.year() == year && date.month() == month { | ||
filtered_amounts.push(amount); | ||
filtered_types.push(record[1].to_string()); | ||
} | ||
} else { | ||
eprintln!("Skipping invalid row: {:?}", record); // Log invalid rows | ||
} | ||
} | ||
|
||
// Print the results | ||
println!("Total Balance: {:?}", total_balance); | ||
println!("Rounded Balance: {:?}", rounded_balance); | ||
println!("Transaction Labels: {:?}", concatenated_labels_plaintext); | ||
Ok((filtered_amounts, filtered_types)) | ||
} |
Oops, something went wrong.