diff --git a/coprocessor/fhevm-engine/stress-test-generator/data/json/batch_allow_handles.json b/coprocessor/fhevm-engine/stress-test-generator/data/json/batch_allow_handles.json new file mode 100644 index 000000000..06776ebac --- /dev/null +++ b/coprocessor/fhevm-engine/stress-test-generator/data/json/batch_allow_handles.json @@ -0,0 +1,18 @@ +[ + { + "transaction": "BatchAllowHandles", + "kind": "Rate", + "variant": "NoCMUX", + "inputs": "NA", + "is_dependent": "Independent", + "contract_address": "0xa5880e99d86F081E8D3868A8C4732C8f65dfdB08", + "user_address": "0xa0534e99d86F081E8D3868A8C4732C8f65dfdB07", + "batch_size": 400, + "scenario": [ + [ + 1.0, + 600 + ] + ] + } +] \ No newline at end of file diff --git a/coprocessor/fhevm-engine/stress-test-generator/data/json/batch_bids_auction.json b/coprocessor/fhevm-engine/stress-test-generator/data/json/batch_bids_auction.json new file mode 100644 index 000000000..e945a117c --- /dev/null +++ b/coprocessor/fhevm-engine/stress-test-generator/data/json/batch_bids_auction.json @@ -0,0 +1,18 @@ +[ + { + "transaction": "BatchSubmitEncryptedBids", + "kind": "Rate", + "variant": "NoCMUX", + "inputs": "ReuseInputs", + "is_dependent": "Independent", + "contract_address": "0xa5880e99d86F081E8D3868A8C4732C8f65dfdB08", + "user_address": "0xa0534e99d86F081E8D3868A8C4732C8f65dfdB07", + "batch_size": 1, + "scenario": [ + [ + 2.0, + 1200 + ] + ] + } +] \ No newline at end of file diff --git a/coprocessor/fhevm-engine/stress-test-generator/data/json/batch_input_proofs.json b/coprocessor/fhevm-engine/stress-test-generator/data/json/batch_input_proofs.json new file mode 100644 index 000000000..b0e531981 --- /dev/null +++ b/coprocessor/fhevm-engine/stress-test-generator/data/json/batch_input_proofs.json @@ -0,0 +1,18 @@ +[ + { + "transaction": "BatchInputProofs", + "kind": "Rate", + "variant": "NoCMUX", + "inputs": "NA", + "is_dependent": "Independent", + "contract_address": "0xa5880e99d86F081E8D3868A8C4732C8f65dfdB08", + "user_address": "0xa0534e99d86F081E8D3868A8C4732C8f65dfdB07", + "batch_size": 4000, + "scenario": [ + [ + 1.0, + 600 + ] + ] + } +] \ No newline at end of file diff --git a/coprocessor/fhevm-engine/stress-test-generator/data/json/minitest_002_erc20.json b/coprocessor/fhevm-engine/stress-test-generator/data/json/minitest_002_erc20.json index 65dae4f24..37437eef3 100644 --- a/coprocessor/fhevm-engine/stress-test-generator/data/json/minitest_002_erc20.json +++ b/coprocessor/fhevm-engine/stress-test-generator/data/json/minitest_002_erc20.json @@ -9,8 +9,8 @@ "user_address": "0xa0534e99d86F081E8D3868A8C4732C8f65dfdB07", "scenario": [ [ - 20.0, - 120 + 1.0, + 1 ] ] } diff --git a/coprocessor/fhevm-engine/stress-test-generator/src/auction.rs b/coprocessor/fhevm-engine/stress-test-generator/src/auction.rs new file mode 100644 index 000000000..151a5a352 --- /dev/null +++ b/coprocessor/fhevm-engine/stress-test-generator/src/auction.rs @@ -0,0 +1,469 @@ +use std::collections::BTreeMap; +use std::sync::{Arc, OnceLock}; + +use fhevm_engine_common::types::AllowEvents; +use host_listener::contracts::{TfheContract, TfheContract::TfheContractEvents}; +use host_listener::database::tfhe_event_propagate::{ + Database as ListenerDatabase, Handle, ScalarByte, +}; +use tokio::sync::RwLock; +use tracing::info; + +use crate::utils::{ + allow_handle, generate_trivial_encrypt, insert_tfhe_event, new_transaction_id, + next_random_handle, tfhe_event, Context, FheType, DEF_TYPE, +}; + +#[derive(Clone, Copy)] +pub struct BidEntry { + pub e_amount: Handle, + pub e_paid: Handle, + pub price: u64, +} + +pub struct ContractState { + pub bids_submitted: Vec, + pub e_total_requested_amount_by_price: BTreeMap, + pub e_requested_amount_by_token_and_price: BTreeMap, +} + +pub static CONTRACT_STATE: OnceLock>> = OnceLock::new(); + +#[allow(clippy::too_many_arguments)] +pub async fn batch_submit_encrypted_bids( + ctx: &Context, + tx: &mut sqlx::Transaction<'_, sqlx::Postgres>, + listener_event_to_db: &ListenerDatabase, + transaction_id: Option, + user_address: &str, + payment_token_address: &str, + bids: &[Option], +) -> Result> { + let caller = user_address.parse().unwrap(); + let transaction_id = transaction_id.unwrap_or(new_transaction_id()); + + let _state = CONTRACT_STATE.get_or_init(|| { + Arc::new(RwLock::new(ContractState { + bids_submitted: Vec::new(), + e_total_requested_amount_by_price: BTreeMap::new(), + e_requested_amount_by_token_and_price: BTreeMap::new(), + })) + }); + + // euint64 eTotalPaymentValue = FHE.asEuint64(0); + let mut e_total_payment_value = generate_trivial_encrypt( + tx, + user_address, + user_address, + transaction_id, + listener_event_to_db, + Some(DEF_TYPE), + Some(0), + false, + ) + .await?; + + let mut user_submitted_bids = vec![]; + + for e_amount in bids.iter() { + let bid_price = 1; // rand::random_range(1..=1000); + + let (e_paid, e_amount, price) = process_bid_entry( + ctx, + tx, + e_amount.expect("should be a valid bid"), + bid_price, + transaction_id, + listener_event_to_db, + user_address, + ) + .await?; + + /* + eTotalPaymentValue = FHE.add( + eTotalPaymentValue, + _processBidEntry(eAmount, _inputBids[i].price, _paymentTokenAddress) + ); + */ + let result_handle = next_random_handle(DEF_TYPE); + let event = tfhe_event(TfheContractEvents::FheAdd(TfheContract::FheAdd { + caller, + lhs: e_total_payment_value, + rhs: e_paid, + result: result_handle, + scalarByte: ScalarByte::from(false as u8), + })); + insert_tfhe_event(tx, listener_event_to_db, transaction_id, event, false).await?; + e_total_payment_value = result_handle; + + user_submitted_bids.push(BidEntry { + e_amount, + e_paid, + price, + }); + } + + let e_is_payment_confirmed = process_batch_payment( + ctx, + tx, + transaction_id, + listener_event_to_db, + user_address, + payment_token_address, + e_total_payment_value, + ) + .await?; + + // Confirm and finalize each bid based on the payment result + for bid_entry in user_submitted_bids.iter() { + confirm_and_finalize_bid( + tx, + transaction_id, + listener_event_to_db, + bid_entry, + user_address, + e_is_payment_confirmed, + payment_token_address, + ) + .await?; + } + + Ok(e_is_payment_confirmed) +} +// +// eAmount = FHE.select( +// FHE.le(eAmount, FHE.asEuint64(auctionConfig.zamaTokenSupply)), +// eAmount, +// FHE.asEuint64(0), +// ); +// ePaid = FHE.mul(eAmount, FHE.asEuint64(price)); +// +#[allow(clippy::too_many_arguments)] +pub async fn process_bid_entry( + _ctx: &Context, + tx: &mut sqlx::Transaction<'_, sqlx::Postgres>, + mut e_amount: Handle, + price: u64, + transaction_id: Handle, + listener_event_to_db: &ListenerDatabase, + user_address: &str, +) -> Result<(Handle, Handle, u64), Box> { + let caller = user_address.parse().unwrap(); + info!(target: "tool", "Process Bid Entry: tx_id: {:?}", transaction_id); + + let total_supply = generate_trivial_encrypt( + tx, + user_address, + user_address, + transaction_id, + listener_event_to_db, + Some(DEF_TYPE), + Some(1_000_000), + false, + ) + .await?; + + let less_than_total_supply = next_random_handle(FheType::FheBool); + let event = tfhe_event(TfheContractEvents::FheLe(TfheContract::FheLe { + caller, + lhs: e_amount, + rhs: total_supply, + result: less_than_total_supply, + scalarByte: ScalarByte::from(false as u8), + })); + insert_tfhe_event(tx, listener_event_to_db, transaction_id, event, false).await?; + + let zero = generate_trivial_encrypt( + tx, + user_address, + user_address, + transaction_id, + listener_event_to_db, + Some(DEF_TYPE), + Some(0), + false, + ) + .await?; + + let result_handle = next_random_handle(DEF_TYPE); + let event = tfhe_event(TfheContractEvents::FheIfThenElse( + TfheContract::FheIfThenElse { + caller, + control: less_than_total_supply, + ifTrue: e_amount, + ifFalse: zero, + result: result_handle, + }, + )); + + insert_tfhe_event(tx, listener_event_to_db, transaction_id, event, false).await?; + e_amount = result_handle; + + let e_price = generate_trivial_encrypt( + tx, + user_address, + user_address, + transaction_id, + listener_event_to_db, + Some(DEF_TYPE), + Some(price as u128), + false, + ) + .await?; + + let e_paid = next_random_handle(DEF_TYPE); + + let event = tfhe_event(TfheContractEvents::FheMul(TfheContract::FheMul { + caller, + lhs: e_amount, + rhs: e_price, + result: e_paid, + scalarByte: ScalarByte::from(false as u8), + })); + insert_tfhe_event(tx, listener_event_to_db, transaction_id, event, false).await?; + + Ok((e_paid, e_amount, price)) +} + +// euint64 eTotalPaid = IERC7984(paymentTokenAddress).confidentialTransferFrom( +// msg.sender, +// address(this), +// eTotalValue +// ); +// FHE.allow(eTotalPaid, auctionConfig.complianceAddress); +// eIsPaymentConfirmed = FHE.eq(eTotalPaid, eTotalValue); +pub async fn process_batch_payment( + ctx: &Context, + tx: &mut sqlx::Transaction<'_, sqlx::Postgres>, + transaction_id: Handle, + listener_event_to_db: &ListenerDatabase, + user_address: &str, + payment_token_address: &str, + e_total_value: Handle, +) -> Result> { + let caller = user_address.parse().unwrap(); + info!(target: "tool", "Process Batch Payment: tx_id: {:?}", transaction_id); + + let e_total_paid = crate::erc7984::confidential_transfer_from( + ctx, + tx, + transaction_id, + listener_event_to_db, + e_total_value, + user_address, + ) + .await?; + + allow_handle( + tx, + &e_total_paid.to_vec(), + AllowEvents::AllowedAccount, + payment_token_address.to_string(), + transaction_id, + ) + .await?; + + let e_is_payment_confirmed = next_random_handle(FheType::FheBool); + let event = tfhe_event(TfheContractEvents::FheEq(TfheContract::FheEq { + caller, + lhs: e_total_paid, + rhs: e_total_value, + result: e_is_payment_confirmed, + scalarByte: ScalarByte::from(false as u8), + })); + insert_tfhe_event(tx, listener_event_to_db, transaction_id, event, false).await?; + + Ok(e_is_payment_confirmed) +} + +pub async fn confirm_and_finalize_bid( + tx: &mut sqlx::Transaction<'_, sqlx::Postgres>, + transaction_id: Handle, + listener_event_to_db: &ListenerDatabase, + bid_entry: &BidEntry, + user_address: &str, + e_is_payment_confirmed: Handle, + _payment_token_address: &str, +) -> Result<(), Box> { + let mut bid_entry = *bid_entry; + let caller = user_address.parse().unwrap(); + info!(target: "tool", "Confirm and Finalize Bid: tx_id: {:?}", transaction_id); + + let zero = generate_trivial_encrypt( + tx, + user_address, + user_address, + transaction_id, + listener_event_to_db, + Some(DEF_TYPE), + Some(0), + false, + ) + .await?; + + /* + Bid storage _bid = _bids[bidId]; + _bid.eAmount = FHE.select(eIsPaymentConfirmed, _bid.eAmount, FHE.asEuint64(0)); + _bid.ePaid = FHE.select(eIsPaymentConfirmed, _bid.ePaid, FHE.asEuint64(0)); + */ + + let result_handle = next_random_handle(DEF_TYPE); + let event = tfhe_event(TfheContractEvents::FheIfThenElse( + TfheContract::FheIfThenElse { + caller, + control: e_is_payment_confirmed, + ifTrue: bid_entry.e_amount, + ifFalse: zero, + result: result_handle, + }, + )); + insert_tfhe_event(tx, listener_event_to_db, transaction_id, event, true).await?; + bid_entry.e_amount = result_handle; + + let result_handle = next_random_handle(DEF_TYPE); + let event = tfhe_event(TfheContractEvents::FheIfThenElse( + TfheContract::FheIfThenElse { + caller, + control: e_is_payment_confirmed, + ifTrue: bid_entry.e_paid, + ifFalse: zero, + result: result_handle, + }, + )); + insert_tfhe_event(tx, listener_event_to_db, transaction_id, event, true).await?; + bid_entry.e_paid = result_handle; + + // Update contract state + + // FHE.allowThis(updatedTotalAmountByTokenPrice); + { + let mut state_guard = CONTRACT_STATE.get().unwrap().write().await; + + // euint64 updatedTotalAmount = _eTotalRequestedAmountByPrice[_bid.price]; + // updatedTotalAmount = FHE.isInitialized(updatedTotalAmount) + // ? FHE.add(updatedTotalAmount, _bid.eAmount) + // : _bid.eAmount; + // _eTotalRequestedAmountByPrice[_bid.price] = updatedTotalAmount; + + let mut event = None; + state_guard + .e_total_requested_amount_by_price + .entry(bid_entry.price) + .and_modify(|updated_total_amount| { + let result_handle = next_random_handle(DEF_TYPE); + event = Some(tfhe_event(TfheContractEvents::FheAdd( + TfheContract::FheAdd { + caller, + lhs: *updated_total_amount, + rhs: bid_entry.e_amount, + result: result_handle, + scalarByte: ScalarByte::from(false as u8), + }, + ))); + + info!(target: "tool", "modify e_total_requested_amount_by_price, handle: {:?}", hex::encode(result_handle)); + + *updated_total_amount = result_handle; + }) + .or_insert_with(|| bid_entry.e_amount); + + if let Some(event) = event { + insert_tfhe_event(tx, listener_event_to_db, transaction_id, event, true) + .await + .unwrap(); + } + + let updated_total_amount = state_guard + .e_total_requested_amount_by_price + .get(&bid_entry.price) + .expect("should be valid handle") + .to_vec(); + + // FHE.allowThis(updatedTotalAmount); + allow_handle( + tx, + &updated_total_amount, + AllowEvents::AllowedAccount, + user_address.to_string(), + transaction_id, + ) + .await?; + + // Tracks the amount of tokens per payment token and price level to determine the amount to send to the treasury + // euint64 updatedTotalAmountByTokenPrice = _eRequestedAmountByTokenAndPrice[paymentTokenAddress][_bid.price]; + // updatedTotalAmountByTokenPrice = FHE.isInitialized(updatedTotalAmountByTokenPrice) + // ? FHE.add(updatedTotalAmountByTokenPrice, _bid.eAmount) + // : _bid.eAmount; + // _eRequestedAmountByTokenAndPrice[paymentTokenAddress][_bid.price] = updatedTotalAmountByTokenPrice; + + let mut event = None; + state_guard + .e_requested_amount_by_token_and_price + .entry(bid_entry.price) + .and_modify(|updated_total_amount_by_token_price| { + let result_handle = next_random_handle(DEF_TYPE); + event = Some(tfhe_event(TfheContractEvents::FheAdd( + TfheContract::FheAdd { + caller, + lhs: *updated_total_amount_by_token_price, + rhs: bid_entry.e_amount, + result: result_handle, + scalarByte: ScalarByte::from(false as u8), + }, + ))); + + info!(target: "tool", "modify e_requested_amount_by_token_and_price, handle: {:?}", hex::encode(result_handle)); + + *updated_total_amount_by_token_price = result_handle; + }) + .or_insert_with(|| bid_entry.e_amount); + + if let Some(event) = event { + insert_tfhe_event(tx, listener_event_to_db, transaction_id, event, true) + .await + .unwrap(); + } + + let updated_total_amount_by_token_price = state_guard + .e_requested_amount_by_token_and_price + .get(&bid_entry.price) + .expect("should be valid handle") + .to_vec(); + + // FHE.allowThis(updatedTotalAmountByTokenPrice); + allow_handle( + tx, + &updated_total_amount_by_token_price, + AllowEvents::AllowedAccount, + user_address.to_string(), + transaction_id, + ) + .await?; + } + + /* + FHE.allow(_bid.eAmount, msg.sender); + FHE.allow(_bid.ePaid, msg.sender); + FHE.allow(_bid.eAmount, auctionConfig.complianceAddress); + FHE.allow(_bid.ePaid, auctionConfig.complianceAddress); + */ + allow_handle( + tx, + bid_entry.e_amount.to_vec().as_ref(), + AllowEvents::AllowedAccount, + user_address.to_string(), + transaction_id, + ) + .await?; + + allow_handle( + tx, + bid_entry.e_paid.to_vec().as_ref(), + AllowEvents::AllowedAccount, + user_address.to_string(), + transaction_id, + ) + .await?; + + Ok(()) +} diff --git a/coprocessor/fhevm-engine/stress-test-generator/src/bin/stress_generator.rs b/coprocessor/fhevm-engine/stress-test-generator/src/bin/stress_generator.rs index e94286e3b..f427be10b 100644 --- a/coprocessor/fhevm-engine/stress-test-generator/src/bin/stress_generator.rs +++ b/coprocessor/fhevm-engine/stress-test-generator/src/bin/stress_generator.rs @@ -9,7 +9,7 @@ use fhevm_engine_common::utils::DatabaseURL; use host_listener::database::tfhe_event_propagate::{Database as ListenerDatabase, Handle}; use sqlx::Postgres; -use std::io::Write; +use std::{cmp::min, io::Write}; use std::{collections::HashMap, fmt, sync::atomic::AtomicU64}; use std::{ ops::{Add, Sub}, @@ -19,16 +19,26 @@ use std::{ sync::atomic::Ordering, time::{Duration, SystemTime}, }; -use stress_test_generator::utils::{ - default_dependence_cache_size, get_ciphertext_digests, Dependence, GeneratorKind, Transaction, +use stress_test_generator::{ + args::parse_args, dex::dex_swap_claim_transaction, utils::new_transaction_id, + zk_gen::generate_and_insert_inputs_batch, +}; +use stress_test_generator::{ + auction::batch_submit_encrypted_bids, + zk_gen::{generate_input_verification_transaction, get_inputs_vector}, }; -use stress_test_generator::zk_gen::{generate_input_verification_transaction, get_inputs_vector}; -use stress_test_generator::{args::parse_args, dex::dex_swap_claim_transaction}; use stress_test_generator::{ dex::dex_swap_request_transaction, erc20::erc20_transaction, utils::{EnvConfig, Job, Scenario}, }; +use stress_test_generator::{ + erc7984, + utils::{ + allow_handles, default_dependence_cache_size, get_ciphertext_digests, next_random_handle, + Dependence, GeneratorKind, Transaction, DEF_TYPE, + }, +}; use stress_test_generator::{ synthetics::{ add_chain_transaction, generate_pub_decrypt_handles_types, @@ -41,6 +51,7 @@ use tokio_util::sync::CancellationToken; use tracing::{error, info, warn}; const MAX_RETRIES: usize = 500; +const MAX_NUMBER_OF_BIDS: usize = 10; #[tokio::main] async fn main() { @@ -56,6 +67,7 @@ async fn main() { args: args.clone(), ecfg: EnvConfig::new(), cancel_token: CancellationToken::new(), + inputs_pool: vec![], }; if args.run_server { @@ -425,11 +437,7 @@ async fn generate_transactions_at_rate( default_dependence_cache_size(), ) .await?; - let pool = sqlx::postgres::PgPoolOptions::new() - .max_connections(2) - .connect(database_url.as_str()) - .await - .unwrap(); + let mut dependence_handle1: Option = None; let mut dependence_handle2: Option = None; for (target_throughput, duration_seconds) in scenario.scenario.iter() { @@ -469,7 +477,6 @@ async fn generate_transactions_at_rate( dependence_handle1, dependence_handle2, &mut listener_event_to_db, - &pool, ) .await?; @@ -507,11 +514,6 @@ async fn generate_transactions_count( default_dependence_cache_size(), ) .await?; - let pool = sqlx::postgres::PgPoolOptions::new() - .max_connections(2) - .connect(database_url.as_str()) - .await - .unwrap(); let mut dependence_handle1: Option = None; let mut dependence_handle2: Option = None; @@ -531,7 +533,6 @@ async fn generate_transactions_count( dependence_handle1, dependence_handle2, &mut listener_event_to_db, - &pool, ) .await?; if scenario.is_dependent == Dependence::Dependent { @@ -549,7 +550,6 @@ async fn generate_transaction( dependence1: Option, dependence2: Option, listener_event_to_db: &mut ListenerDatabase, - pool: &sqlx::Pool, ) -> Result<(Handle, Handle), Box> { let ecfg = EnvConfig::new(); let inputs = get_inputs_vector( @@ -584,26 +584,95 @@ async fn generate_transaction( }, }; + let mut new_ctx = ctx.clone(); + new_ctx.inputs_pool = inputs.clone(); + let ctx = &new_ctx; + + let mut tx: sqlx::Transaction<'_, Postgres> = listener_event_to_db.new_transaction().await?; + match scenario.transaction { + Transaction::BatchInputProofs => { + let batch_size = scenario.batch_size.unwrap_or(1); + generate_and_insert_inputs_batch( + ctx, + &mut tx, + listener_event_to_db, + batch_size, + MAX_NUMBER_OF_BIDS as u8, + &scenario.contract_address, + &scenario.user_address, + ) + .await?; + + Ok((Handle::default(), Handle::default())) + } + Transaction::BatchSubmitEncryptedBids => { + let batch_size = min(MAX_NUMBER_OF_BIDS, scenario.batch_size.unwrap_or(1)); + + // reuse the existing inputs as bids + let bids = inputs + .iter() + .take(batch_size) + .copied() + .collect::>>(); + + let e_total_payment = batch_submit_encrypted_bids( + ctx, + &mut tx, + listener_event_to_db, + None, // Transaction ID + &scenario.contract_address, + &scenario.user_address, + &bids, + ) + .await?; + + tx.commit().await?; + + Ok((e_total_payment, e_total_payment)) + } + Transaction::BatchAllowHandles => { + let mut handles = Vec::new(); + for _ in 0..scenario.batch_size.unwrap_or(1) { + handles.push(next_random_handle(DEF_TYPE).to_vec()); + } + + info!(target: "tool", batch_size = handles.len(), "Batch allowing handles"); + + allow_handles( + &mut tx, + &handles, + fhevm_engine_common::types::AllowEvents::AllowedAccount, + scenario.user_address.to_string(), + true, + ) + .await?; + + tx.commit().await?; + + Ok((Handle::default(), Handle::default())) + } Transaction::ERC20Transfer => { let (_, output_dependence) = erc20_transaction( ctx, + &mut tx, inputs[0], dependence1, inputs[1], None, // Transaction ID listener_event_to_db, - pool, scenario.variant.to_owned(), &scenario.contract_address, &scenario.user_address, ) .await?; + tx.commit().await?; Ok((output_dependence, output_dependence)) } Transaction::DEXSwapRequest => { let (new_current_balance_0, new_current_balance_1) = dex_swap_request_transaction( ctx, + &mut tx, inputs[0], inputs[1], dependence1, @@ -615,17 +684,18 @@ async fn generate_transaction( inputs[6], inputs[7], listener_event_to_db, - pool, scenario.variant.to_owned(), &scenario.contract_address, &scenario.user_address, ) .await?; + tx.commit().await?; Ok((new_current_balance_0, new_current_balance_1)) } Transaction::DEXSwapClaim => { let (new_current_balance_0, new_current_balance_1) = dex_swap_claim_transaction( ctx, + &mut tx, inputs[0], inputs[1], rand::random::(), @@ -637,42 +707,44 @@ async fn generate_transaction( dependence1, dependence2, listener_event_to_db, - pool, scenario.variant.to_owned(), &scenario.contract_address, &scenario.user_address, ) .await?; + tx.commit().await?; Ok((new_current_balance_0, new_current_balance_1)) } Transaction::ADDChain => { let (output_dependence1, output_dependence2) = add_chain_transaction( ctx, + &mut tx, dependence1, inputs[1], ecfg.synthetic_chain_length, None, // Transaction ID listener_event_to_db, - pool, &scenario.contract_address, &scenario.user_address, ) .await?; + tx.commit().await?; Ok((output_dependence1, output_dependence2)) } Transaction::MULChain => { let (output_dependence1, output_dependence2) = mul_chain_transaction( ctx, + &mut tx, dependence1, inputs[1], ecfg.synthetic_chain_length, None, // Transaction ID listener_event_to_db, - pool, &scenario.contract_address, &scenario.user_address, ) .await?; + tx.commit().await?; Ok((output_dependence1, output_dependence2)) } Transaction::InputVerif => { @@ -688,11 +760,11 @@ async fn generate_transaction( } Transaction::GenPubDecHandles => { let (output_dependence1, output_dependence2) = generate_pub_decrypt_handles_types( + &mut tx, ecfg.min_decryption_type, ecfg.max_decryption_type, None, // Transaction ID listener_event_to_db, - pool, &scenario.contract_address, &scenario.user_address, ) @@ -701,16 +773,39 @@ async fn generate_transaction( } Transaction::GenUsrDecHandles => { let (output_dependence1, output_dependence2) = generate_user_decrypt_handles_types( + &mut tx, ecfg.min_decryption_type, ecfg.max_decryption_type, None, // Transaction ID listener_event_to_db, - pool, &scenario.contract_address, &scenario.user_address, ) .await?; + tx.commit().await?; Ok((output_dependence1, output_dependence2)) } + Transaction::ERC7984Transfer => { + let transaction_id = new_transaction_id(); + let e_amount = inputs + .first() + .unwrap() + .expect("should be at least one input available"); + + info!(target: "tool", "ERC7984 Transaction: tx_id: {:?}", transaction_id); + let e_total_paid = erc7984::confidential_transfer_from( + ctx, + &mut tx, + transaction_id, + listener_event_to_db, + e_amount, + scenario.user_address.as_str(), + ) + .await?; + + tx.commit().await?; + + Ok((e_total_paid, e_total_paid)) + } } } diff --git a/coprocessor/fhevm-engine/stress-test-generator/src/dex.rs b/coprocessor/fhevm-engine/stress-test-generator/src/dex.rs index 699339748..dfad420af 100644 --- a/coprocessor/fhevm-engine/stress-test-generator/src/dex.rs +++ b/coprocessor/fhevm-engine/stress-test-generator/src/dex.rs @@ -1,27 +1,25 @@ -use alloy_primitives::Address; -use fhevm_engine_common::types::AllowEvents; -use host_listener::contracts::{TfheContract, TfheContract::TfheContractEvents}; -use host_listener::database::tfhe_event_propagate::{ - Database as ListenerDatabase, Handle, ScalarByte, -}; -use sqlx::Postgres; - use crate::erc20::erc20_transaction; use crate::utils::{ allow_handle, generate_trivial_encrypt, insert_tfhe_event, next_random_handle, tfhe_event, Context, ERCTransferVariant, DEF_TYPE, }; use crate::zk_gen::generate_random_handle_amount_if_none; +use alloy_primitives::Address; +use fhevm_engine_common::types::AllowEvents; +use host_listener::contracts::{TfheContract, TfheContract::TfheContractEvents}; +use host_listener::database::tfhe_event_propagate::{ + Database as ListenerDatabase, Handle, ScalarByte, +}; #[allow(clippy::too_many_arguments)] async fn dex_swap_request_update_dex_balance( ctx: &Context, + tx: &mut sqlx::Transaction<'_, sqlx::Postgres>, from_balance: Option, current_dex_balance: Option, amount: Option, transaction_id: Handle, - listener_event_to_db: &mut ListenerDatabase, - pool: &sqlx::Pool, + listener_event_to_db: &ListenerDatabase, variant: ERCTransferVariant, contract_address: &String, user_address: &String, @@ -41,12 +39,12 @@ async fn dex_swap_request_update_dex_balance( generate_random_handle_amount_if_none(ctx, amount, contract_address, user_address).await?; let (_, new_current_balance) = erc20_transaction( ctx, + tx, Some(from_balance), Some(current_dex_balance), Some(amount), Some(transaction_id), listener_event_to_db, - pool, variant, contract_address, user_address, @@ -60,19 +58,19 @@ async fn dex_swap_request_update_dex_balance( result: sent_amount, scalarByte: ScalarByte::from(false as u8), })); - insert_tfhe_event(listener_event_to_db, transaction_id, event, false).await?; + insert_tfhe_event(tx, listener_event_to_db, transaction_id, event, false).await?; Ok((sent_amount, new_current_balance)) } #[allow(clippy::too_many_arguments)] async fn dex_swap_request_finalize( ctx: &Context, + tx: &mut sqlx::Transaction<'_, sqlx::Postgres>, to_balance: Option, total_dex_token_in: Option, sent: Option, transaction_id: Handle, - listener_event_to_db: &mut ListenerDatabase, - _pool: &sqlx::Pool, + listener_event_to_db: &ListenerDatabase, contract_address: &String, user_address: &String, ) -> Result<(Handle, Handle), Box> { @@ -97,7 +95,7 @@ async fn dex_swap_request_finalize( result: pending_in, scalarByte: ScalarByte::from(false as u8), })); - insert_tfhe_event(listener_event_to_db, transaction_id, event, true).await?; + insert_tfhe_event(tx, listener_event_to_db, transaction_id, event, true).await?; let pending_total_token_in = next_random_handle(DEF_TYPE); let event = tfhe_event(TfheContractEvents::FheAdd(TfheContract::FheAdd { caller, @@ -106,13 +104,14 @@ async fn dex_swap_request_finalize( result: pending_total_token_in, scalarByte: ScalarByte::from(false as u8), })); - insert_tfhe_event(listener_event_to_db, transaction_id, event, true).await?; + insert_tfhe_event(tx, listener_event_to_db, transaction_id, event, true).await?; Ok((pending_in, pending_total_token_in)) } #[allow(clippy::too_many_arguments)] pub async fn dex_swap_request_transaction( ctx: &Context, + tx: &mut sqlx::Transaction<'_, sqlx::Postgres>, from_balance_0: Option, from_balance_1: Option, current_balance_0: Option, @@ -123,8 +122,7 @@ pub async fn dex_swap_request_transaction( total_token_1: Option, amount_0: Option, amount_1: Option, - listener_event_to_db: &mut ListenerDatabase, - pool: &sqlx::Pool, + listener_event_to_db: &ListenerDatabase, variant: ERCTransferVariant, contract_address: &String, user_address: &String, @@ -171,12 +169,12 @@ pub async fn dex_swap_request_transaction( let (sent_0, new_current_balance_0) = dex_swap_request_update_dex_balance( ctx, + tx, Some(from_balance_0), Some(current_balance_0), Some(amount_0), transaction_id, listener_event_to_db, - pool, variant.to_owned(), contract_address, user_address, @@ -184,12 +182,12 @@ pub async fn dex_swap_request_transaction( .await?; let (sent_1, new_current_balance_1) = dex_swap_request_update_dex_balance( ctx, + tx, Some(from_balance_1), Some(current_balance_1), Some(amount_1), transaction_id, listener_event_to_db, - pool, variant.to_owned(), contract_address, user_address, @@ -198,74 +196,74 @@ pub async fn dex_swap_request_transaction( let (pending_in_0, pending_total_token_in_0) = dex_swap_request_finalize( ctx, + tx, Some(to_balance_0), Some(total_token_0), Some(sent_0), transaction_id, listener_event_to_db, - pool, contract_address, user_address, ) .await?; let (pending_in_1, pending_total_token_in_1) = dex_swap_request_finalize( ctx, + tx, Some(to_balance_1), Some(total_token_1), Some(sent_1), transaction_id, listener_event_to_db, - pool, contract_address, user_address, ) .await?; allow_handle( + tx, &pending_in_0.to_vec(), AllowEvents::AllowedForDecryption, contract_address.to_string(), transaction_id, - pool, ) .await?; allow_handle( + tx, &pending_in_1.to_vec(), AllowEvents::AllowedForDecryption, contract_address.to_string(), transaction_id, - pool, ) .await?; allow_handle( + tx, &pending_total_token_in_0.to_vec(), AllowEvents::AllowedForDecryption, contract_address.to_string(), transaction_id, - pool, ) .await?; allow_handle( + tx, &pending_total_token_in_1.to_vec(), AllowEvents::AllowedForDecryption, contract_address.to_string(), transaction_id, - pool, ) .await?; allow_handle( + tx, &new_current_balance_0.to_vec(), AllowEvents::AllowedForDecryption, contract_address.to_string(), transaction_id, - pool, ) .await?; allow_handle( + tx, &new_current_balance_1.to_vec(), AllowEvents::AllowedForDecryption, contract_address.to_string(), transaction_id, - pool, ) .await?; Ok((new_current_balance_0, new_current_balance_1)) @@ -274,6 +272,7 @@ pub async fn dex_swap_request_transaction( #[allow(clippy::too_many_arguments)] async fn dex_swap_claim_prepare( ctx: &Context, + tx: &mut sqlx::Transaction<'_, sqlx::Postgres>, pending_0_in: Option, pending_1_in: Option, total_dex_token_0_in: u64, @@ -281,8 +280,7 @@ async fn dex_swap_claim_prepare( total_dex_token_0_out: u64, total_dex_token_1_out: u64, transaction_id: Handle, - listener_event_to_db: &mut ListenerDatabase, - _pool: &sqlx::Pool, + listener_event_to_db: &ListenerDatabase, _variant: ERCTransferVariant, contract_address: &String, user_address: &String, @@ -304,8 +302,9 @@ async fn dex_swap_claim_prepare( toType: crate::utils::FheType::FheUint128 as u8, result: big_pending_1_in, })); - insert_tfhe_event(listener_event_to_db, transaction_id, event, false).await?; + insert_tfhe_event(tx, listener_event_to_db, transaction_id, event, false).await?; let total_dex_token_0_out_te = generate_trivial_encrypt( + tx, contract_address, user_address, transaction_id, @@ -323,8 +322,9 @@ async fn dex_swap_claim_prepare( result: big_amount_0_out_mul, scalarByte: ScalarByte::from(false as u8), })); - insert_tfhe_event(listener_event_to_db, transaction_id, event, false).await?; + insert_tfhe_event(tx, listener_event_to_db, transaction_id, event, false).await?; let total_dex_token_1_in_te = generate_trivial_encrypt( + tx, contract_address, user_address, transaction_id, @@ -342,7 +342,7 @@ async fn dex_swap_claim_prepare( result: big_amount_0_out_div, scalarByte: ScalarByte::from(false as u8), })); - insert_tfhe_event(listener_event_to_db, transaction_id, event, false).await?; + insert_tfhe_event(tx, listener_event_to_db, transaction_id, event, false).await?; amount_0_out = next_random_handle(crate::utils::FheType::FheUint64); let event = tfhe_event(TfheContractEvents::Cast(TfheContract::Cast { caller, @@ -350,7 +350,7 @@ async fn dex_swap_claim_prepare( toType: crate::utils::FheType::FheUint64 as u8, result: amount_0_out, })); - insert_tfhe_event(listener_event_to_db, transaction_id, event, false).await?; + insert_tfhe_event(tx, listener_event_to_db, transaction_id, event, false).await?; } if total_dex_token_0_in != 0 { let big_pending_0_in = next_random_handle(crate::utils::FheType::FheUint128); @@ -360,8 +360,9 @@ async fn dex_swap_claim_prepare( toType: crate::utils::FheType::FheUint128 as u8, result: big_pending_0_in, })); - insert_tfhe_event(listener_event_to_db, transaction_id, event, false).await?; + insert_tfhe_event(tx, listener_event_to_db, transaction_id, event, false).await?; let total_dex_token_1_out_te = generate_trivial_encrypt( + tx, contract_address, user_address, transaction_id, @@ -379,8 +380,9 @@ async fn dex_swap_claim_prepare( result: big_amount_1_out_mul, scalarByte: ScalarByte::from(false as u8), })); - insert_tfhe_event(listener_event_to_db, transaction_id, event, false).await?; + insert_tfhe_event(tx, listener_event_to_db, transaction_id, event, false).await?; let total_dex_token_0_in_te = generate_trivial_encrypt( + tx, contract_address, user_address, transaction_id, @@ -398,7 +400,7 @@ async fn dex_swap_claim_prepare( result: big_amount_1_out_div, scalarByte: ScalarByte::from(false as u8), })); - insert_tfhe_event(listener_event_to_db, transaction_id, event, false).await?; + insert_tfhe_event(tx, listener_event_to_db, transaction_id, event, false).await?; amount_1_out = next_random_handle(crate::utils::FheType::FheUint64); let event = tfhe_event(TfheContractEvents::Cast(TfheContract::Cast { caller, @@ -406,7 +408,7 @@ async fn dex_swap_claim_prepare( toType: crate::utils::FheType::FheUint64 as u8, result: amount_1_out, })); - insert_tfhe_event(listener_event_to_db, transaction_id, event, false).await?; + insert_tfhe_event(tx, listener_event_to_db, transaction_id, event, false).await?; } Ok((amount_0_out, amount_1_out)) } @@ -414,13 +416,13 @@ async fn dex_swap_claim_prepare( #[allow(clippy::too_many_arguments)] async fn dex_swap_claim_update_dex_balance( ctx: &Context, + tx: &mut sqlx::Transaction<'_, sqlx::Postgres>, amount_out: Option, total_dex_other_token_in: u64, old_balance: Option, current_dex_balance: Option, transaction_id: Handle, - listener_event_to_db: &mut ListenerDatabase, - pool: &sqlx::Pool, + listener_event_to_db: &ListenerDatabase, variant: ERCTransferVariant, contract_address: &String, user_address: &String, @@ -443,12 +445,12 @@ async fn dex_swap_claim_update_dex_balance( if total_dex_other_token_in != 0 { (new_dex_balance, new_balance) = erc20_transaction( ctx, + tx, Some(current_dex_balance), Some(old_balance), Some(amount_out), Some(transaction_id), listener_event_to_db, - pool, variant, contract_address, user_address, @@ -461,6 +463,7 @@ async fn dex_swap_claim_update_dex_balance( #[allow(clippy::too_many_arguments)] pub async fn dex_swap_claim_transaction( ctx: &Context, + tx: &mut sqlx::Transaction<'_, sqlx::Postgres>, pending_0_in: Option, pending_1_in: Option, total_token_0_in: u64, @@ -471,8 +474,7 @@ pub async fn dex_swap_claim_transaction( old_balance_1: Option, current_balance_0: Option, current_balance_1: Option, - listener_event_to_db: &mut ListenerDatabase, - pool: &sqlx::Pool, + listener_event_to_db: &ListenerDatabase, variant: ERCTransferVariant, contract_address: &String, user_address: &String, @@ -507,6 +509,7 @@ pub async fn dex_swap_claim_transaction( let (amount_0_out, amount_1_out) = dex_swap_claim_prepare( ctx, + tx, Some(pending_0_in), Some(pending_1_in), total_token_0_in, @@ -515,7 +518,6 @@ pub async fn dex_swap_claim_transaction( total_token_1_out, transaction_id, listener_event_to_db, - pool, variant.to_owned(), contract_address, user_address, @@ -524,13 +526,13 @@ pub async fn dex_swap_claim_transaction( let (new_dex_balance_0, new_balance_0) = dex_swap_claim_update_dex_balance( ctx, + tx, Some(amount_0_out), total_token_1_in, Some(old_balance_0), Some(current_balance_0), transaction_id, listener_event_to_db, - pool, variant.to_owned(), contract_address, user_address, @@ -538,48 +540,48 @@ pub async fn dex_swap_claim_transaction( .await?; let (new_dex_balance_1, new_balance_1) = dex_swap_claim_update_dex_balance( ctx, + tx, Some(amount_1_out), total_token_0_in, Some(old_balance_1), Some(current_balance_1), transaction_id, listener_event_to_db, - pool, variant.to_owned(), contract_address, user_address, ) .await?; allow_handle( + tx, &new_balance_0.to_vec(), AllowEvents::AllowedForDecryption, contract_address.to_string(), transaction_id, - pool, ) .await?; allow_handle( + tx, &new_balance_1.to_vec(), AllowEvents::AllowedForDecryption, contract_address.to_string(), transaction_id, - pool, ) .await?; allow_handle( + tx, &new_dex_balance_0.to_vec(), AllowEvents::AllowedForDecryption, contract_address.to_string(), transaction_id, - pool, ) .await?; allow_handle( + tx, &new_dex_balance_1.to_vec(), AllowEvents::AllowedForDecryption, contract_address.to_string(), transaction_id, - pool, ) .await?; Ok((new_dex_balance_0, new_dex_balance_1)) diff --git a/coprocessor/fhevm-engine/stress-test-generator/src/erc20.rs b/coprocessor/fhevm-engine/stress-test-generator/src/erc20.rs index 1aa820e30..e148c5acd 100644 --- a/coprocessor/fhevm-engine/stress-test-generator/src/erc20.rs +++ b/coprocessor/fhevm-engine/stress-test-generator/src/erc20.rs @@ -7,26 +7,26 @@ use sqlx::Postgres; use tracing::{error, info}; use crate::utils::{ - allow_handle, insert_tfhe_event, next_random_handle, tfhe_event, Context, ERCTransferVariant, - FheType, DEF_TYPE, + allow_handle, insert_tfhe_event, new_transaction_id, next_random_handle, tfhe_event, Context, + ERCTransferVariant, FheType, DEF_TYPE, }; use crate::zk_gen::generate_random_handle_amount_if_none; #[allow(clippy::too_many_arguments)] pub async fn erc20_transaction( ctx: &Context, + tx: &mut sqlx::Transaction<'_, Postgres>, source: Option, destination: Option, amount: Option, transaction_id: Option, - listener_event_to_db: &mut ListenerDatabase, - pool: &sqlx::Pool, + listener_event_to_db: &ListenerDatabase, variant: ERCTransferVariant, contract_address: &String, user_address: &String, ) -> Result<(Handle, Handle), Box> { let caller = user_address.parse().unwrap(); - let transaction_id = transaction_id.unwrap_or(next_random_handle(DEF_TYPE)); + let transaction_id = transaction_id.unwrap_or(new_transaction_id()); info!(target: "tool", "ERC20 Transaction: tx_id: {:?}", transaction_id); @@ -54,7 +54,7 @@ pub async fn erc20_transaction( result: has_enough_funds, scalarByte: ScalarByte::from(false as u8), })); - insert_tfhe_event(listener_event_to_db, transaction_id, event, false).await?; + insert_tfhe_event(tx, listener_event_to_db, transaction_id, event, false).await?; let new_source = next_random_handle(DEF_TYPE); let new_destination = next_random_handle(DEF_TYPE); match variant { @@ -67,7 +67,7 @@ pub async fn erc20_transaction( result: new_destination_target, scalarByte: ScalarByte::from(false as u8), })); - insert_tfhe_event(listener_event_to_db, transaction_id, event, false).await?; + insert_tfhe_event(tx, listener_event_to_db, transaction_id, event, false).await?; let event = tfhe_event(TfheContractEvents::FheIfThenElse( TfheContract::FheIfThenElse { caller, @@ -77,13 +77,13 @@ pub async fn erc20_transaction( result: new_destination, }, )); - insert_tfhe_event(listener_event_to_db, transaction_id, event, true).await?; + insert_tfhe_event(tx, listener_event_to_db, transaction_id, event, true).await?; allow_handle( + tx, &new_destination.to_vec(), AllowEvents::AllowedForDecryption, contract_address.to_string(), transaction_id, - pool, ) .await?; let new_source_target = next_random_handle(DEF_TYPE); @@ -94,7 +94,7 @@ pub async fn erc20_transaction( result: new_source_target, scalarByte: ScalarByte::from(false as u8), })); - insert_tfhe_event(listener_event_to_db, transaction_id, event, false).await?; + insert_tfhe_event(tx, listener_event_to_db, transaction_id, event, false).await?; let event = tfhe_event(TfheContractEvents::FheIfThenElse( TfheContract::FheIfThenElse { caller, @@ -104,13 +104,13 @@ pub async fn erc20_transaction( result: new_source, }, )); - insert_tfhe_event(listener_event_to_db, transaction_id, event, true).await?; + insert_tfhe_event(tx, listener_event_to_db, transaction_id, event, true).await?; allow_handle( + tx, &new_source.to_vec(), AllowEvents::AllowedForDecryption, contract_address.to_string(), transaction_id, - pool, ) .await?; } @@ -122,7 +122,7 @@ pub async fn erc20_transaction( toType: 5u8, result: cast_has_enough_funds, })); - insert_tfhe_event(listener_event_to_db, transaction_id, event, false).await?; + insert_tfhe_event(tx, listener_event_to_db, transaction_id, event, false).await?; let select_amount = next_random_handle(DEF_TYPE); let event = tfhe_event(TfheContractEvents::FheMul(TfheContract::FheMul { caller, @@ -131,7 +131,7 @@ pub async fn erc20_transaction( result: select_amount, scalarByte: ScalarByte::from(false as u8), })); - insert_tfhe_event(listener_event_to_db, transaction_id, event, false).await?; + insert_tfhe_event(tx, listener_event_to_db, transaction_id, event, false).await?; let event = tfhe_event(TfheContractEvents::FheAdd(TfheContract::FheAdd { caller, lhs: destination, @@ -139,13 +139,13 @@ pub async fn erc20_transaction( result: new_destination, scalarByte: ScalarByte::from(false as u8), })); - insert_tfhe_event(listener_event_to_db, transaction_id, event, true).await?; + insert_tfhe_event(tx, listener_event_to_db, transaction_id, event, true).await?; allow_handle( + tx, &new_destination.to_vec(), AllowEvents::AllowedForDecryption, contract_address.to_string(), transaction_id, - pool, ) .await?; let event = tfhe_event(TfheContractEvents::FheSub(TfheContract::FheSub { @@ -155,13 +155,13 @@ pub async fn erc20_transaction( result: new_source, scalarByte: ScalarByte::from(false as u8), })); - insert_tfhe_event(listener_event_to_db, transaction_id, event, true).await?; + insert_tfhe_event(tx, listener_event_to_db, transaction_id, event, true).await?; allow_handle( + tx, &new_source.to_vec(), AllowEvents::AllowedForDecryption, contract_address.to_string(), transaction_id, - pool, ) .await?; } diff --git a/coprocessor/fhevm-engine/stress-test-generator/src/erc7984.rs b/coprocessor/fhevm-engine/stress-test-generator/src/erc7984.rs new file mode 100644 index 000000000..2ce15f4ba --- /dev/null +++ b/coprocessor/fhevm-engine/stress-test-generator/src/erc7984.rs @@ -0,0 +1,183 @@ +use crate::utils::{ + allow_handle, generate_trivial_encrypt, insert_tfhe_event, next_random_handle, tfhe_event, + Context, FheType, DEF_TYPE, +}; +use alloy_primitives::Address; +use fhevm_engine_common::types::AllowEvents; +use host_listener::{ + contracts::TfheContract::{self, TfheContractEvents}, + database::tfhe_event_propagate::{Database as ListenerDatabase, Handle, ScalarByte}, +}; + +/// Implements ERC-7984's confidential transfer function +/// see also: github.com/OpenZeppelin/openzeppelin-confidential-contracts/blob/master/contracts/token/ERC7984/ERC7984.sol +pub async fn confidential_transfer_from( + ctx: &Context, + tx: &mut sqlx::Transaction<'_, sqlx::Postgres>, + transaction_id: Handle, + db: &ListenerDatabase, + e_amount: Handle, + user_address: &str, +) -> Result> { + let caller: Address = user_address.parse().unwrap(); + + let balance_from = ctx + .inputs_pool + .first() + .unwrap() + .expect("should be at least one input available"); + + let balance_to = ctx + .inputs_pool + .get(1) + .unwrap() + .expect("should be at least two inputs available"); + + /* + euint64 fromBalance = _balances[from]; + require(FHE.isInitialized(fromBalance), ERC7984ZeroBalance(from)); + (success, ptr) = FHESafeMath.tryDecrease(fromBalance, amount); + FHE.allowThis(ptr); + FHE.allow(ptr, from); + _balances[from] = ptr; + */ + + let (success, ptr) = + try_decrease(tx, db, caller, transaction_id, balance_from, e_amount).await?; + + allow_handle( + tx, + &ptr.to_vec(), + AllowEvents::AllowedAccount, + user_address.to_string(), + transaction_id, + ) + .await?; + + let zero = generate_trivial_encrypt( + tx, + user_address, + user_address, + transaction_id, + db, + Some(DEF_TYPE), + Some(0), + false, + ) + .await?; + + // transferred = FHE.select(success, amount, FHE.asEuint64(0)); + + let transferred = next_random_handle(DEF_TYPE); + let event = tfhe_event(TfheContractEvents::FheIfThenElse( + TfheContract::FheIfThenElse { + caller, + control: success, + ifTrue: e_amount, + ifFalse: zero, + result: transferred, + }, + )); + insert_tfhe_event(tx, db, transaction_id, event, true).await?; + + /* + + ptr = FHE.add(_balances[to], transferred); + FHE.allowThis(ptr); + FHE.allow(ptr, to); + _balances[to] = ptr; + */ + + let ptr = next_random_handle(DEF_TYPE); + let event = tfhe_event(TfheContractEvents::FheAdd(TfheContract::FheAdd { + caller, + lhs: balance_to, + rhs: transferred, + result: ptr, + scalarByte: ScalarByte::from(false as u8), + })); + insert_tfhe_event(tx, db, transaction_id, event, true).await?; + + allow_handle( + tx, + &ptr.to_vec(), + AllowEvents::AllowedForDecryption, + user_address.to_string(), + transaction_id, + ) + .await?; + + /* + if (from != address(0)) FHE.allow(transferred, from); + if (to != address(0)) FHE.allow(transferred, to); + FHE.allowThis(transferred); + emit ConfidentialTransfer(from, to, transferred); + */ + + allow_handle( + tx, + &transferred.to_vec(), + AllowEvents::AllowedAccount, + user_address.to_string(), + transaction_id, + ) + .await?; + + Ok(transferred) +} + +/* + function tryDecrease(euint64 oldValue, euint64 delta) internal returns (ebool success, euint64 updated) { + if (!FHE.isInitialized(oldValue)) { + if (!FHE.isInitialized(delta)) { + return (FHE.asEbool(true), oldValue); + } + return (FHE.eq(delta, 0), FHE.asEuint64(0)); + } + success = FHE.ge(oldValue, delta); + updated = FHE.select(success, FHE.sub(oldValue, delta), oldValue); +} +*/ +pub async fn try_decrease( + tx: &mut sqlx::Transaction<'_, sqlx::Postgres>, + db: &ListenerDatabase, + caller: Address, + transaction_id: Handle, + old_value: Handle, + delta: Handle, +) -> Result<(Handle, Handle), Box> { + let success = next_random_handle(FheType::FheBool); + let event = tfhe_event(TfheContractEvents::FheGe(TfheContract::FheGe { + caller, + lhs: old_value, + rhs: delta, + result: success, + scalarByte: ScalarByte::from(false as u8), + })); + insert_tfhe_event(tx, db, transaction_id, event, false).await?; + + let result_handle = next_random_handle(DEF_TYPE); + + let event = tfhe_event(TfheContractEvents::FheSub(TfheContract::FheSub { + caller, + lhs: old_value, + rhs: delta, + result: result_handle, + scalarByte: ScalarByte::from(false as u8), + })); + insert_tfhe_event(tx, db, transaction_id, event, false).await?; + + let updated = next_random_handle(DEF_TYPE); + let event = tfhe_event(TfheContractEvents::FheIfThenElse( + TfheContract::FheIfThenElse { + caller, + control: success, + ifTrue: result_handle, + ifFalse: old_value, + result: updated, + }, + )); + insert_tfhe_event(tx, db, transaction_id, event, true).await?; + + Ok((success, updated)) +} diff --git a/coprocessor/fhevm-engine/stress-test-generator/src/lib.rs b/coprocessor/fhevm-engine/stress-test-generator/src/lib.rs index 53a221a8b..732711dc7 100644 --- a/coprocessor/fhevm-engine/stress-test-generator/src/lib.rs +++ b/coprocessor/fhevm-engine/stress-test-generator/src/lib.rs @@ -1,5 +1,7 @@ +pub mod auction; pub mod dex; pub mod erc20; +pub mod erc7984; pub mod synthetics; pub mod utils; pub mod zk_gen; diff --git a/coprocessor/fhevm-engine/stress-test-generator/src/synthetics.rs b/coprocessor/fhevm-engine/stress-test-generator/src/synthetics.rs index 1593e9916..2198e4337 100644 --- a/coprocessor/fhevm-engine/stress-test-generator/src/synthetics.rs +++ b/coprocessor/fhevm-engine/stress-test-generator/src/synthetics.rs @@ -9,18 +9,17 @@ use host_listener::database::tfhe_event_propagate::{ Database as ListenerDatabase, Handle, ScalarByte, }; -use sqlx::Postgres; use std::io::prelude::*; #[allow(clippy::too_many_arguments)] pub async fn add_chain_transaction( ctx: &Context, + tx: &mut sqlx::Transaction<'_, sqlx::Postgres>, counter: Option, amount: Option, length: u32, transaction_id: Option, - listener_event_to_db: &mut ListenerDatabase, - pool: &sqlx::Pool, + listener_event_to_db: &ListenerDatabase, contract_address: &String, user_address: &String, ) -> Result<(Handle, Handle), Box> { @@ -33,6 +32,7 @@ pub async fn add_chain_transaction( Some(amount) => amount, None => { generate_trivial_encrypt( + tx, contract_address, contract_address, transaction_id, @@ -54,15 +54,22 @@ pub async fn add_chain_transaction( result: new_counter, scalarByte: ScalarByte::from(false as u8), })); - insert_tfhe_event(listener_event_to_db, transaction_id, event, i == length - 1).await?; + insert_tfhe_event( + tx, + listener_event_to_db, + transaction_id, + event, + i == length - 1, + ) + .await?; counter = new_counter; } allow_handle( + tx, &counter.to_vec(), AllowEvents::AllowedForDecryption, contract_address.to_string(), transaction_id, - pool, ) .await?; Ok((counter, counter)) @@ -71,12 +78,12 @@ pub async fn add_chain_transaction( #[allow(clippy::too_many_arguments)] pub async fn mul_chain_transaction( ctx: &Context, + tx: &mut sqlx::Transaction<'_, sqlx::Postgres>, counter: Option, amount: Option, length: u32, transaction_id: Option, - listener_event_to_db: &mut ListenerDatabase, - pool: &sqlx::Pool, + listener_event_to_db: &ListenerDatabase, contract_address: &String, user_address: &String, ) -> Result<(Handle, Handle), Box> { @@ -89,6 +96,7 @@ pub async fn mul_chain_transaction( Some(amount) => amount, None => { generate_trivial_encrypt( + tx, contract_address, contract_address, transaction_id, @@ -110,15 +118,22 @@ pub async fn mul_chain_transaction( result: new_counter, scalarByte: ScalarByte::from(false as u8), })); - insert_tfhe_event(listener_event_to_db, transaction_id, event, i == length - 1).await?; + insert_tfhe_event( + tx, + listener_event_to_db, + transaction_id, + event, + i == length - 1, + ) + .await?; counter = new_counter; } allow_handle( + tx, &counter.to_vec(), AllowEvents::AllowedForDecryption, contract_address.to_string(), transaction_id, - pool, ) .await?; Ok((counter, counter)) @@ -126,11 +141,11 @@ pub async fn mul_chain_transaction( #[allow(clippy::too_many_arguments)] pub async fn generate_pub_decrypt_handles_types( + tx: &mut sqlx::Transaction<'_, sqlx::Postgres>, min_type: u8, max_type: u8, transaction_id: Option, - listener_event_to_db: &mut ListenerDatabase, - pool: &sqlx::Pool, + listener_event_to_db: &ListenerDatabase, contract_address: &str, user_address: &String, ) -> Result<(Handle, Handle), Box> { @@ -144,6 +159,7 @@ pub async fn generate_pub_decrypt_handles_types( let mut handle = next_random_handle(DEF_TYPE); for type_num in min_type..=max_type { handle = generate_trivial_encrypt( + tx, contract_address, user_address, transaction_id, @@ -154,11 +170,11 @@ pub async fn generate_pub_decrypt_handles_types( ) .await?; allow_handle( + tx, &handle.to_vec(), AllowEvents::AllowedForDecryption, user_address.to_string(), transaction_id, - pool, ) .await?; writeln!(out_file, "{}", "0x".to_owned() + &hex::encode(handle))?; @@ -168,11 +184,11 @@ pub async fn generate_pub_decrypt_handles_types( #[allow(clippy::too_many_arguments)] pub async fn generate_user_decrypt_handles_types( + tx: &mut sqlx::Transaction<'_, sqlx::Postgres>, min_type: u8, max_type: u8, transaction_id: Option, - listener_event_to_db: &mut ListenerDatabase, - pool: &sqlx::Pool, + listener_event_to_db: &ListenerDatabase, contract_address: &str, user_address: &String, ) -> Result<(Handle, Handle), Box> { @@ -186,6 +202,7 @@ pub async fn generate_user_decrypt_handles_types( let mut handle = next_random_handle(DEF_TYPE); for type_num in min_type..=max_type { handle = generate_trivial_encrypt( + tx, contract_address, user_address, transaction_id, @@ -196,19 +213,19 @@ pub async fn generate_user_decrypt_handles_types( ) .await?; allow_handle( + tx, &handle.to_vec(), AllowEvents::AllowedAccount, contract_address.to_string(), transaction_id, - pool, ) .await?; allow_handle( + tx, &handle.to_vec(), AllowEvents::AllowedAccount, user_address.to_string(), transaction_id, - pool, ) .await?; writeln!(out_file, "{}", "0x".to_owned() + &hex::encode(handle))?; diff --git a/coprocessor/fhevm-engine/stress-test-generator/src/utils.rs b/coprocessor/fhevm-engine/stress-test-generator/src/utils.rs index 704076b45..34f86260d 100644 --- a/coprocessor/fhevm-engine/stress-test-generator/src/utils.rs +++ b/coprocessor/fhevm-engine/stress-test-generator/src/utils.rs @@ -8,6 +8,7 @@ use host_listener::database::tfhe_event_propagate::{ use rand::Rng; use sqlx::types::time::PrimitiveDateTime; use sqlx::Postgres; +use std::ops::DerefMut; use std::sync::Arc; use tracing::info; @@ -63,6 +64,10 @@ pub fn next_random_handle(ct_type: FheType) -> Handle { handle_hash.update(rand::rng().random::().to_be_bytes()); let mut handle = handle_hash.finalize().to_vec(); assert_eq!(handle.len(), 32); + + // Mark it as a mocked handle + handle[0..3].copy_from_slice(&[0u8; 3]); + // Handle from computation handle[21] = 255u8; handle[22..30].copy_from_slice(&ecfg.chain_id.to_be_bytes()); @@ -70,6 +75,16 @@ pub fn next_random_handle(ct_type: FheType) -> Handle { handle[31] = 0u8; Handle::from_slice(&handle) } + +pub fn new_transaction_id() -> Handle { + let mut handle_hash = Keccak256::new(); + handle_hash.update(rand::rng().random::().to_be_bytes()); + let mut txn_id = handle_hash.finalize().to_vec(); + assert_eq!(txn_id.len(), 32); + // Mark it as a mocked transaction id + txn_id[20..32].copy_from_slice(&[0u8; 12]); + Handle::from_slice(&txn_id) +} pub fn default_dependence_cache_size() -> u16 { 128 } @@ -77,6 +92,7 @@ pub fn default_dependence_cache_size() -> u16 { #[derive(Debug, serde::Deserialize, serde::Serialize, Eq, PartialEq, Clone)] pub enum Transaction { ERC20Transfer, + ERC7984Transfer, DEXSwapRequest, DEXSwapClaim, MULChain, @@ -84,7 +100,11 @@ pub enum Transaction { InputVerif, GenPubDecHandles, GenUsrDecHandles, + BatchAllowHandles, + BatchSubmitEncryptedBids, + BatchInputProofs, } + #[derive(Debug, serde::Deserialize, serde::Serialize, Eq, PartialEq, Clone)] pub enum ERCTransferVariant { Whitepaper, @@ -118,6 +138,7 @@ pub struct Scenario { pub contract_address: String, pub user_address: String, pub scenario: Vec<(f64, u64)>, + pub batch_size: Option, } pub struct Job { @@ -131,15 +152,17 @@ pub struct Context { pub args: Args, pub ecfg: EnvConfig, pub cancel_token: tokio_util::sync::CancellationToken, + // Pre-generated inputs pool + pub inputs_pool: Vec>, } #[allow(dead_code)] pub async fn allow_handle( + tx: &mut sqlx::Transaction<'_, Postgres>, handle: &Vec, event_type: AllowEvents, account_address: String, transaction_id: TransactionHash, - pool: &sqlx::Pool, ) -> Result<(), Box> { let started_at = std::time::Instant::now(); @@ -153,7 +176,7 @@ pub async fn allow_handle( account_address, event_type as i16, transaction_id.to_vec(), - ).execute(pool).await?; + ).execute(tx.deref_mut()).await?; let _query = sqlx::query!( "INSERT INTO pbs_computations(tenant_id, handle, transaction_id) VALUES($1, $2, $3) ON CONFLICT DO NOTHING;", @@ -161,7 +184,7 @@ pub async fn allow_handle( handle, transaction_id.to_vec() ) - .execute(pool) + .execute(tx.deref_mut()) .await?; tracing::debug!(target: "tool", duration = ?started_at.elapsed(), "Handle allowed, db_query"); @@ -170,10 +193,11 @@ pub async fn allow_handle( #[allow(dead_code)] pub async fn allow_handles( + tx: &mut sqlx::Transaction<'_, Postgres>, handles: &Vec>, event_type: AllowEvents, account_address: String, - pool: &sqlx::Pool, + disable_pbs_computations: bool, ) -> Result<(), Box> { let ecfg = EnvConfig::new(); let tenant_id = vec![ecfg.tenant_id; handles.len()]; @@ -188,8 +212,12 @@ pub async fn allow_handles( &account_address, &event_type, ) - .execute(pool) + .execute(tx.deref_mut()) .await?; + + if disable_pbs_computations { + return Ok(()); + } let _query = sqlx::query!( "INSERT INTO pbs_computations(tenant_id, handle) SELECT * FROM UNNEST($1::INTEGER[], $2::BYTEA[]) @@ -197,7 +225,7 @@ pub async fn allow_handles( &tenant_id, handles, ) - .execute(pool) + .execute(tx.deref_mut()) .await?; Ok(()) } @@ -207,11 +235,13 @@ pub fn as_scalar_uint(big_int: &BigInt) -> ClearConst { ClearConst::from_be_slice(&bytes) } +#[allow(clippy::too_many_arguments)] pub async fn generate_trivial_encrypt( + tx: &mut sqlx::Transaction<'_, Postgres>, _contract_address: &str, user_address: &str, transaction_hash: TransactionHash, - listener_event_to_db: &mut ListenerDatabase, + listener_event_to_db: &ListenerDatabase, ct_type: Option, ct_value: Option, is_allowed: bool, @@ -234,11 +264,7 @@ pub async fn generate_trivial_encrypt( block_number: 1, block_timestamp: PrimitiveDateTime::MAX, }; - let mut tx = listener_event_to_db.new_transaction().await?; - listener_event_to_db - .insert_tfhe_event(&mut tx, &log) - .await?; - tx.commit().await?; + listener_event_to_db.insert_tfhe_event(tx, &log).await?; Ok(handle) } @@ -402,13 +428,14 @@ impl EnvConfig { } pub async fn insert_tfhe_event( + tx: &mut sqlx::Transaction<'_, sqlx::Postgres>, listener_event_to_db: &ListenerDatabase, transaction_hash: TransactionHash, event: Log, is_allowed: bool, ) -> Result<(), Box> { let started_at = tokio::time::Instant::now(); - let mut tx = listener_event_to_db.new_transaction().await?; + let log = LogTfhe { event, transaction_hash: Some(transaction_hash), @@ -416,10 +443,12 @@ pub async fn insert_tfhe_event( block_number: 1, block_timestamp: PrimitiveDateTime::MAX, }; - listener_event_to_db - .insert_tfhe_event(&mut tx, &log) - .await?; - tx.commit().await?; + listener_event_to_db.insert_tfhe_event(tx, &log).await?; + tracing::debug!(target: "tool", duration = ?started_at.elapsed(), "TFHE event, db_query"); Ok(()) } + +pub async fn pool(listener_event_to_db: &ListenerDatabase) -> sqlx::Pool { + listener_event_to_db.pool.clone().read().await.clone() +} diff --git a/coprocessor/fhevm-engine/stress-test-generator/src/zk_gen.rs b/coprocessor/fhevm-engine/stress-test-generator/src/zk_gen.rs index d47cde1f0..8eb5be2ea 100644 --- a/coprocessor/fhevm-engine/stress-test-generator/src/zk_gen.rs +++ b/coprocessor/fhevm-engine/stress-test-generator/src/zk_gen.rs @@ -1,11 +1,13 @@ -use crate::utils::{next_random_handle, query_and_save_pks, EnvConfig, Inputs, DEF_TYPE}; +use crate::utils::{ + new_transaction_id, next_random_handle, pool, query_and_save_pks, EnvConfig, Inputs, DEF_TYPE, +}; use fhevm_engine_common::utils::to_hex; -use host_listener::database::tfhe_event_propagate::Handle; +use host_listener::database::tfhe_event_propagate::{Database as ListenerDatabase, Handle}; use rand::Rng; -use std::collections::HashMap; use std::str::FromStr; use std::sync::Arc; use std::time::Duration; +use std::{collections::HashMap, ops::DerefMut}; use tokio::sync::RwLock; use tracing::{error, info}; @@ -57,29 +59,32 @@ impl ZkData { } } +#[allow(clippy::too_many_arguments)] async fn insert_proof( - pool: &sqlx::PgPool, + tx: &mut sqlx::Transaction<'_, sqlx::Postgres>, request_id: i64, - zk_pok: &[u8], - aux: &ZkData, + zk_pok: Vec, + aux: ZkData, db_notify_channel: &str, transaction_id: Handle, + retry_count: i32, _block_number: u64, ) -> Result<(), sqlx::Error> { // Insert ZkPok into database sqlx::query( - "INSERT INTO verify_proofs (zk_proof_id, input, chain_id, contract_address, user_address, verified, transaction_id) - VALUES ($1, $2, $3, $4, $5, NULL, $6)" + "INSERT INTO verify_proofs (zk_proof_id, input, chain_id, contract_address, user_address, verified, transaction_id, retry_count) + VALUES ($1, $2, $3, $4, $5, NULL, $6, $7)" ).bind(request_id) .bind(zk_pok) .bind(aux.chain_id) .bind(aux.contract_address.clone()) .bind(aux.user_address.clone()) .bind(transaction_id.to_vec()) - .execute(pool).await?; + .bind(retry_count) + .execute(tx.deref_mut()).await?; sqlx::query("SELECT pg_notify($1, '')") .bind(db_notify_channel) - .execute(pool) + .execute(tx.deref_mut()) .await .unwrap(); @@ -187,17 +192,22 @@ pub async fn generate_random_handle_vec( let zk_pok = fhevm_engine_common::utils::safe_serialize(&the_list); let zk_id = ZK_PROOF_ID.fetch_add(1, std::sync::atomic::Ordering::SeqCst); + let mut db_tx = pool.begin().await?; + insert_proof( - &pool, + &mut db_tx, zk_id, - &zk_pok, - &zk_data, + zk_pok, + zk_data, &ctx.args.zkproof_notify_channel, transaction_id, 0, + 0, ) .await?; + db_tx.commit().await?; + info!(zk_id, count, "waiting for verification..."); let handles = wait_for_verification_and_handle(&pool, zk_id, 5000).await?; info!(handles = ?handles.iter().map(hex::encode), count = handles.len(), "received handles"); @@ -205,6 +215,65 @@ pub async fn generate_random_handle_vec( Ok(handles) } +pub async fn generate_and_insert_inputs_batch( + ctx: &Context, + tx: &mut sqlx::Transaction<'_, sqlx::Postgres>, + listener_event_to_db: &ListenerDatabase, + batch_size: usize, + inputs_count: u8, + contract_address: &String, + user_address: &String, +) -> Result<(), Box> { + assert!(inputs_count <= 254); + let ecfg = EnvConfig::new(); + let pool = pool(listener_event_to_db).await; + + let (pks, public_params) = query_and_save_pks(ecfg.tenant_id, &pool).await?; + + // Generate a batch of zkpoks + for idx in 0..batch_size { + let transaction_id = new_transaction_id(); + + let zk_data = ZkData { + contract_address: contract_address.to_owned(), + user_address: user_address.to_owned(), + acl_contract_address: ecfg.acl_contract_address.clone(), + chain_id: ecfg.chain_id, + }; + let aux_data = zk_data.to_owned().assemble()?; + + let mut builder = tfhe::ProvenCompactCiphertextList::builder(&pks); + for _ in 0..inputs_count { + builder.push(rand::rng().random::()); + } + let zk_id = ZK_PROOF_ID.fetch_add(1, std::sync::atomic::Ordering::SeqCst); + info!(target: "tool", "zkpok id: {}, count = {:?}, seq_num = {} of {} , txn: {:?}, ", zk_id, inputs_count, idx, batch_size, transaction_id ); + let the_list = builder + .build_with_proof_packed(&public_params, &aux_data, tfhe::zk::ZkComputeLoad::Proof) + .unwrap(); + + let zk_pok = fhevm_engine_common::utils::safe_serialize(&the_list); + + let retry_count = 5; + // retry_count = 5 to ensure the txn-sender will delete it after first try + // If not deleted, txn-sender will report too many VerifyProofNotRequested errors + // In devnet, verify_proof_resp_max_retries: 6, + insert_proof( + tx, + zk_id, + zk_pok.clone(), + zk_data, + &ctx.args.zkproof_notify_channel, + transaction_id, + retry_count, + 0, + ) + .await?; + } + + Ok(()) +} + pub async fn get_inputs_vector( ctx: &Context, in_type: Inputs,