From 35339445b261bbdea8158d31eeace87c1f8b8988 Mon Sep 17 00:00:00 2001 From: David Kazlauskas Date: Wed, 11 Sep 2024 08:27:54 +0300 Subject: [PATCH 01/13] Fhe randomness implementation + refactor tests to only leave production functions in coprocessor --- fhevm-engine/Cargo.lock | 10 +- fhevm-engine/Cargo.toml | 2 +- fhevm-engine/coprocessor/Cargo.toml | 2 +- .../migrations/20240723111257_coprocessor.sql | 4 +- .../coprocessor/migrations/gen-keys.sh | 3 +- fhevm-engine/coprocessor/src/db_queries.rs | 34 +- fhevm-engine/coprocessor/src/server.rs | 317 +++---- fhevm-engine/coprocessor/src/tests/errors.rs | 367 ++++---- fhevm-engine/coprocessor/src/tests/inputs.rs | 139 ++- fhevm-engine/coprocessor/src/tests/mod.rs | 72 +- .../coprocessor/src/tests/operators.rs | 179 ++-- fhevm-engine/coprocessor/src/tests/random.rs | 243 +++++ fhevm-engine/coprocessor/src/tests/utils.rs | 105 ++- fhevm-engine/coprocessor/src/tfhe_worker.rs | 60 +- fhevm-engine/coprocessor/src/types.rs | 55 +- fhevm-engine/coprocessor/src/utils.rs | 15 +- fhevm-engine/executor/src/server.rs | 2 +- fhevm-engine/fhevm-engine-common/Cargo.toml | 2 + .../fhevm-engine-common/src/tfhe_ops.rs | 310 ++++++- fhevm-engine/fhevm-engine-common/src/types.rs | 54 ++ fhevm-engine/fhevm-go-coproc/fhevm/api.go | 153 ++-- .../fhevm-go-coproc/fhevm/common.pb.go | 120 +-- .../fhevm-go-coproc/fhevm/coprocessor.pb.go | 863 ++++++------------ .../fhevm/coprocessor_grpc.pb.go | 235 +++++ fhevm-engine/fhevm-go-coproc/fhevm/fhelib.go | 2 +- .../fhevm-go-coproc/fhevm/fhelib_ops.go | 66 +- proto/coprocessor.proto | 35 +- 27 files changed, 1967 insertions(+), 1482 deletions(-) create mode 100644 fhevm-engine/coprocessor/src/tests/random.rs create mode 100644 fhevm-engine/fhevm-go-coproc/fhevm/coprocessor_grpc.pb.go diff --git a/fhevm-engine/Cargo.lock b/fhevm-engine/Cargo.lock index 9059fa9e..34b88f6b 100644 --- a/fhevm-engine/Cargo.lock +++ b/fhevm-engine/Cargo.lock @@ -1522,7 +1522,7 @@ dependencies = [ "lazy_static", "lru", "prost", - "random", + "rand", "regex", "serde_json", "sha3", @@ -1954,6 +1954,8 @@ dependencies = [ "bigdecimal", "bincode", "hex", + "rand", + "rand_chacha", "sha3", "strum", "tfhe", @@ -3573,12 +3575,6 @@ dependencies = [ "rand_core", ] -[[package]] -name = "random" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c7093a82705b48f10d4b165c6482dbe9f58cd63a870d85537e857b2badbd88" - [[package]] name = "rayon" version = "1.10.0" diff --git a/fhevm-engine/Cargo.toml b/fhevm-engine/Cargo.toml index 7be39b60..4ff0e950 100644 --- a/fhevm-engine/Cargo.toml +++ b/fhevm-engine/Cargo.toml @@ -12,4 +12,4 @@ sha3 = "0.10.8" anyhow = "1.0.86" [profile.dev.package.tfhe] -overflow-checks = false \ No newline at end of file +overflow-checks = false diff --git a/fhevm-engine/coprocessor/Cargo.toml b/fhevm-engine/coprocessor/Cargo.toml index 89f86943..eb6ca1dc 100644 --- a/fhevm-engine/coprocessor/Cargo.toml +++ b/fhevm-engine/coprocessor/Cargo.toml @@ -34,7 +34,7 @@ sha3.workspace = true [dev-dependencies] testcontainers = "0.21" -random = "0.14.0" +rand = "0.8.5" [build-dependencies] tonic-build = "0.12" diff --git a/fhevm-engine/coprocessor/migrations/20240723111257_coprocessor.sql b/fhevm-engine/coprocessor/migrations/20240723111257_coprocessor.sql index 0ba14271..4cdf4ced 100644 --- a/fhevm-engine/coprocessor/migrations/20240723111257_coprocessor.sql +++ b/fhevm-engine/coprocessor/migrations/20240723111257_coprocessor.sql @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:68bf6fb0ef64b629a0ff622c7c5ef900118d32fe77baa9e34c8660bc43cce641 -size 530459546 +oid sha256:96078adc7be2469c034b922d58b058c96f35a1a3e2d5a346c01c736e8cead869 +size 530459562 diff --git a/fhevm-engine/coprocessor/migrations/gen-keys.sh b/fhevm-engine/coprocessor/migrations/gen-keys.sh index dd1f28d7..a7b86723 100755 --- a/fhevm-engine/coprocessor/migrations/gen-keys.sh +++ b/fhevm-engine/coprocessor/migrations/gen-keys.sh @@ -1,9 +1,10 @@ #!/bin/sh echo " -INSERT INTO tenants(tenant_api_key, chain_id, verifying_contract_address, pks_key, sks_key, cks_key) +INSERT INTO tenants(tenant_api_key, tenant_id, chain_id, verifying_contract_address, pks_key, sks_key, cks_key) VALUES ( 'a1503fb6-d79b-4e9e-826d-44cf262f3e05', + 1, 12345, '0x6819e3aDc437fAf9D533490eD3a7552493fCE3B1', decode('$(cat ../../fhevm-keys/pks | xxd -p | tr -d '\n')','hex'), diff --git a/fhevm-engine/coprocessor/src/db_queries.rs b/fhevm-engine/coprocessor/src/db_queries.rs index 9d8b96cb..464c532a 100644 --- a/fhevm-engine/coprocessor/src/db_queries.rs +++ b/fhevm-engine/coprocessor/src/db_queries.rs @@ -101,9 +101,13 @@ pub struct FetchTenantKeyResult { } /// Returns chain id and verifying contract address for EIP712 signature and tfhe server key -pub async fn fetch_tenant_server_key<'a, T>(tenant_id: i32, pool: T, tenant_key_cache: &std::sync::Arc>>) --> Result> -where T: sqlx::PgExecutor<'a> + Copy +pub async fn fetch_tenant_server_key<'a, T>( + tenant_id: i32, + pool: T, + tenant_key_cache: &std::sync::Arc>>, +) -> Result> +where + T: sqlx::PgExecutor<'a> + Copy, { // try getting from cache until it succeeds with populating cache loop { @@ -122,9 +126,12 @@ where T: sqlx::PgExecutor<'a> + Copy } } -pub async fn query_tenant_keys<'a, T>(tenants_to_query: Vec, conn: T) --> Result, Box> -where T: sqlx::PgExecutor<'a> +pub async fn query_tenant_keys<'a, T>( + tenants_to_query: Vec, + conn: T, +) -> Result, Box> +where + T: sqlx::PgExecutor<'a>, { let mut res = Vec::with_capacity(tenants_to_query.len()); let keys = query!( @@ -145,7 +152,8 @@ where T: sqlx::PgExecutor<'a> .expect("We can't deserialize our own validated pks key"); res.push(TfheTenantKeys { tenant_id: key.tenant_id, - sks, pks, + sks, + pks, chain_id: key.chain_id, verifying_contract_address: key.verifying_contract_address, }); @@ -154,9 +162,13 @@ where T: sqlx::PgExecutor<'a> Ok(res) } -pub async fn populate_cache_with_tenant_keys<'a, T>(tenants_to_query: Vec, conn: T, tenant_key_cache: &std::sync::Arc>>) --> Result<(), Box> -where T: sqlx::PgExecutor<'a> +pub async fn populate_cache_with_tenant_keys<'a, T>( + tenants_to_query: Vec, + conn: T, + tenant_key_cache: &std::sync::Arc>>, +) -> Result<(), Box> +where + T: sqlx::PgExecutor<'a>, { if !tenants_to_query.is_empty() { let keys = query_tenant_keys(tenants_to_query, conn).await?; @@ -174,4 +186,4 @@ where T: sqlx::PgExecutor<'a> } Ok(()) -} \ No newline at end of file +} diff --git a/fhevm-engine/coprocessor/src/server.rs b/fhevm-engine/coprocessor/src/server.rs index 0b2b3df0..f0a65fb4 100644 --- a/fhevm-engine/coprocessor/src/server.rs +++ b/fhevm-engine/coprocessor/src/server.rs @@ -2,19 +2,25 @@ use std::collections::BTreeMap; use std::num::NonZeroUsize; use std::str::FromStr; -use crate::db_queries::{check_if_api_key_is_valid, check_if_ciphertexts_exist_in_db, fetch_tenant_server_key}; +use crate::db_queries::{ + check_if_api_key_is_valid, check_if_ciphertexts_exist_in_db, fetch_tenant_server_key, +}; use crate::server::coprocessor::GenericResponse; +use crate::types::{CoprocessorError, TfheTenantKeys}; +use crate::utils::sort_computations_by_dependencies; use alloy::signers::local::PrivateKeySigner; use alloy::signers::SignerSync; use alloy::sol_types::SolStruct; -use bigdecimal::num_bigint::BigUint; -use fhevm_engine_common::tfhe_ops::{check_fhe_operand_types, current_ciphertext_version, debug_trivial_encrypt_be_bytes, deserialize_fhe_ciphertext, try_expand_ciphertext_list}; -use fhevm_engine_common::types::{FhevmError, SupportedFheCiphertexts}; -use sha3::{Digest, Keccak256}; -use crate::types::{CoprocessorError, TfheTenantKeys}; -use crate::utils::sort_computations_by_dependencies; use coprocessor::async_computation_input::Input; -use coprocessor::{DebugDecryptResponse, DebugDecryptResponseSingle, InputCiphertextResponse, InputCiphertextResponseHandle, InputUploadBatch, InputUploadResponse}; +use coprocessor::{ + InputCiphertextResponse, InputCiphertextResponseHandle, InputUploadBatch, InputUploadResponse, +}; +use fhevm_engine_common::tfhe_ops::{ + check_fhe_operand_types, current_ciphertext_version, trivial_encrypt_be_bytes, + try_expand_ciphertext_list, +}; +use fhevm_engine_common::types::{FhevmError, SupportedFheCiphertexts, SupportedFheOperations}; +use sha3::{Digest, Keccak256}; use sqlx::{query, Acquire}; use tonic::transport::Server; @@ -42,9 +48,7 @@ pub async fn run_server( .expect("Can't parse server address"); let db_url = crate::utils::db_url(&args); - let coprocessor_key_file = - tokio::fs::read_to_string(&args.coprocessor_private_key) - .await?; + let coprocessor_key_file = tokio::fs::read_to_string(&args.coprocessor_private_key).await?; let signer = PrivateKeySigner::from_str(coprocessor_key_file.trim())?; println!("Coprocessor signer address: {}", signer.address()); @@ -60,7 +64,12 @@ pub async fn run_server( NonZeroUsize::new(args.tenant_key_cache_size as usize).unwrap(), ))); - let service = CoprocessorService { pool, args, tenant_key_cache, signer }; + let service = CoprocessorService { + pool, + args, + tenant_key_cache, + signer, + }; Server::builder() .add_service( @@ -126,7 +135,7 @@ impl coprocessor::fhevm_coprocessor_server::FhevmCoprocessor for CoprocessorServ } let mut response = InputUploadResponse { - upload_responses: Vec::with_capacity(req.input_ciphertexts.len()) + upload_responses: Vec::with_capacity(req.input_ciphertexts.len()), }; if req.input_ciphertexts.is_empty() { return Ok(tonic::Response::new(response)); @@ -135,19 +144,19 @@ impl coprocessor::fhevm_coprocessor_server::FhevmCoprocessor for CoprocessorServ let fetch_key_response = { fetch_tenant_server_key(tenant_id, &self.pool, &self.tenant_key_cache) .await - .map_err(|e| { - tonic::Status::from_error(e) - })? + .map_err(|e| tonic::Status::from_error(e))? }; let chain_id = fetch_key_response.chain_id; let server_key = fetch_key_response.server_key; let verifying_contract_address = fetch_key_response.verifying_contract_address; - let verifying_contract_address = alloy::primitives::Address::from_str(&verifying_contract_address) - .map_err(|e| { - tonic::Status::from_error(Box::new(CoprocessorError::CannotParseTenantEthereumAddress { - bad_address: verifying_contract_address.clone(), - parsing_error: e.to_string(), - })) + let verifying_contract_address = + alloy::primitives::Address::from_str(&verifying_contract_address).map_err(|e| { + tonic::Status::from_error(Box::new( + CoprocessorError::CannotParseTenantEthereumAddress { + bad_address: verifying_contract_address.clone(), + parsing_error: e.to_string(), + }, + )) })?; let eip_712_domain = alloy::sol_types::eip712_domain! { @@ -165,20 +174,22 @@ impl coprocessor::fhevm_coprocessor_server::FhevmCoprocessor for CoprocessorServ let mut caller_addresses = Vec::with_capacity(req.input_ciphertexts.len()); for ci in &req.input_ciphertexts { // parse addresses - contract_addresses.push(alloy::primitives::Address::from_str(&ci.contract_address) - .map_err(|e| { + contract_addresses.push( + alloy::primitives::Address::from_str(&ci.contract_address).map_err(|e| { CoprocessorError::CannotParseEthereumAddress { bad_address: ci.contract_address.clone(), parsing_error: e.to_string(), } - })?); - caller_addresses.push(alloy::primitives::Address::from_str(&ci.caller_address) - .map_err(|e| { + })?, + ); + caller_addresses.push( + alloy::primitives::Address::from_str(&ci.caller_address).map_err(|e| { CoprocessorError::CannotParseEthereumAddress { bad_address: ci.contract_address.clone(), parsing_error: e.to_string(), } - })?); + })?, + ); } for (idx, ci) in req.input_ciphertexts.iter().enumerate() { @@ -200,44 +211,66 @@ impl coprocessor::fhevm_coprocessor_server::FhevmCoprocessor for CoprocessorServ let mut results: BTreeMap> = BTreeMap::new(); while let Some(output) = tfhe_work_set.join_next().await { - let (cts, idx) = output.map_err(|e| { - let err: Box<(dyn std::error::Error + Sync + Send)> = Box::new(e); - tonic::Status::from_error(err) - })?.map_err(|e| { - tonic::Status::from_error(e.0) - })?; + let (cts, idx) = output + .map_err(|e| { + let err: Box<(dyn std::error::Error + Sync + Send)> = Box::new(e); + tonic::Status::from_error(err) + })? + .map_err(|e| tonic::Status::from_error(e.0))?; if cts.len() > self.args.maximum_handles_per_input as usize { - return Err(tonic::Status::from_error( - Box::new(CoprocessorError::CompactInputCiphertextHasMoreCiphertextThanLimitAllows { + return Err(tonic::Status::from_error(Box::new( + CoprocessorError::CompactInputCiphertextHasMoreCiphertextThanLimitAllows { input_blob_index: idx, input_ciphertexts_in_blob: cts.len(), - input_maximum_ciphertexts_allowed: self.args.maximum_handles_per_input as usize, - }) - )); + input_maximum_ciphertexts_allowed: self.args.maximum_handles_per_input + as usize, + }, + ))); } - assert!(results.insert(idx, cts).is_none(), "fresh map, we passed vector ordered by indexes before"); + assert!( + results.insert(idx, cts).is_none(), + "fresh map, we passed vector ordered by indexes before" + ); } - assert_eq!(results.len(), req.input_ciphertexts.len(), "We should have all the ciphertexts now"); + assert_eq!( + results.len(), + req.input_ciphertexts.len(), + "We should have all the ciphertexts now" + ); - let mut trx = self.pool.begin().await.map_err(Into::::into)?; + let mut trx = self + .pool + .begin() + .await + .map_err(Into::::into)?; for (idx, input_blob) in req.input_ciphertexts.iter().enumerate() { let mut state = Keccak256::new(); state.update(&input_blob.input_payload); let blob_hash = state.finalize().to_vec(); assert_eq!(blob_hash.len(), 32, "should be 32 bytes"); - let corresponding_unpacked = results.get(&idx).expect("we should have all results computed now"); + let corresponding_unpacked = results + .get(&idx) + .expect("we should have all results computed now"); // save blob for audits and historical reference - let _ = sqlx::query!(" + let _ = sqlx::query!( + " INSERT INTO input_blobs(tenant_id, blob_hash, blob_data, blob_ciphertext_count) VALUES($1, $2, $3, $4) ON CONFLICT (tenant_id, blob_hash) DO NOTHING - ", tenant_id, &blob_hash, &input_blob.input_payload, corresponding_unpacked.len() as i32) - .execute(trx.as_mut()).await.map_err(Into::::into)?; + ", + tenant_id, + &blob_hash, + &input_blob.input_payload, + corresponding_unpacked.len() as i32 + ) + .execute(trx.as_mut()) + .await + .map_err(Into::::into)?; let mut ct_verification = CiphertextVerification { contractAddress: contract_addresses[idx], @@ -267,7 +300,8 @@ impl coprocessor::fhevm_coprocessor_server::FhevmCoprocessor for CoprocessorServ handle[30] = serialized_type as u8; handle[31] = ciphertext_version as u8; - let _ = sqlx::query!(" + let _ = sqlx::query!( + " INSERT INTO ciphertexts( tenant_id, handle, @@ -279,10 +313,22 @@ impl coprocessor::fhevm_coprocessor_server::FhevmCoprocessor for CoprocessorServ ) VALUES($1, $2, $3, $4, $5, $6, $7) ON CONFLICT (tenant_id, handle, ciphertext_version) DO NOTHING - ", tenant_id, &handle, &serialized_ct, ciphertext_version, serialized_type, &blob_hash, ct_idx as i32) - .execute(trx.as_mut()).await.map_err(Into::::into)?; + ", + tenant_id, + &handle, + &serialized_ct, + ciphertext_version, + serialized_type, + &blob_hash, + ct_idx as i32 + ) + .execute(trx.as_mut()) + .await + .map_err(Into::::into)?; - ct_verification.handlesList.push(alloy::primitives::U256::from_be_slice(&handle)); + ct_verification + .handlesList + .push(alloy::primitives::U256::from_be_slice(&handle)); ct_resp.input_handles.push(InputCiphertextResponseHandle { handle: handle.to_vec(), ciphertext_type: serialized_type as i32, @@ -290,10 +336,11 @@ impl coprocessor::fhevm_coprocessor_server::FhevmCoprocessor for CoprocessorServ } let signing_hash = ct_verification.eip712_signing_hash(&eip_712_domain); - let eip_712_signature = self.signer.sign_hash_sync(&signing_hash) - .map_err(|e| { - CoprocessorError::Eip712SigningFailure { error: e.to_string() } - })?; + let eip_712_signature = self.signer.sign_hash_sync(&signing_hash).map_err(|e| { + CoprocessorError::Eip712SigningFailure { + error: e.to_string(), + } + })?; ct_resp.eip712_signature = eip_712_signature.as_bytes().to_vec(); @@ -344,6 +391,10 @@ impl coprocessor::fhevm_coprocessor_server::FhevmCoprocessor for CoprocessorServ let mut this_comp_inputs: Vec> = Vec::with_capacity(comp.inputs.len()); let mut is_scalar_op_vec: Vec = Vec::with_capacity(comp.inputs.len()); for (idx, ih) in comp.inputs.iter().enumerate() { + let fhe_op: SupportedFheOperations = comp + .operation + .try_into() + .map_err(|e| CoprocessorError::FhevmError(e))?; if let Some(input) = &ih.input { match input { Input::InputHandle(ih) => { @@ -359,7 +410,7 @@ impl coprocessor::fhevm_coprocessor_server::FhevmCoprocessor for CoprocessorServ handle_types.push(-1); this_comp_inputs.push(sc.clone()); is_scalar_op_vec.push(true); - assert!(idx == 1, "we should have checked earlier that only second operand can be scalar"); + assert!(idx == 1 || fhe_op.is_random(), "we should have checked earlier that only second operand can be scalar"); } } } @@ -372,7 +423,8 @@ impl coprocessor::fhevm_coprocessor_server::FhevmCoprocessor for CoprocessorServ &handle_types, &this_comp_inputs, &is_scalar_op_vec, - ).map_err(|e| CoprocessorError::FhevmError(e))?; + ) + .map_err(|e| CoprocessorError::FhevmError(e))?; computations_inputs.push(this_comp_inputs); are_comps_scalar.push(is_computation_scalar); @@ -393,10 +445,9 @@ impl coprocessor::fhevm_coprocessor_server::FhevmCoprocessor for CoprocessorServ let output_type = ct_types .get(&comp.output_handle) .expect("we should have collected all output result types by now with check_fhe_operand_types"); - let fhe_operation: i16 = comp - .operation - .try_into() - .map_err(|_| CoprocessorError::FhevmError(FhevmError::UnknownFheOperation(comp.operation)))?; + let fhe_operation: i16 = comp.operation.try_into().map_err(|_| { + CoprocessorError::FhevmError(FhevmError::UnknownFheOperation(comp.operation)) + })?; let res = query!( " INSERT INTO computations( @@ -417,7 +468,10 @@ impl coprocessor::fhevm_coprocessor_server::FhevmCoprocessor for CoprocessorServ fhe_operation, are_comps_scalar[idx], output_type - ).execute(trx.as_mut()).await.map_err(Into::::into)?; + ) + .execute(trx.as_mut()) + .await + .map_err(Into::::into)?; if res.rows_affected() > 0 { new_work_available = true; } @@ -440,16 +494,18 @@ impl coprocessor::fhevm_coprocessor_server::FhevmCoprocessor for CoprocessorServ } // debug functions below, should be removed in production - async fn debug_encrypt_ciphertext( + async fn trivial_encrypt_ciphertexts( &self, - request: tonic::Request, + request: tonic::Request, ) -> std::result::Result, tonic::Status> { let tenant_id = check_if_api_key_is_valid(&request, &self.pool).await?; let req = request.get_ref(); + // TODO: add check against duplicate input handles in the batch + let mut public_key = sqlx::query!( " - SELECT sks_key, cks_key + SELECT sks_key FROM tenants WHERE tenant_id = $1 ", @@ -462,25 +518,15 @@ impl coprocessor::fhevm_coprocessor_server::FhevmCoprocessor for CoprocessorServ assert_eq!(public_key.len(), 1); let public_key = public_key.pop().unwrap(); - - // for checking if decryption equals to trivial encryption value - let client_key: tfhe::ClientKey = bincode::deserialize(&public_key.cks_key.unwrap()).unwrap(); - let cloned = req.values.clone(); let out_cts = tokio::task::spawn_blocking(move || { let server_key: tfhe::ServerKey = bincode::deserialize(&public_key.sks_key).unwrap(); tfhe::set_server_key(server_key); - // single threaded implementation as this is debug function and it is simple to implement + // single threaded implementation, we can optimize later let mut res: Vec<(Vec, i16, Vec)> = Vec::with_capacity(cloned.len()); for v in cloned { - let the_num = BigUint::from_bytes_be(&v.be_value).to_string(); - let ct = debug_trivial_encrypt_be_bytes(v.output_type as i16, &v.be_value); - let decr = ct.decrypt(&client_key); - let fhe_bool_type = 0; - if v.output_type != fhe_bool_type { - assert_eq!(the_num, decr, "Trivial encryption must preserve the original value"); - } + let ct = trivial_encrypt_be_bytes(v.output_type as i16, &v.be_value); let (ct_type, ct_bytes) = ct.serialize(); res.push((v.handle, ct_type, ct_bytes)); } @@ -501,6 +547,7 @@ impl coprocessor::fhevm_coprocessor_server::FhevmCoprocessor for CoprocessorServ sqlx::query!(" INSERT INTO ciphertexts(tenant_id, handle, ciphertext, ciphertext_version, ciphertext_type) VALUES ($1, $2, $3, $4, $5) + ON CONFLICT (tenant_id, handle, ciphertext_version) DO NOTHING ", tenant_id, handle, db_bytes, current_ciphertext_version(), db_type as i16 ) @@ -511,122 +558,4 @@ impl coprocessor::fhevm_coprocessor_server::FhevmCoprocessor for CoprocessorServ return Ok(tonic::Response::new(GenericResponse { response_code: 0 })); } - - async fn debug_decrypt_ciphertext( - &self, - request: tonic::Request, - ) -> std::result::Result, tonic::Status> - { - let tenant_id = check_if_api_key_is_valid(&request, &self.pool).await?; - let req = request.get_ref(); - - let mut priv_key = sqlx::query!( - " - SELECT cks_key - FROM tenants - WHERE tenant_id = $1 - ", - tenant_id - ) - .fetch_all(&self.pool) - .await - .map_err(Into::::into)?; - - if priv_key.is_empty() || priv_key[0].cks_key.is_none() { - return Err(tonic::Status::not_found("tenant private key not found")); - } - - let mut ct_indexes: BTreeMap<&[u8], usize> = BTreeMap::new(); - for (idx, h) in req.handles.iter().enumerate() { - ct_indexes.insert(h.as_slice(), idx); - } - - assert_eq!(priv_key.len(), 1); - - let cts = sqlx::query!( - " - SELECT ciphertext, ciphertext_type, handle - FROM ciphertexts - WHERE tenant_id = $1 - AND handle = ANY($2::BYTEA[]) - AND ciphertext_version = $3 - ", - tenant_id, - &req.handles, - current_ciphertext_version() - ) - .fetch_all(&self.pool) - .await - .map_err(Into::::into)?; - - if cts.is_empty() { - return Err(tonic::Status::not_found("ciphertext not found")); - } - - let priv_key = priv_key.pop().unwrap().cks_key.unwrap(); - - let mut values = tokio::task::spawn_blocking(move || { - let client_key: tfhe::ClientKey = bincode::deserialize(&priv_key).unwrap(); - - let mut decrypted: Vec<(Vec, DebugDecryptResponseSingle)> = Vec::with_capacity(cts.len()); - for ct in cts { - let deserialized = - deserialize_fhe_ciphertext(ct.ciphertext_type, &ct.ciphertext) - .unwrap(); - decrypted.push((ct.handle, DebugDecryptResponseSingle { - output_type: ct.ciphertext_type as i32, - value: deserialized.decrypt(&client_key), - })); - } - - decrypted - }) - .await - .unwrap(); - - values.sort_by_key(|(h, _)| { - ct_indexes.get(h.as_slice()).unwrap() - }); - - let values = values.into_iter().map(|i| i.1).collect::>(); - - return Ok(tonic::Response::new(DebugDecryptResponse { values })); - } - - async fn upload_ciphertexts( - &self, - request: tonic::Request, - ) -> std::result::Result, tonic::Status> { - let tenant_id = check_if_api_key_is_valid(&request, &self.pool).await?; - - let req = request.get_ref(); - - // TODO: check if ciphertext deserializes into type correctly - // TODO: check for duplicate handles in the input - // TODO: check if ciphertext doesn't exist already - // TODO: if ciphertexts exists check that it is equal to the one being uploaded - - let mut trx = self - .pool - .begin() - .await - .map_err(Into::::into)?; - for i_ct in &req.input_ciphertexts { - let ciphertext_type: i16 = i_ct - .ciphertext_type - .try_into() - .map_err(|_e| CoprocessorError::FhevmError(FhevmError::UnknownFheType(i_ct.ciphertext_type)))?; - let _ = sqlx::query!(" - INSERT INTO ciphertexts(tenant_id, handle, ciphertext, ciphertext_version, ciphertext_type) - VALUES($1, $2, $3, $4, $5) - ON CONFLICT (tenant_id, handle, ciphertext_version) DO NOTHING - ", tenant_id, i_ct.ciphertext_handle, i_ct.ciphertext_bytes, current_ciphertext_version(), ciphertext_type) - .execute(trx.as_mut()).await.map_err(Into::::into)?; - } - - trx.commit().await.map_err(Into::::into)?; - - return Ok(tonic::Response::new(GenericResponse { response_code: 0 })); - } - } diff --git a/fhevm-engine/coprocessor/src/tests/errors.rs b/fhevm-engine/coprocessor/src/tests/errors.rs index 13e7d35c..a2086e1d 100644 --- a/fhevm-engine/coprocessor/src/tests/errors.rs +++ b/fhevm-engine/coprocessor/src/tests/errors.rs @@ -1,7 +1,21 @@ use std::str::FromStr; +use crate::{ + db_queries::query_tenant_keys, + server::{ + common::FheOperation, + coprocessor::{ + async_computation_input::Input, fhevm_coprocessor_client::FhevmCoprocessorClient, + AsyncComputation, AsyncComputationInput, AsyncComputeRequest, InputToUpload, + InputUploadBatch, + }, + }, + tests::{ + inputs::{test_random_caller_address, test_random_contract_address}, + utils::{default_api_key, default_tenant_id, setup_test_app}, + }, +}; use tonic::metadata::MetadataValue; -use crate::{db_queries::query_tenant_keys, server::{common::FheOperation, coprocessor::{async_computation_input::Input, fhevm_coprocessor_client::FhevmCoprocessorClient, AsyncComputation, AsyncComputationInput, AsyncComputeRequest, InputToUpload, InputUploadBatch}}, tests::{inputs::{test_random_caller_address, test_random_contract_address}, utils::{default_api_key, default_tenant_id, setup_test_app}}}; #[tokio::test] async fn test_coprocessor_input_errors() -> Result<(), Box> { @@ -13,13 +27,16 @@ async fn test_coprocessor_input_errors() -> Result<(), Box = e; - e - })?; + let keys = query_tenant_keys(vec![default_tenant_id()], &pool) + .await + .map_err(|e| { + let e: Box = e; + e + })?; let keys = &keys[0]; - { // too many uploads at once + { + // too many uploads at once let mut builder = tfhe::CompactCiphertextListBuilder::new(&keys.pks); let the_list = builder .push(false) @@ -42,9 +59,7 @@ async fn test_coprocessor_input_errors() -> Result<(), Box Result<(), Box { - assert!(e.to_string().contains("More than maximum input blobs uploaded, maximum allowed: 10, uploaded: 12")); + assert!(e.to_string().contains( + "More than maximum input blobs uploaded, maximum allowed: 10, uploaded: 12" + )); } Ok(_) => { panic!("Should not have succeeded") @@ -60,7 +77,8 @@ async fn test_coprocessor_input_errors() -> Result<(), Box Result<(), Box Result<(), Box Result<(), Box Result<(), Box { eprintln!("error: {e}"); - assert!(e.to_string().contains("Input blob contains too many ciphertexts")); + assert!(e + .to_string() + .contains("Input blob contains too many ciphertexts")); } Ok(_) => { panic!("Should not have succeeded") @@ -136,13 +153,12 @@ async fn test_coprocessor_input_errors() -> Result<(), Box Result<(), Box Result<(), Box { - assert!(e.to_string().contains("API key unknown/invalid/not provided")); + assert!(e + .to_string() + .contains("API key unknown/invalid/not provided")); } Ok(_) => { panic!("Should not have succeeded") @@ -180,7 +199,8 @@ async fn test_coprocessor_api_key_errors() -> Result<(), Box Result<(), Box { - assert!(e.to_string().contains("API key unknown/invalid/not provided")); + assert!(e + .to_string() + .contains("API key unknown/invalid/not provided")); } Ok(_) => { panic!("Should not have succeeded") @@ -201,7 +223,8 @@ async fn test_coprocessor_api_key_errors() -> Result<(), Box Result<(), Box { - assert!(e.to_string().contains("API key unknown/invalid/not provided")); + assert!(e + .to_string() + .contains("API key unknown/invalid/not provided")); } Ok(_) => { panic!("Should not have succeeded") @@ -236,10 +261,12 @@ async fn test_coprocessor_computation_errors() -> Result<(), Box = e; - e - })?; + let keys = query_tenant_keys(vec![default_tenant_id()], &pool) + .await + .map_err(|e| { + let e: Box = e; + e + })?; let keys = &keys[0]; let mut handle_counter = 0; @@ -249,7 +276,8 @@ async fn test_coprocessor_computation_errors() -> Result<(), Box Result<(), Box Result<(), Box Result<(), Box { eprintln!("error: {}", e); - assert!(e.to_string().contains("has circular dependency and is uncomputable")); + assert!(e + .to_string() + .contains("has circular dependency and is uncomputable")); } } } - { // test invalid binary op between uncast types + { + // test invalid binary op between uncast types let output_handle_a = next_handle(); - let async_computations = vec![ - AsyncComputation { - operation: FheOperation::FheAdd.into(), - output_handle: output_handle_a.clone(), - inputs: vec![ - AsyncComputationInput { - input: Some(Input::InputHandle(test_u8.handle.clone())), - }, - AsyncComputationInput { - input: Some(Input::InputHandle(test_u16.handle.clone())), - }, - ], - }, - ]; + let async_computations = vec![AsyncComputation { + operation: FheOperation::FheAdd.into(), + output_handle: output_handle_a.clone(), + inputs: vec![ + AsyncComputationInput { + input: Some(Input::InputHandle(test_u8.handle.clone())), + }, + AsyncComputationInput { + input: Some(Input::InputHandle(test_u16.handle.clone())), + }, + ], + }]; let mut input_request = tonic::Request::new(AsyncComputeRequest { computations: async_computations, }); @@ -383,27 +411,28 @@ async fn test_coprocessor_computation_errors() -> Result<(), Box { eprintln!("error: {}", e); - assert!(e.to_string().contains("fhevm error: FheOperationDoesntHaveUniformTypesAsInput")); + assert!(e + .to_string() + .contains("fhevm error: FheOperationDoesntHaveUniformTypesAsInput")); } } } - { // empty ciphertext handle + { + // empty ciphertext handle let output_handle_a = next_handle(); - let async_computations = vec![ - AsyncComputation { - operation: FheOperation::FheAdd.into(), - output_handle: output_handle_a.clone(), - inputs: vec![ - AsyncComputationInput { - input: Some(Input::InputHandle(test_u32.handle.clone())), - }, - AsyncComputationInput { - input: Some(Input::InputHandle(vec![])), - }, - ], - }, - ]; + let async_computations = vec![AsyncComputation { + operation: FheOperation::FheAdd.into(), + output_handle: output_handle_a.clone(), + inputs: vec![ + AsyncComputationInput { + input: Some(Input::InputHandle(test_u32.handle.clone())), + }, + AsyncComputationInput { + input: Some(Input::InputHandle(vec![])), + }, + ], + }]; let mut input_request = tonic::Request::new(AsyncComputeRequest { computations: async_computations, }); @@ -422,22 +451,21 @@ async fn test_coprocessor_computation_errors() -> Result<(), Box Result<(), Box { eprintln!("error: {}", e); - assert!(e.to_string().contains("Found ciphertext handle longer than 64 bytes")); + assert!(e + .to_string() + .contains("Found ciphertext handle longer than 64 bytes")); } } } - { // computation too many inputs + { + // computation too many inputs let output_handle_a = next_handle(); - let async_computations = vec![ - AsyncComputation { - operation: FheOperation::FheAdd.into(), - output_handle: output_handle_a.clone(), - inputs: vec![ - AsyncComputationInput { - input: Some(Input::InputHandle(test_u64.handle.clone())), - }, - AsyncComputationInput { - input: Some(Input::InputHandle(test_u64.handle.clone())), - }, - AsyncComputationInput { - input: Some(Input::InputHandle(test_u64.handle.clone())), - }, - ], - }, - ]; + let async_computations = vec![AsyncComputation { + operation: FheOperation::FheAdd.into(), + output_handle: output_handle_a.clone(), + inputs: vec![ + AsyncComputationInput { + input: Some(Input::InputHandle(test_u64.handle.clone())), + }, + AsyncComputationInput { + input: Some(Input::InputHandle(test_u64.handle.clone())), + }, + AsyncComputationInput { + input: Some(Input::InputHandle(test_u64.handle.clone())), + }, + ], + }]; let mut input_request = tonic::Request::new(AsyncComputeRequest { computations: async_computations, }); @@ -488,27 +517,28 @@ async fn test_coprocessor_computation_errors() -> Result<(), Box { eprintln!("error: {}", e); - assert!(e.to_string().contains("fhevm error: UnexpectedOperandCountForFheOperation")); + assert!(e + .to_string() + .contains("fhevm error: UnexpectedOperandCountForFheOperation")); } } } - { // scalar operand on the left + { + // scalar operand on the left let output_handle_a = next_handle(); - let async_computations = vec![ - AsyncComputation { - operation: FheOperation::FheAdd.into(), - output_handle: output_handle_a.clone(), - inputs: vec![ - AsyncComputationInput { - input: Some(Input::Scalar(vec![123])), - }, - AsyncComputationInput { - input: Some(Input::InputHandle(test_u64.handle.clone())), - }, - ], - }, - ]; + let async_computations = vec![AsyncComputation { + operation: FheOperation::FheAdd.into(), + output_handle: output_handle_a.clone(), + inputs: vec![ + AsyncComputationInput { + input: Some(Input::Scalar(vec![123])), + }, + AsyncComputationInput { + input: Some(Input::InputHandle(test_u64.handle.clone())), + }, + ], + }]; let mut input_request = tonic::Request::new(AsyncComputeRequest { computations: async_computations, }); @@ -522,27 +552,28 @@ async fn test_coprocessor_computation_errors() -> Result<(), Box { eprintln!("error: {}", e); - assert!(e.to_string().contains("fhevm error: FheOperationOnlySecondOperandCanBeScalar")); + assert!(e + .to_string() + .contains("fhevm error: FheOperationOnlySecondOperandCanBeScalar")); } } } - { // scalar division by zero + { + // scalar division by zero let output_handle_a = next_handle(); - let async_computations = vec![ - AsyncComputation { - operation: FheOperation::FheDiv.into(), - output_handle: output_handle_a.clone(), - inputs: vec![ - AsyncComputationInput { - input: Some(Input::InputHandle(test_u64.handle.clone())), - }, - AsyncComputationInput { - input: Some(Input::Scalar(vec![0])), - }, - ], - }, - ]; + let async_computations = vec![AsyncComputation { + operation: FheOperation::FheDiv.into(), + output_handle: output_handle_a.clone(), + inputs: vec![ + AsyncComputationInput { + input: Some(Input::InputHandle(test_u64.handle.clone())), + }, + AsyncComputationInput { + input: Some(Input::Scalar(vec![0])), + }, + ], + }]; let mut input_request = tonic::Request::new(AsyncComputeRequest { computations: async_computations, }); @@ -556,27 +587,28 @@ async fn test_coprocessor_computation_errors() -> Result<(), Box { eprintln!("error: {}", e); - assert!(e.to_string().contains("fhevm error: FheOperationScalarDivisionByZero")); + assert!(e + .to_string() + .contains("fhevm error: FheOperationScalarDivisionByZero")); } } } - { // binary boolean inputs + { + // binary boolean inputs let output_handle_a = next_handle(); - let async_computations = vec![ - AsyncComputation { - operation: FheOperation::FheAdd.into(), - output_handle: output_handle_a.clone(), - inputs: vec![ - AsyncComputationInput { - input: Some(Input::InputHandle(test_bool.handle.clone())), - }, - AsyncComputationInput { - input: Some(Input::InputHandle(test_bool.handle.clone())), - }, - ], - }, - ]; + let async_computations = vec![AsyncComputation { + operation: FheOperation::FheAdd.into(), + output_handle: output_handle_a.clone(), + inputs: vec![ + AsyncComputationInput { + input: Some(Input::InputHandle(test_bool.handle.clone())), + }, + AsyncComputationInput { + input: Some(Input::InputHandle(test_bool.handle.clone())), + }, + ], + }]; let mut input_request = tonic::Request::new(AsyncComputeRequest { computations: async_computations, }); @@ -590,24 +622,23 @@ async fn test_coprocessor_computation_errors() -> Result<(), Box { eprintln!("error: {}", e); - assert!(e.to_string().contains("fhevm error: OperationDoesntSupportBooleanInputs")); + assert!(e + .to_string() + .contains("fhevm error: OperationDoesntSupportBooleanInputs")); } } } - { // unary boolean inputs + { + // unary boolean inputs let output_handle_a = next_handle(); - let async_computations = vec![ - AsyncComputation { - operation: FheOperation::FheNeg.into(), - output_handle: output_handle_a.clone(), - inputs: vec![ - AsyncComputationInput { - input: Some(Input::InputHandle(test_bool.handle.clone())), - }, - ], - }, - ]; + let async_computations = vec![AsyncComputation { + operation: FheOperation::FheNeg.into(), + output_handle: output_handle_a.clone(), + inputs: vec![AsyncComputationInput { + input: Some(Input::InputHandle(test_bool.handle.clone())), + }], + }]; let mut input_request = tonic::Request::new(AsyncComputeRequest { computations: async_computations, }); @@ -621,10 +652,12 @@ async fn test_coprocessor_computation_errors() -> Result<(), Box { eprintln!("error: {}", e); - assert!(e.to_string().contains("fhevm error: OperationDoesntSupportBooleanInputs")); + assert!(e + .to_string() + .contains("fhevm error: OperationDoesntSupportBooleanInputs")); } } } Ok(()) -} \ No newline at end of file +} diff --git a/fhevm-engine/coprocessor/src/tests/inputs.rs b/fhevm-engine/coprocessor/src/tests/inputs.rs index 3b2894a1..269bf34c 100644 --- a/fhevm-engine/coprocessor/src/tests/inputs.rs +++ b/fhevm-engine/coprocessor/src/tests/inputs.rs @@ -3,7 +3,13 @@ use std::str::FromStr; use tfhe::integer::{bigint::StaticUnsignedBigInt, U256}; use tonic::metadata::MetadataValue; -use crate::{db_queries::query_tenant_keys, server::coprocessor::{fhevm_coprocessor_client::FhevmCoprocessorClient, DebugDecryptRequest, InputToUpload, InputUploadBatch}, tests::utils::{default_api_key, default_tenant_id, setup_test_app}}; +use crate::{ + db_queries::query_tenant_keys, + server::coprocessor::{ + fhevm_coprocessor_client::FhevmCoprocessorClient, InputToUpload, InputUploadBatch, + }, + tests::utils::{decrypt_ciphertexts, default_api_key, default_tenant_id, setup_test_app}, +}; pub fn test_random_caller_address() -> String { let _private_key = "bd2400c676871534a682ca1c5e4cd647ec9c3e122f188c6e3f54e6900d586c7b"; @@ -25,10 +31,12 @@ async fn test_fhe_inputs() -> Result<(), Box> { .connect(app.db_url()) .await?; - let keys = query_tenant_keys(vec![default_tenant_id()], &pool).await.map_err(|e| { - let e: Box = e; - e - })?; + let keys = query_tenant_keys(vec![default_tenant_id()], &pool) + .await + .map_err(|e| { + let e: Box = e; + e + })?; let keys = &keys[0]; let mut builder = tfhe::CompactCiphertextListBuilder::new(&keys.pks); @@ -50,14 +58,12 @@ async fn test_fhe_inputs() -> Result<(), Box> { println!("Encrypting inputs..."); let mut input_request = tonic::Request::new(InputUploadBatch { - input_ciphertexts: vec![ - InputToUpload { - input_payload: serialized, - signature: Vec::new(), - caller_address: test_random_caller_address(), - contract_address: test_random_contract_address(), - } - ] + input_ciphertexts: vec![InputToUpload { + input_payload: serialized, + signature: Vec::new(), + caller_address: test_random_caller_address(), + contract_address: test_random_contract_address(), + }], }); input_request.metadata_mut().append( "authorization", @@ -76,37 +82,29 @@ async fn test_fhe_inputs() -> Result<(), Box> { decr_handles.push(handle.handle.clone()); } - let mut decrypt_request = tonic::Request::new(DebugDecryptRequest { - handles: decr_handles, - }); - decrypt_request.metadata_mut().append( - "authorization", - MetadataValue::from_str(&api_key_header).unwrap(), - ); - let resp = client.debug_decrypt_ciphertext(decrypt_request).await?; - let resp = resp.get_ref(); - assert_eq!(resp.values.len(), 10); - - assert_eq!(resp.values[0].output_type, 0); - assert_eq!(resp.values[0].value, "false"); - assert_eq!(resp.values[1].output_type, 2); - assert_eq!(resp.values[1].value, "1"); - assert_eq!(resp.values[2].output_type, 3); - assert_eq!(resp.values[2].value, "2"); - assert_eq!(resp.values[3].output_type, 4); - assert_eq!(resp.values[3].value, "3"); - assert_eq!(resp.values[4].output_type, 5); - assert_eq!(resp.values[4].value, "4"); - assert_eq!(resp.values[5].output_type, 6); - assert_eq!(resp.values[5].value, "5"); - assert_eq!(resp.values[6].output_type, 8); - assert_eq!(resp.values[6].value, "7"); - assert_eq!(resp.values[7].output_type, 9); - assert_eq!(resp.values[7].value, "8"); - assert_eq!(resp.values[8].output_type, 10); - assert_eq!(resp.values[8].value, "9"); - assert_eq!(resp.values[9].output_type, 11); - assert_eq!(resp.values[9].value, "10"); + let resp = decrypt_ciphertexts(&pool, 1, decr_handles).await?; + assert_eq!(resp.len(), 10); + + assert_eq!(resp[0].output_type, 0); + assert_eq!(resp[0].value, "false"); + assert_eq!(resp[1].output_type, 2); + assert_eq!(resp[1].value, "1"); + assert_eq!(resp[2].output_type, 3); + assert_eq!(resp[2].value, "2"); + assert_eq!(resp[3].output_type, 4); + assert_eq!(resp[3].value, "3"); + assert_eq!(resp[4].output_type, 5); + assert_eq!(resp[4].value, "4"); + assert_eq!(resp[5].output_type, 6); + assert_eq!(resp[5].value, "5"); + assert_eq!(resp[6].output_type, 8); + assert_eq!(resp[6].value, "7"); + assert_eq!(resp[7].output_type, 9); + assert_eq!(resp[7].value, "8"); + assert_eq!(resp[8].output_type, 10); + assert_eq!(resp[8].value, "9"); + assert_eq!(resp[9].output_type, 11); + assert_eq!(resp[9].value, "10"); Ok(()) } @@ -126,10 +124,12 @@ async fn custom_insert_inputs() -> Result<(), Box> { .connect(&db_url) .await?; - let keys = query_tenant_keys(vec![default_tenant_id()], &pool).await.map_err(|e| { - let e: Box = e; - e - })?; + let keys = query_tenant_keys(vec![default_tenant_id()], &pool) + .await + .map_err(|e| { + let e: Box = e; + e + })?; let keys = &keys[0]; let mut builder = tfhe::CompactCiphertextListBuilder::new(&keys.pks); @@ -146,14 +146,12 @@ async fn custom_insert_inputs() -> Result<(), Box> { println!("Encrypting inputs..."); let mut input_request = tonic::Request::new(InputUploadBatch { - input_ciphertexts: vec![ - InputToUpload { - input_payload: serialized, - signature: Vec::new(), - caller_address: test_random_caller_address(), - contract_address: test_random_contract_address(), - } - ] + input_ciphertexts: vec![InputToUpload { + input_payload: serialized, + signature: Vec::new(), + caller_address: test_random_caller_address(), + contract_address: test_random_contract_address(), + }], }); input_request.metadata_mut().append( "authorization", @@ -172,32 +170,3 @@ async fn custom_insert_inputs() -> Result<(), Box> { Ok(()) } - -#[ignore] -#[tokio::test] -// custom function to decrypt the ciphertext from grpc -// ct_to_decrypt should be changed to your environment -async fn custom_decrypt_ct() -> Result<(), Box> { - let grpc_url = "http://127.0.0.1:50051"; - let api_key_header = format!("bearer {}", default_api_key()); - let ct_to_decrypt = "5bcaeef7d5bee3b5dffff3dfbfafcfb73cf57ddbbff73f777ffdfe677ebc0500"; - - let mut client = FhevmCoprocessorClient::connect(grpc_url).await?; - println!("Encrypting inputs..."); - let mut input_request = tonic::Request::new(DebugDecryptRequest { - handles: vec![ - hex::decode(ct_to_decrypt).unwrap() - ] - }); - input_request.metadata_mut().append( - "authorization", - MetadataValue::from_str(&api_key_header).unwrap(), - ); - - let uploaded = client.debug_decrypt_ciphertext(input_request).await?; - let response = uploaded.get_ref(); - - println!("{:#?}", response); - - Ok(()) -} \ No newline at end of file diff --git a/fhevm-engine/coprocessor/src/tests/mod.rs b/fhevm-engine/coprocessor/src/tests/mod.rs index 71d3b6c6..7dd6f893 100644 --- a/fhevm-engine/coprocessor/src/tests/mod.rs +++ b/fhevm-engine/coprocessor/src/tests/mod.rs @@ -1,23 +1,28 @@ use std::str::FromStr; +use crate::server::common::FheOperation; use crate::server::coprocessor::async_computation_input::Input; use crate::server::coprocessor::fhevm_coprocessor_client::FhevmCoprocessorClient; use crate::server::coprocessor::{ - AsyncComputation, AsyncComputationInput, AsyncComputeRequest, DebugDecryptRequest, - DebugEncryptRequest, DebugEncryptRequestSingle, + AsyncComputation, AsyncComputationInput, AsyncComputeRequest, TrivialEncryptBatch, + TrivialEncryptRequestSingle, }; -use crate::server::common::FheOperation; use tonic::metadata::MetadataValue; -use utils::{default_api_key, wait_until_all_ciphertexts_computed}; +use utils::{decrypt_ciphertexts, default_api_key, wait_until_all_ciphertexts_computed}; +mod errors; +mod inputs; mod operators; +mod random; mod utils; -mod inputs; -mod errors; #[tokio::test] async fn test_smoke() -> Result<(), Box> { let app = utils::setup_test_app().await?; + let pool = sqlx::postgres::PgPoolOptions::new() + .max_connections(2) + .connect(app.db_url()) + .await?; let mut client = FhevmCoprocessorClient::connect(app.app_url().to_string()).await?; @@ -26,14 +31,14 @@ async fn test_smoke() -> Result<(), Box> { // encrypt two ciphertexts { - let mut encrypt_request = tonic::Request::new(DebugEncryptRequest { + let mut encrypt_request = tonic::Request::new(TrivialEncryptBatch { values: vec![ - DebugEncryptRequestSingle { + TrivialEncryptRequestSingle { handle: vec![0x0a, 0xbc], be_value: vec![123], output_type: ct_type, }, - DebugEncryptRequestSingle { + TrivialEncryptRequestSingle { handle: vec![0x0a, 0xbd], be_value: vec![124], output_type: ct_type, @@ -44,7 +49,7 @@ async fn test_smoke() -> Result<(), Box> { "authorization", MetadataValue::from_str(&api_key_header).unwrap(), ); - let resp = client.debug_encrypt_ciphertext(encrypt_request).await?; + let resp = client.trivial_encrypt_ciphertexts(encrypt_request).await?; println!("encryption request: {:?}", resp); } @@ -91,23 +96,44 @@ async fn test_smoke() -> Result<(), Box> { // decrypt values { - let mut decrypt_request = tonic::Request::new(DebugDecryptRequest { - handles: vec![vec![0x0a, 0xbe], vec![0x0a, 0xbf]], - }); - decrypt_request.metadata_mut().append( - "authorization", - MetadataValue::from_str(&api_key_header).unwrap(), - ); - let resp = client.debug_decrypt_ciphertext(decrypt_request).await?; + let decrypt_request = vec![vec![0x0a, 0xbe], vec![0x0a, 0xbf]]; + let resp = decrypt_ciphertexts(&pool, 1, decrypt_request).await?; println!("decrypt request: {:?}", resp); - assert_eq!(resp.get_ref().values.len(), 2); + assert_eq!(resp.len(), 2); // first value - assert_eq!(resp.get_ref().values[0].value, "247"); - assert_eq!(resp.get_ref().values[0].output_type, ct_type); + assert_eq!(resp[0].value, "247"); + assert_eq!(resp[0].output_type, ct_type as i16); // second value - assert_eq!(resp.get_ref().values[1].value, "263"); - assert_eq!(resp.get_ref().values[1].output_type, ct_type); + assert_eq!(resp[1].value, "263"); + assert_eq!(resp[1].output_type, ct_type as i16); } + Ok(()) +} + +#[tokio::test] +#[ignore] +// custom test to run against local instance for decrypting custom ciphertexts +async fn test_custom_function() -> Result<(), Box> { + let pool = sqlx::postgres::PgPoolOptions::new() + .max_connections(2) + .connect("postgresql://postgres:postgres@127.0.0.1:5432/coprocessor") + .await?; + + let res = utils::decrypt_ciphertexts(&pool, 1, vec![ + hex::decode("de2c33227b24ca797f7ad88495648446c70612c17f416d27513c77f2d0810200").unwrap(), + hex::decode("51d1d882d1e5ce54f15523558edd2746766c14cd5177faeb659418c57cec0200").unwrap(), + hex::decode("e3935354c48514fdfb0cbd965ad506d8865a2c88efffffca94dc9e0cecec0300").unwrap(), + hex::decode("3eed1ad1d1aa030b3bb3d3587ece4661a56945affcdee6bbdc02e28779380200").unwrap(), + hex::decode("55fe0c4283fbad83dc6fab91c3f85c098ada7a70ca8089e3076043efc9c60200").unwrap(), + hex::decode("3b42e61e197b88c083b4a2ab4b0ec542775e2282bebcc574e45d09f9779a0200").unwrap(), + hex::decode("9718b490a41e20fecaa90a7ab75e74de0c4105213ac3e5d8b5368ab813160200").unwrap(), + hex::decode("164c6d678ddf95f12bfa6b0fee7fd8b12e6221bd0c587640ae61dfc624f20200").unwrap(), + hex::decode("52a01af58c3d2b8ed1d04cd846706c1d214b72e079bd0930827628cb69180200").unwrap(), + hex::decode("d8d493764d46b62187b6a42917d58e297922d9ebab0dee306324e8c78a130200").unwrap(), + ]).await.unwrap(); + + println!("{:#?}", res); + Ok(()) } \ No newline at end of file diff --git a/fhevm-engine/coprocessor/src/tests/operators.rs b/fhevm-engine/coprocessor/src/tests/operators.rs index 75e4da78..95923d1e 100644 --- a/fhevm-engine/coprocessor/src/tests/operators.rs +++ b/fhevm-engine/coprocessor/src/tests/operators.rs @@ -1,20 +1,22 @@ +use crate::server::common::FheOperation; use crate::server::coprocessor::fhevm_coprocessor_client::FhevmCoprocessorClient; use crate::server::coprocessor::{ - AsyncComputation, AsyncComputeRequest, DebugDecryptRequest, DebugEncryptRequest, - DebugEncryptRequestSingle, + AsyncComputation, AsyncComputeRequest, TrivialEncryptBatch, TrivialEncryptRequestSingle, +}; +use crate::tests::utils::{ + decrypt_ciphertexts, random_handle_start, wait_until_all_ciphertexts_computed, }; -use crate::server::common::FheOperation; -use crate::tests::utils::wait_until_all_ciphertexts_computed; use crate::{ server::coprocessor::{async_computation_input::Input, AsyncComputationInput}, tests::utils::{default_api_key, setup_test_app}, }; use bigdecimal::num_bigint::BigInt; -use fhevm_engine_common::tfhe_ops::{does_fhe_operation_support_both_encrypted_operands, does_fhe_operation_support_scalar}; +use fhevm_engine_common::tfhe_ops::{ + does_fhe_operation_support_both_encrypted_operands, does_fhe_operation_support_scalar, +}; use fhevm_engine_common::types::{FheOperationType, SupportedFheOperations}; -use random::Source; -use strum::IntoEnumIterator; use std::{ops::Not, str::FromStr}; +use strum::IntoEnumIterator; use tonic::metadata::MetadataValue; struct BinaryOperatorTestCase { @@ -37,32 +39,21 @@ struct UnaryOperatorTestCase { } fn supported_bits() -> &'static [i32] { - &[ - 8, - 16, - 32, - 64, - 128, - 160, - 256, - 512, - 1024, - 2048, - ] + &[8, 16, 32, 64, 128, 160, 256, 512, 1024, 2048] } -fn supported_types() -> &'static [i32] { +pub fn supported_types() -> &'static [i32] { &[ 0, // bool // 1, TODO: add 4 bit support - 2, // 8 bit - 3, // 16 bit - 4, // 32 bit - 5, // 64 bit - 6, // 128 bit - 7, // 160 bit - 8, // 256 bit - 9, // ebytes 64 + 2, // 8 bit + 3, // 16 bit + 4, // 32 bit + 5, // 64 bit + 6, // 128 bit + 7, // 160 bit + 8, // 256 bit + 9, // ebytes 64 10, // ebytes 128 11, // ebytes 256 ] @@ -84,17 +75,14 @@ fn supported_bits_to_bit_type_in_db(inp: i32) -> i32 { } } -fn random_handle_start() -> u64 { - let time = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH) - .unwrap().as_nanos(); - let mut random_gen = random::default(time as u64); - random_gen.read_u64() -} - #[tokio::test] async fn test_fhe_binary_operands() -> Result<(), Box> { let ops = generate_binary_test_cases(); let app = setup_test_app().await?; + let pool = sqlx::postgres::PgPoolOptions::new() + .max_connections(2) + .connect(app.db_url()) + .await?; let mut client = FhevmCoprocessorClient::connect(app.app_url().to_string()).await?; let mut handle_counter: u64 = random_handle_start(); @@ -130,14 +118,14 @@ async fn test_fhe_binary_operands() -> Result<(), Box> { op.lhs.to_string(), op.rhs.to_string() ); - enc_request_payload.push(DebugEncryptRequestSingle { + enc_request_payload.push(TrivialEncryptRequestSingle { handle: lhs_handle.clone(), be_value: lhs_bytes, output_type: op.input_types, }); if !op.is_scalar { let (_, rhs_bytes) = op.rhs.to_bytes_be(); - enc_request_payload.push(DebugEncryptRequestSingle { + enc_request_payload.push(TrivialEncryptRequestSingle { handle: rhs_handle.clone(), be_value: rhs_bytes, output_type: op.input_types, @@ -168,14 +156,14 @@ async fn test_fhe_binary_operands() -> Result<(), Box> { } println!("Encrypting inputs..."); - let mut encrypt_request = tonic::Request::new(DebugEncryptRequest { + let mut encrypt_request = tonic::Request::new(TrivialEncryptBatch { values: enc_request_payload, }); encrypt_request.metadata_mut().append( "authorization", MetadataValue::from_str(&api_key_header).unwrap(), ); - let _resp = client.debug_encrypt_ciphertext(encrypt_request).await?; + let _resp = client.trivial_encrypt_ciphertexts(encrypt_request).await?; println!("Scheduling computations..."); let mut compute_request = tonic::Request::new(AsyncComputeRequest { @@ -191,26 +179,20 @@ async fn test_fhe_binary_operands() -> Result<(), Box> { wait_until_all_ciphertexts_computed(&app).await?; - let mut decrypt_request = tonic::Request::new(DebugDecryptRequest { - handles: output_handles.clone(), - }); - decrypt_request.metadata_mut().append( - "authorization", - MetadataValue::from_str(&api_key_header).unwrap(), - ); - let resp = client.debug_decrypt_ciphertext(decrypt_request).await?; + let decrypt_request = output_handles.clone(); + let resp = decrypt_ciphertexts(&pool, 1, decrypt_request).await?; assert_eq!( - resp.get_ref().values.len(), + resp.len(), output_handles.len(), "Outputs length doesn't match" ); for (idx, op) in ops.iter().enumerate() { - let decr_response = &resp.get_ref().values[idx]; + let decr_response = &resp[idx]; println!("Checking computation for binary test bits:{} op:{} is_scalar:{} lhs:{} rhs:{} output:{}", op.bits, op.operand, op.is_scalar, op.lhs.to_string(), op.rhs.to_string(), op.expected_output.to_string()); assert_eq!( - decr_response.output_type, op.expected_output_type, + decr_response.output_type, op.expected_output_type as i16, "operand types not equal" ); let value_to_compare = match decr_response.value.as_str() { @@ -233,6 +215,10 @@ async fn test_fhe_binary_operands() -> Result<(), Box> { async fn test_fhe_unary_operands() -> Result<(), Box> { let ops = generate_unary_test_cases(); let app = setup_test_app().await?; + let pool = sqlx::postgres::PgPoolOptions::new() + .max_connections(2) + .connect(app.db_url()) + .await?; let mut client = FhevmCoprocessorClient::connect(app.app_url().to_string()).await?; let mut handle_counter: u64 = random_handle_start(); @@ -260,7 +246,7 @@ async fn test_fhe_unary_operands() -> Result<(), Box> { op.operand, op.inp.to_string() ); - enc_request_payload.push(DebugEncryptRequestSingle { + enc_request_payload.push(TrivialEncryptRequestSingle { handle: input_handle.clone(), be_value: inp_bytes, output_type: op.operand_types, @@ -283,14 +269,14 @@ async fn test_fhe_unary_operands() -> Result<(), Box> { } println!("Encrypting inputs..."); - let mut encrypt_request = tonic::Request::new(DebugEncryptRequest { + let mut encrypt_request = tonic::Request::new(TrivialEncryptBatch { values: enc_request_payload, }); encrypt_request.metadata_mut().append( "authorization", MetadataValue::from_str(&api_key_header).unwrap(), ); - let _resp = client.debug_encrypt_ciphertext(encrypt_request).await?; + let _resp = client.trivial_encrypt_ciphertexts(encrypt_request).await?; println!("Scheduling computations..."); let mut compute_request = tonic::Request::new(AsyncComputeRequest { @@ -306,22 +292,16 @@ async fn test_fhe_unary_operands() -> Result<(), Box> { wait_until_all_ciphertexts_computed(&app).await?; - let mut decrypt_request = tonic::Request::new(DebugDecryptRequest { - handles: output_handles.clone(), - }); - decrypt_request.metadata_mut().append( - "authorization", - MetadataValue::from_str(&api_key_header).unwrap(), - ); - let resp = client.debug_decrypt_ciphertext(decrypt_request).await?; + let decrypt_request = output_handles.clone(); + let resp = decrypt_ciphertexts(&pool, 1, decrypt_request).await?; assert_eq!( - resp.get_ref().values.len(), + resp.len(), output_handles.len(), "Outputs length doesn't match" ); for (idx, op) in ops.iter().enumerate() { - let decr_response = &resp.get_ref().values[idx]; + let decr_response = &resp[idx]; println!( "Checking computation for binary test bits:{} op:{} input:{} output:{}", op.bits, @@ -330,7 +310,7 @@ async fn test_fhe_unary_operands() -> Result<(), Box> { op.expected_output.to_string() ); assert_eq!( - decr_response.output_type, op.operand_types, + decr_response.output_type, op.operand_types as i16, "operand types not equal" ); assert_eq!( @@ -346,6 +326,10 @@ async fn test_fhe_unary_operands() -> Result<(), Box> { #[tokio::test] async fn test_fhe_casts() -> Result<(), Box> { let app = setup_test_app().await?; + let pool = sqlx::postgres::PgPoolOptions::new() + .max_connections(2) + .connect(app.db_url()) + .await?; let mut client = FhevmCoprocessorClient::connect(app.app_url().to_string()).await?; let mut handle_counter = random_handle_start(); @@ -385,7 +369,7 @@ async fn test_fhe_casts() -> Result<(), Box> { println!( "Encrypting inputs for cast test type from:{type_from} type to:{type_to} input:{input} output:{output}", ); - enc_request_payload.push(DebugEncryptRequestSingle { + enc_request_payload.push(TrivialEncryptRequestSingle { handle: input_handle.clone(), be_value: inp_bytes, output_type: *type_from, @@ -418,14 +402,14 @@ async fn test_fhe_casts() -> Result<(), Box> { } println!("Encrypting inputs..."); - let mut encrypt_request = tonic::Request::new(DebugEncryptRequest { + let mut encrypt_request = tonic::Request::new(TrivialEncryptBatch { values: enc_request_payload, }); encrypt_request.metadata_mut().append( "authorization", MetadataValue::from_str(&api_key_header).unwrap(), ); - let _resp = client.debug_encrypt_ciphertext(encrypt_request).await?; + let _resp = client.trivial_encrypt_ciphertexts(encrypt_request).await?; println!("Scheduling computations..."); let mut compute_request = tonic::Request::new(AsyncComputeRequest { @@ -441,22 +425,16 @@ async fn test_fhe_casts() -> Result<(), Box> { wait_until_all_ciphertexts_computed(&app).await?; - let mut decrypt_request = tonic::Request::new(DebugDecryptRequest { - handles: output_handles.clone(), - }); - decrypt_request.metadata_mut().append( - "authorization", - MetadataValue::from_str(&api_key_header).unwrap(), - ); - let resp = client.debug_decrypt_ciphertext(decrypt_request).await?; + let decrypt_request = output_handles.clone(); + let resp = decrypt_ciphertexts(&pool, 1, decrypt_request).await?; assert_eq!( - resp.get_ref().values.len(), + resp.len(), output_handles.len(), "Outputs length doesn't match" ); for (idx, co) in cast_outputs.iter().enumerate() { - let decr_response = &resp.get_ref().values[idx]; + let decr_response = &resp[idx]; println!( "Checking computation for cast test from:{} to:{} input:{} output:{}", co.type_from, co.type_to, co.input, co.expected_result, @@ -466,7 +444,7 @@ async fn test_fhe_casts() -> Result<(), Box> { decr_response.output_type, decr_response.value ); assert_eq!( - decr_response.output_type, co.type_to, + decr_response.output_type, co.type_to as i16, "operand types not equal" ); assert_eq!( @@ -481,6 +459,10 @@ async fn test_fhe_casts() -> Result<(), Box> { #[tokio::test] async fn test_fhe_if_then_else() -> Result<(), Box> { let app = setup_test_app().await?; + let pool = sqlx::postgres::PgPoolOptions::new() + .max_connections(2) + .connect(app.db_url()) + .await?; let mut client = FhevmCoprocessorClient::connect(app.app_url().to_string()).await?; let mut handle_counter = random_handle_start(); @@ -508,12 +490,12 @@ async fn test_fhe_if_then_else() -> Result<(), Box> { let fhe_bool_type = 0; let false_handle = next_handle(); let true_handle = next_handle(); - enc_request_payload.push(DebugEncryptRequestSingle { + enc_request_payload.push(TrivialEncryptRequestSingle { handle: false_handle.clone(), be_value: BigInt::from(0).to_bytes_be().1, output_type: fhe_bool_type, }); - enc_request_payload.push(DebugEncryptRequestSingle { + enc_request_payload.push(TrivialEncryptRequestSingle { handle: true_handle.clone(), be_value: BigInt::from(1).to_bytes_be().1, output_type: fhe_bool_type, @@ -524,18 +506,13 @@ async fn test_fhe_if_then_else() -> Result<(), Box> { let left_handle = next_handle(); let right_handle = next_handle(); let is_input_bool = *input_types == fhe_bool_type; - let (left_input, right_input) = - if is_input_bool { - (0, 1) - } else { - (7, 12) - }; - enc_request_payload.push(DebugEncryptRequestSingle { + let (left_input, right_input) = if is_input_bool { (0, 1) } else { (7, 12) }; + enc_request_payload.push(TrivialEncryptRequestSingle { handle: left_handle.clone(), be_value: BigInt::from(left_input).to_bytes_be().1, output_type: *input_types, }); - enc_request_payload.push(DebugEncryptRequestSingle { + enc_request_payload.push(TrivialEncryptRequestSingle { handle: right_handle.clone(), be_value: BigInt::from(right_input).to_bytes_be().1, output_type: *input_types, @@ -545,7 +522,9 @@ async fn test_fhe_if_then_else() -> Result<(), Box> { let output_handle = next_handle(); let (expected_result, input_handle) = if test_value { (left_input, &true_handle) - } else { (right_input, &false_handle) }; + } else { + (right_input, &false_handle) + }; if_then_else_outputs.push(IfThenElseOutput { input_type: *input_types, input_bool: test_value, @@ -578,14 +557,14 @@ async fn test_fhe_if_then_else() -> Result<(), Box> { } println!("Encrypting inputs..."); - let mut encrypt_request = tonic::Request::new(DebugEncryptRequest { + let mut encrypt_request = tonic::Request::new(TrivialEncryptBatch { values: enc_request_payload, }); encrypt_request.metadata_mut().append( "authorization", MetadataValue::from_str(&api_key_header).unwrap(), ); - let _resp = client.debug_encrypt_ciphertext(encrypt_request).await?; + let _resp = client.trivial_encrypt_ciphertexts(encrypt_request).await?; println!("Scheduling computations..."); let mut compute_request = tonic::Request::new(AsyncComputeRequest { @@ -601,22 +580,16 @@ async fn test_fhe_if_then_else() -> Result<(), Box> { wait_until_all_ciphertexts_computed(&app).await?; - let mut decrypt_request = tonic::Request::new(DebugDecryptRequest { - handles: output_handles.clone(), - }); - decrypt_request.metadata_mut().append( - "authorization", - MetadataValue::from_str(&api_key_header).unwrap(), - ); - let resp = client.debug_decrypt_ciphertext(decrypt_request).await?; + let decrypt_request = output_handles.clone(); + let resp = decrypt_ciphertexts(&pool, 1, decrypt_request).await?; assert_eq!( - resp.get_ref().values.len(), + resp.len(), output_handles.len(), "Outputs length doesn't match" ); for (idx, co) in if_then_else_outputs.iter().enumerate() { - let decr_response = &resp.get_ref().values[idx]; + let decr_response = &resp[idx]; println!( "Checking if then else computation for test type:{} control:{} lhs:{} rhs:{} output:{}", co.input_type, co.input_bool, co.left_input, co.right_input, co.expected_result, @@ -626,7 +599,7 @@ async fn test_fhe_if_then_else() -> Result<(), Box> { decr_response.output_type, decr_response.value ); assert_eq!( - decr_response.output_type, co.input_type, + decr_response.output_type, co.input_type as i16, "operand types not equal" ); assert_eq!( @@ -743,7 +716,7 @@ fn compute_expected_unary_output(inp: &BigInt, op: SupportedFheOperations) -> Bi } let num = BigInt::from_bytes_be(bigdecimal::num_bigint::Sign::Plus, &bytes); num + 1 - }, + } other => panic!("unsupported unary operation: {:?}", other), } } diff --git a/fhevm-engine/coprocessor/src/tests/random.rs b/fhevm-engine/coprocessor/src/tests/random.rs new file mode 100644 index 00000000..e6941ce4 --- /dev/null +++ b/fhevm-engine/coprocessor/src/tests/random.rs @@ -0,0 +1,243 @@ +use std::str::FromStr; + +use bigdecimal::num_bigint::BigInt; +use tonic::metadata::MetadataValue; + +use crate::{ + server::{ + common::FheOperation, + coprocessor::{ + async_computation_input::Input, fhevm_coprocessor_client::FhevmCoprocessorClient, + AsyncComputation, AsyncComputationInput, AsyncComputeRequest, + }, + }, + tests::utils::{ + decrypt_ciphertexts, default_api_key, random_handle_start, setup_test_app, + wait_until_all_ciphertexts_computed, DecryptionResult, + }, +}; + +use super::operators::supported_types; + +#[tokio::test] +async fn test_fhe_random_basic() -> Result<(), Box> { + let app = setup_test_app().await?; + let pool = sqlx::postgres::PgPoolOptions::new() + .max_connections(2) + .connect(app.db_url()) + .await?; + let mut client = FhevmCoprocessorClient::connect(app.app_url().to_string()).await?; + + let mut handle_counter = random_handle_start(); + let mut next_handle = || { + let out: u64 = handle_counter; + handle_counter += 1; + out.to_be_bytes().to_vec() + }; + + let api_key_header = format!("bearer {}", default_api_key()); + + let mut async_computations = Vec::new(); + let mut output_handles = Vec::new(); + // second batch + let mut repeated_output_handles = Vec::new(); + let mut other_seed_output_handles = Vec::new(); + + let deterministic_seed = 123u8; + for the_type in supported_types() { + let output_handle = next_handle(); + output_handles.push(output_handle.clone()); + + async_computations.push(AsyncComputation { + operation: FheOperation::FheRand.into(), + output_handle, + inputs: vec![ + AsyncComputationInput { + input: Some(Input::Scalar(vec![deterministic_seed])), + }, + AsyncComputationInput { + input: Some(Input::Scalar(vec![*the_type as u8])), + }, + ], + }); + } + + for the_type in supported_types() { + let output_handle = next_handle(); + repeated_output_handles.push(output_handle.clone()); + + async_computations.push(AsyncComputation { + operation: FheOperation::FheRand.into(), + output_handle, + inputs: vec![ + AsyncComputationInput { + input: Some(Input::Scalar(vec![deterministic_seed])), + }, + AsyncComputationInput { + input: Some(Input::Scalar(vec![*the_type as u8])), + }, + ], + }); + } + + let deterministic_seed = 124u8; + for the_type in supported_types() { + let output_handle = next_handle(); + other_seed_output_handles.push(output_handle.clone()); + + async_computations.push(AsyncComputation { + operation: FheOperation::FheRand.into(), + output_handle, + inputs: vec![ + AsyncComputationInput { + input: Some(Input::Scalar(vec![deterministic_seed])), + }, + AsyncComputationInput { + input: Some(Input::Scalar(vec![*the_type as u8])), + }, + ], + }); + } + println!("Scheduling computations..."); + let mut compute_request = tonic::Request::new(AsyncComputeRequest { + computations: async_computations, + }); + compute_request.metadata_mut().append( + "authorization", + MetadataValue::from_str(&api_key_header).unwrap(), + ); + let _resp = client.async_compute(compute_request).await?; + println!("Computations scheduled, waiting upon completion..."); + + wait_until_all_ciphertexts_computed(&app).await?; + + let decrypt_request = output_handles.clone(); + let resp = decrypt_ciphertexts(&pool, 1, decrypt_request).await?; + let expected: Vec = vec![ + DecryptionResult { value: "true".to_string(), output_type: 0 }, + DecryptionResult { value: "246".to_string(), output_type: 2 }, + DecryptionResult { value: "63017".to_string(), output_type: 3 }, + DecryptionResult { value: "4129931760".to_string(), output_type: 4 }, + DecryptionResult { value: "17737921846358948632".to_string(), output_type: 5 }, + DecryptionResult { value: "327206904699245123432435293866544047473".to_string(), output_type: 6 }, + DecryptionResult { value: "1405342954708646521029792852792956072442307244669".to_string(), output_type: 7 }, + DecryptionResult { value: "111342740003933073063880775186901842883137555493599211263718834336721313367065".to_string(), output_type: 8 }, + DecryptionResult { value: "12892608486462714192025621397040078423423955713740464502763239020120634468299230123105397307872954410250318097947282133574275938210017581341447804344613993".to_string(), output_type: 9 }, + DecryptionResult { value: "172861618302440003871985169870765936350085874287296918960492143478488761618246504771315475060233667593872334502225689924250171990509708174838253519566820157798395010064387130671854353305667120118078771871328847226561467056702239601377047878285846377269405811576578942463596956091674468702130036387127918822209".to_string(), output_type: 10 }, + DecryptionResult { value: "31075214450348645370562638259461457156660301565210267906013240737336053083827159054203712794280858545976020541340200687848819992364752918396584991833648247977557620662493453593968051971701814968601733859743322792627162168039060394006823897396302176759600031160814967191408374105854249751828206266805375926002417511561943897787495111082912113112803400667069378922271635394928755975139359534582265256642037856166688849734457088884995628268940935666204883226039421296557591795669918923722604457778199088364855244515207994602899710427947199154593190772832070461956037473623162197147067799409973348481724693194438433297577".to_string(), output_type: 11 } + ]; + + assert_eq!(expected, resp,); + + let resp_repeated = decrypt_ciphertexts(&pool, 1, repeated_output_handles).await?; + assert_eq!( + resp, resp_repeated, + "randomness generation is not deterministic" + ); + + let resp_repeated = decrypt_ciphertexts(&pool, 1, other_seed_output_handles).await?; + assert_ne!(resp, resp_repeated, "seed has change, so must the values"); + + Ok(()) +} + +fn to_be_bytes(input: &str) -> Vec { + let num = BigInt::from_str(input).unwrap(); + let (_, bytes_be) = num.to_bytes_be(); + bytes_be +} + +#[tokio::test] +async fn test_fhe_random_bounded() -> Result<(), Box> { + let app = setup_test_app().await?; + let pool = sqlx::postgres::PgPoolOptions::new() + .max_connections(2) + .connect(app.db_url()) + .await?; + let mut client = FhevmCoprocessorClient::connect(app.app_url().to_string()).await?; + + let mut handle_counter = random_handle_start(); + let mut next_handle = || { + let out: u64 = handle_counter; + handle_counter += 1; + out.to_be_bytes().to_vec() + }; + + let api_key_header = format!("bearer {}", default_api_key()); + + let mut async_computations = Vec::new(); + let mut output_handles = Vec::new(); + + let deterministic_seed = 123u8; + let bounds = [ + "0", + "200", + "60000", + "4000000000", + "10000000000000000000", + "300000000000000000000000000000000000000", + "100000000000000000000000000000000000000000000000", + "100000000000000000000000000000000000000000000000000000000000000000000000000000", + ]; + let results = [ + "false", + "46", + "3017", + "129931760", + "7737921846358948632", + "27206904699245123432435293866544047473", + "5342954708646521029792852792956072442307244669", + "11342740003933073063880775186901842883137555493599211263718834336721313367065", + ]; + + for (idx, the_type) in supported_types().iter().enumerate() { + if *the_type > 8 { + // don't support bounded numbers larger than 256 scalar type + break; + } + + let output_handle = next_handle(); + output_handles.push(output_handle.clone()); + + async_computations.push(AsyncComputation { + operation: FheOperation::FheRandBounded.into(), + output_handle, + inputs: vec![ + AsyncComputationInput { + input: Some(Input::Scalar(vec![deterministic_seed])), + }, + AsyncComputationInput { + input: Some(Input::Scalar(to_be_bytes(bounds[idx]))), + }, + AsyncComputationInput { + input: Some(Input::Scalar(vec![*the_type as u8])), + }, + ], + }); + } + + println!("Scheduling computations..."); + let mut compute_request = tonic::Request::new(AsyncComputeRequest { + computations: async_computations, + }); + compute_request.metadata_mut().append( + "authorization", + MetadataValue::from_str(&api_key_header).unwrap(), + ); + let _resp = client.async_compute(compute_request).await?; + println!("Computations scheduled, waiting upon completion..."); + + wait_until_all_ciphertexts_computed(&app).await?; + + let decrypt_request = output_handles.clone(); + let resp = decrypt_ciphertexts(&pool, 1, decrypt_request).await?; + assert_eq!(resp.len(), bounds.len()); + + println!("response: {:#?}", resp); + for idx in 0..bounds.len() { + assert_eq!(resp[idx].output_type, supported_types()[idx] as i16); + assert_eq!(resp[idx].value, results[idx]); + } + + Ok(()) +} diff --git a/fhevm-engine/coprocessor/src/tests/utils.rs b/fhevm-engine/coprocessor/src/tests/utils.rs index 256a55f3..84095b63 100644 --- a/fhevm-engine/coprocessor/src/tests/utils.rs +++ b/fhevm-engine/coprocessor/src/tests/utils.rs @@ -1,4 +1,7 @@ use crate::cli::Args; +use fhevm_engine_common::tfhe_ops::{current_ciphertext_version, deserialize_fhe_ciphertext}; +use rand::RngCore; +use std::collections::BTreeMap; use std::sync::atomic::{AtomicU16, Ordering}; use testcontainers::{core::WaitFor, runners::AsyncRunner, GenericImage, ImageExt}; @@ -46,7 +49,8 @@ pub async fn setup_test_app() -> Result } } -pub async fn setup_test_app_existing_localhost() -> Result> { +pub async fn setup_test_app_existing_localhost() -> Result> +{ Ok(TestInstance { _container: None, app_close_channel: None, @@ -130,7 +134,9 @@ pub async fn setup_test_app_custom_docker() -> Result Result<(), Box> { +pub async fn wait_until_all_ciphertexts_computed( + test_instance: &TestInstance, +) -> Result<(), Box> { let pool = sqlx::postgres::PgPoolOptions::new() .max_connections(2) .connect(test_instance.db_url()) @@ -138,11 +144,9 @@ pub async fn wait_until_all_ciphertexts_computed(test_instance: &TestInstance) - loop { tokio::time::sleep(tokio::time::Duration::from_secs(3)).await; - let count = sqlx::query!( - "SELECT count(*) FROM computations WHERE NOT is_completed" - ) - .fetch_one(&pool) - .await?; + let count = sqlx::query!("SELECT count(*) FROM computations WHERE NOT is_completed") + .fetch_one(&pool) + .await?; let current_count = count.count.unwrap(); if current_count == 0 { println!("All computations completed"); @@ -153,4 +157,89 @@ pub async fn wait_until_all_ciphertexts_computed(test_instance: &TestInstance) - } Ok(()) -} \ No newline at end of file +} + +#[derive(Debug, PartialEq, Eq)] +pub struct DecryptionResult { + pub value: String, + pub output_type: i16, +} + +pub async fn decrypt_ciphertexts( + pool: &sqlx::PgPool, + tenant_id: i32, + input: Vec>, +) -> Result, Box> { + let mut priv_key = sqlx::query!( + " + SELECT cks_key + FROM tenants + WHERE tenant_id = $1 + ", + tenant_id + ) + .fetch_all(pool) + .await?; + + if priv_key.is_empty() || priv_key[0].cks_key.is_none() { + panic!("tenant private key not found"); + } + + let mut ct_indexes: BTreeMap<&[u8], usize> = BTreeMap::new(); + for (idx, h) in input.iter().enumerate() { + ct_indexes.insert(h.as_slice(), idx); + } + + assert_eq!(priv_key.len(), 1); + + let cts = sqlx::query!( + " + SELECT ciphertext, ciphertext_type, handle + FROM ciphertexts + WHERE tenant_id = $1 + AND handle = ANY($2::BYTEA[]) + AND ciphertext_version = $3 + ", + tenant_id, + &input, + current_ciphertext_version() + ) + .fetch_all(pool) + .await?; + + if cts.is_empty() { + panic!("ciphertext not found"); + } + + let priv_key = priv_key.pop().unwrap().cks_key.unwrap(); + + let mut values = tokio::task::spawn_blocking(move || { + let client_key: tfhe::ClientKey = bincode::deserialize(&priv_key).unwrap(); + + let mut decrypted: Vec<(Vec, DecryptionResult)> = Vec::with_capacity(cts.len()); + for ct in cts { + let deserialized = + deserialize_fhe_ciphertext(ct.ciphertext_type, &ct.ciphertext).unwrap(); + decrypted.push(( + ct.handle, + DecryptionResult { + output_type: ct.ciphertext_type, + value: deserialized.decrypt(&client_key), + }, + )); + } + + decrypted + }) + .await + .unwrap(); + + values.sort_by_key(|(h, _)| ct_indexes.get(h.as_slice()).unwrap()); + + let values = values.into_iter().map(|i| i.1).collect::>(); + Ok(values) +} + +pub fn random_handle_start() -> u64 { + rand::thread_rng().next_u64() +} diff --git a/fhevm-engine/coprocessor/src/tfhe_worker.rs b/fhevm-engine/coprocessor/src/tfhe_worker.rs index e13dba54..7ca97df2 100644 --- a/fhevm-engine/coprocessor/src/tfhe_worker.rs +++ b/fhevm-engine/coprocessor/src/tfhe_worker.rs @@ -1,8 +1,9 @@ -use fhevm_engine_common::tfhe_ops::{ - current_ciphertext_version, deserialize_fhe_ciphertext, perform_fhe_operation, -}; use crate::{db_queries::populate_cache_with_tenant_keys, types::TfheTenantKeys}; use fhevm_engine_common::types::SupportedFheCiphertexts; +use fhevm_engine_common::{ + tfhe_ops::{current_ciphertext_version, deserialize_fhe_ciphertext, perform_fhe_operation}, + types::SupportedFheOperations, +}; use sqlx::{postgres::PgListener, query, Acquire}; use std::{ cell::Cell, @@ -71,7 +72,12 @@ async fn tfhe_worker_cycle( FROM unnest(c.dependencies) WITH ORDINALITY AS elems(v, dep_index) WHERE (c.tenant_id, elems.v) NOT IN ( SELECT tenant_id, handle FROM ciphertexts ) -- don't select scalar operands - AND ( NOT c.is_scalar OR c.is_scalar AND NOT elems.dep_index = 2 ) + AND ( + NOT c.is_scalar + OR c.is_scalar AND NOT elems.dep_index = 2 + ) + -- ignore fhe random operations, all inputs are scalars + AND NOT c.fhe_operation = ANY(ARRAY[26, 27]) ) LIMIT $1 FOR UPDATE SKIP LOCKED @@ -142,17 +148,21 @@ async fn tfhe_worker_cycle( // process every tenant by tenant id because we must switch keys for each tenant for w in the_work { let tenant_key_cache = tenant_key_cache.clone(); + let fhe_op: SupportedFheOperations = w + .fhe_operation + .try_into() + .expect("only valid fhe ops must have been put in db"); let mut work_ciphertexts: Vec<(i16, Vec)> = Vec::with_capacity(w.dependencies.len()); for (idx, dh) in w.dependencies.iter().enumerate() { - let is_operand_scalar = w.is_scalar && idx == 1; + let is_operand_scalar = w.is_scalar && idx == 1 || fhe_op.is_random(); if is_operand_scalar { work_ciphertexts.push((-1, dh.clone())); } else { - let ct_map_val = ciphertext_map - .get(&(w.tenant_id, &dh)) - .expect("Me must get ciphertext here"); + let ct_map_val = ciphertext_map.get(&(w.tenant_id, &dh)).expect( + "must get ciphertext here, we should have selected all dependencies", + ); work_ciphertexts .push((ct_map_val.ciphertext_type, ct_map_val.ciphertext.clone())); } @@ -166,19 +176,22 @@ async fn tfhe_worker_cycle( } // set thread tenant key - if w.tenant_id != TFHE_TENANT_ID.get() { + let tenant_entropy = { let mut rk = tenant_key_cache.blocking_write(); let keys = rk .get(&w.tenant_id) .expect("Can't get tenant key from cache"); - tfhe::set_server_key(keys.sks.clone()); - TFHE_TENANT_ID.set(w.tenant_id); - } + if w.tenant_id != TFHE_TENANT_ID.get() { + tfhe::set_server_key(keys.sks.clone()); + TFHE_TENANT_ID.set(w.tenant_id); + } + keys.tenant_entropy() + }; let mut deserialized_cts: Vec = Vec::with_capacity(work_ciphertexts.len()); for (idx, (ct_type, ct_bytes)) in work_ciphertexts.iter().enumerate() { - let is_operand_scalar = w.is_scalar && idx == 1; + let is_operand_scalar = w.is_scalar && idx == 1 || fhe_op.is_random(); if is_operand_scalar { let mut the_int = tfhe::integer::U256::default(); assert!( @@ -196,20 +209,23 @@ async fn tfhe_worker_cycle( deserialized_cts.push(SupportedFheCiphertexts::Scalar(the_int)); } else { deserialized_cts.push( - deserialize_fhe_ciphertext(*ct_type, ct_bytes.as_slice()) - .map_err(|e| { - let err: Box = Box::new(e); + deserialize_fhe_ciphertext(*ct_type, ct_bytes.as_slice()).map_err( + |e| { + let err: Box = + Box::new(e); (err, w.tenant_id, w.output_handle.clone()) - })?, + }, + )?, ); } } - let res = perform_fhe_operation(w.fhe_operation, &deserialized_cts) - .map_err(|e| { - let err: Box = Box::new(e); - (err, w.tenant_id, w.output_handle.clone()) - })?; + let res = + perform_fhe_operation(w.fhe_operation, &deserialized_cts, &tenant_entropy) + .map_err(|e| { + let err: Box = Box::new(e); + (err, w.tenant_id, w.output_handle.clone()) + })?; let (db_type, db_bytes) = res.serialize(); Ok((w, db_type, db_bytes)) diff --git a/fhevm-engine/coprocessor/src/types.rs b/fhevm-engine/coprocessor/src/types.rs index 7470e3ec..adec7402 100644 --- a/fhevm-engine/coprocessor/src/types.rs +++ b/fhevm-engine/coprocessor/src/types.rs @@ -1,4 +1,7 @@ +use std::str::FromStr; + use fhevm_engine_common::types::FhevmError; +use sha3::{Digest, Keccak256}; #[derive(Debug)] pub enum CoprocessorError { @@ -61,12 +64,22 @@ impl std::fmt::Display for CoprocessorError { write!(f, "Duplicate output handle in ciphertext batch: {}", op) } Self::DuplicateResultHandleInInputsUploaded { hex_handle } => { - write!(f, "Duplicate result handle in inputs detected: {hex_handle}") + write!( + f, + "Duplicate result handle in inputs detected: {hex_handle}" + ) } - Self::MoreThanMaximumCompactInputCiphertextsUploaded { input_count, maximum_allowed } => { + Self::MoreThanMaximumCompactInputCiphertextsUploaded { + input_count, + maximum_allowed, + } => { write!(f, "More than maximum input blobs uploaded, maximum allowed: {maximum_allowed}, uploaded: {input_count}") } - Self::CompactInputCiphertextHasMoreCiphertextThanLimitAllows { input_blob_index, input_ciphertexts_in_blob, input_maximum_ciphertexts_allowed } => { + Self::CompactInputCiphertextHasMoreCiphertextThanLimitAllows { + input_blob_index, + input_ciphertexts_in_blob, + input_maximum_ciphertexts_allowed, + } => { write!(f, "Input blob contains too many ciphertexts, input blob index: {input_blob_index}, ciphertexts in blob: {input_ciphertexts_in_blob}, maximum ciphertexts in blob allowed: {input_maximum_ciphertexts_allowed}") } Self::CiphertextHandleLongerThan64Bytes => { @@ -88,28 +101,28 @@ impl std::fmt::Display for CoprocessorError { handle ) } - Self::CannotParseTenantEthereumAddress { bad_address, parsing_error } => { + Self::CannotParseTenantEthereumAddress { + bad_address, + parsing_error, + } => { write!( f, "Cannot parse tenant ethereum verifying contract address: {}, error: {}", - bad_address, - parsing_error, + bad_address, parsing_error, ) } - Self::CannotParseEthereumAddress { bad_address, parsing_error } => { + Self::CannotParseEthereumAddress { + bad_address, + parsing_error, + } => { write!( f, "Cannot parse ethereum address: {}, error: {}", - bad_address, - parsing_error, + bad_address, parsing_error, ) } Self::Eip712SigningFailure { error } => { - write!( - f, - "Error when signing EIP712 hash: {}", - error, - ) + write!(f, "Error when signing EIP712 hash: {}", error,) } Self::CiphertextComputationDependencyLoopDetected { uncomputable_output_handle, @@ -162,3 +175,17 @@ pub struct TfheTenantKeys { #[allow(dead_code)] pub pks: tfhe::CompactPublicKey, } + +impl TfheTenantKeys { + /// Should be always deterministic random 32 bytes per tenant + pub fn tenant_entropy(&self) -> [u8; 32] { + let chain_id: u64 = self.chain_id as u64; + let mut parsed_address = + alloy::primitives::Address::from_str(&self.verifying_contract_address) + .expect("we should have checked earlier that address parses") + .to_vec(); + let chain_id_bytes = chain_id.to_be_bytes(); + parsed_address.extend_from_slice(&chain_id_bytes); + Keccak256::digest(&parsed_address).into() + } +} diff --git a/fhevm-engine/coprocessor/src/utils.rs b/fhevm-engine/coprocessor/src/utils.rs index 4cfaff49..9fff2311 100644 --- a/fhevm-engine/coprocessor/src/utils.rs +++ b/fhevm-engine/coprocessor/src/utils.rs @@ -1,6 +1,6 @@ use std::collections::{BTreeSet, HashMap, HashSet}; -use fhevm_engine_common::types::FhevmError; +use fhevm_engine_common::types::{FhevmError, SupportedFheOperations}; #[cfg(test)] use crate::server::coprocessor::AsyncComputationInput; @@ -46,6 +46,10 @@ pub fn sort_computations_by_dependencies<'a>( for (idx, comp) in input.iter().enumerate() { let mut this_deps = Vec::with_capacity(comp.inputs.len()); let mut this_scalar_operands = Vec::with_capacity(comp.inputs.len()); + let fhe_op: SupportedFheOperations = comp + .operation + .try_into() + .map_err(|e| CoprocessorError::FhevmError(e))?; for (dep_idx, ih) in comp.inputs.iter().enumerate() { let mut is_scalar_operand = false; if let Some(ih_input) = &ih.input { @@ -70,13 +74,13 @@ pub fn sort_computations_by_dependencies<'a>( } Input::Scalar(sc_bytes) => { check_valid_ciphertext_handle(&sc_bytes)?; - if dep_idx != 1 { + if dep_idx != 1 && !fhe_op.is_random() { // TODO: remove wrapping after refactor return Err(CoprocessorError::FhevmError( FhevmError::FheOperationOnlySecondOperandCanBeScalar { scalar_input_index: dep_idx, only_allowed_scalar_input_index: 1, - } + }, )); } is_scalar_operand = true; @@ -145,7 +149,10 @@ pub fn sort_computations_by_dependencies<'a>( "0x{}", hex::encode(&first_uncomputable_handle) ), - uncomputable_handle_dependency: format!("0x{}", hex::encode(first_uncomputable_handle_dependency)), + uncomputable_handle_dependency: format!( + "0x{}", + hex::encode(first_uncomputable_handle_dependency) + ), }, ); } diff --git a/fhevm-engine/executor/src/server.rs b/fhevm-engine/executor/src/server.rs index 66b0c8af..90a7e61a 100644 --- a/fhevm-engine/executor/src/server.rs +++ b/fhevm-engine/executor/src/server.rs @@ -257,7 +257,7 @@ impl FhevmExecutorService { // Do the computation on the inputs. match inputs { - Ok(inputs) => match perform_fhe_operation(comp.operation as i16, &inputs) { + Ok(inputs) => match perform_fhe_operation(comp.operation as i16, &inputs, &[0; 32]) { Ok(result) => { let compressed = result.clone().compress(); state.ciphertexts.insert( diff --git a/fhevm-engine/fhevm-engine-common/Cargo.toml b/fhevm-engine/fhevm-engine-common/Cargo.toml index 8665ca64..858e65d2 100644 --- a/fhevm-engine/fhevm-engine-common/Cargo.toml +++ b/fhevm-engine/fhevm-engine-common/Cargo.toml @@ -15,6 +15,8 @@ strum = { version = "0.26", features = ["derive"] } bincode = "1.3.3" hex = "0.4" bigdecimal = "0.4.5" +rand_chacha = "0.3.1" +rand = "0.8.5" [[bin]] name = "generate-keys" diff --git a/fhevm-engine/fhevm-engine-common/src/tfhe_ops.rs b/fhevm-engine/fhevm-engine-common/src/tfhe_ops.rs index c7e644f2..7687b4aa 100644 --- a/fhevm-engine/fhevm-engine-common/src/tfhe_ops.rs +++ b/fhevm-engine/fhevm-engine-common/src/tfhe_ops.rs @@ -1,4 +1,8 @@ use crate::types::{is_ebytes_type, FheOperationType, FhevmError, SupportedFheCiphertexts, SupportedFheOperations}; +use bigdecimal::num_bigint::BigInt; +use rand_chacha::rand_core::SeedableRng; +use rand::Rng; +use sha3::{Digest, Keccak256}; use tfhe::{ integer::{bigint::StaticUnsignedBigInt, U256}, prelude::{ CastInto, FheEq, FheMax, FheMin, FheOrd, FheTryTrivialEncrypt, IfThenElse, RotateLeft, RotateRight @@ -72,7 +76,7 @@ pub fn deserialize_fhe_ciphertext( } /// Function assumes encryption key already set -pub fn debug_trivial_encrypt_be_bytes( +pub fn trivial_encrypt_be_bytes( output_type: i16, input_bytes: &[u8], ) -> SupportedFheCiphertexts { @@ -86,7 +90,12 @@ pub fn debug_trivial_encrypt_be_bytes( 3 => { let mut padded: [u8; 2] = [0; 2]; let padded_len = padded.len(); - let copy_from = padded_len - input_bytes.len(); + let copy_from = + if padded_len >= input_bytes.len() { + padded_len - input_bytes.len() + } else { + 0 + }; let len = padded.len().min(input_bytes.len()); padded[copy_from..padded_len].copy_from_slice(&input_bytes[0..len]); let res = u16::from_be_bytes(padded); @@ -95,7 +104,12 @@ pub fn debug_trivial_encrypt_be_bytes( 4 => { let mut padded: [u8; 4] = [0; 4]; let padded_len = padded.len(); - let copy_from = padded_len - input_bytes.len(); + let copy_from = + if padded_len >= input_bytes.len() { + padded_len - input_bytes.len() + } else { + 0 + }; let len = padded.len().min(input_bytes.len()); padded[copy_from..padded_len].copy_from_slice(&input_bytes[0..len]); let res: u32 = u32::from_be_bytes(padded); @@ -104,7 +118,12 @@ pub fn debug_trivial_encrypt_be_bytes( 5 => { let mut padded: [u8; 8] = [0; 8]; let padded_len = padded.len(); - let copy_from = padded_len - input_bytes.len(); + let copy_from = + if padded_len >= input_bytes.len() { + padded_len - input_bytes.len() + } else { + 0 + }; let len = padded.len().min(input_bytes.len()); padded[copy_from..padded_len].copy_from_slice(&input_bytes[0..len]); let res: u64 = u64::from_be_bytes(padded); @@ -113,7 +132,12 @@ pub fn debug_trivial_encrypt_be_bytes( 6 => { let mut padded: [u8; 16] = [0; 16]; let padded_len = padded.len(); - let copy_from = padded_len - input_bytes.len(); + let copy_from = + if padded_len >= input_bytes.len() { + padded_len - input_bytes.len() + } else { + 0 + }; let len = padded.len().min(input_bytes.len()); padded[copy_from..padded_len].copy_from_slice(&input_bytes[0..len]); let res: u128 = u128::from_be_bytes(padded); @@ -123,7 +147,12 @@ pub fn debug_trivial_encrypt_be_bytes( 7 => { let mut padded: [u8; 32] = [0; 32]; let padded_len = padded.len(); - let copy_from = padded_len - input_bytes.len(); + let copy_from = + if padded_len >= input_bytes.len() { + padded_len - input_bytes.len() + } else { + 0 + }; let len = padded.len().min(input_bytes.len()); padded[copy_from..padded_len].copy_from_slice(&input_bytes[0..len]); let mut be: U256 = U256::ZERO; @@ -135,7 +164,12 @@ pub fn debug_trivial_encrypt_be_bytes( 8 => { let mut padded: [u8; 32] = [0; 32]; let padded_len = padded.len(); - let copy_from = padded_len - input_bytes.len(); + let copy_from = + if padded_len >= input_bytes.len() { + padded_len - input_bytes.len() + } else { + 0 + }; let len = padded.len().min(input_bytes.len()); padded[copy_from..padded_len].copy_from_slice(&input_bytes[0..len]); let mut be: U256 = U256::ZERO; @@ -146,7 +180,12 @@ pub fn debug_trivial_encrypt_be_bytes( 9 => { let mut padded: [u8; 64] = [0; 64]; let padded_len = padded.len(); - let copy_from = padded_len - input_bytes.len(); + let copy_from = + if padded_len >= input_bytes.len() { + padded_len - input_bytes.len() + } else { + 0 + }; let len = padded.len().min(input_bytes.len()); padded[copy_from..padded_len].copy_from_slice(&input_bytes[0..len]); let mut be: StaticUnsignedBigInt<8> = StaticUnsignedBigInt::<8>::ZERO; @@ -157,7 +196,12 @@ pub fn debug_trivial_encrypt_be_bytes( 10 => { let mut padded: [u8; 128] = [0; 128]; let padded_len = padded.len(); - let copy_from = padded_len - input_bytes.len(); + let copy_from = + if padded_len >= input_bytes.len() { + padded_len - input_bytes.len() + } else { + 0 + }; let len = padded.len().min(input_bytes.len()); padded[copy_from..padded_len].copy_from_slice(&input_bytes[0..len]); let mut be: StaticUnsignedBigInt<16> = StaticUnsignedBigInt::<16>::ZERO; @@ -168,7 +212,12 @@ pub fn debug_trivial_encrypt_be_bytes( 11 => { let mut padded: [u8; 256] = [0; 256]; let padded_len = padded.len(); - let copy_from = padded_len - input_bytes.len(); + let copy_from = + if padded_len >= input_bytes.len() { + padded_len - input_bytes.len() + } else { + 0 + }; let len = padded.len().min(input_bytes.len()); padded[copy_from..padded_len].copy_from_slice(&input_bytes[0..len]); let mut be: StaticUnsignedBigInt<32> = StaticUnsignedBigInt::<32>::ZERO; @@ -314,9 +363,9 @@ pub fn check_fhe_operand_types( input_handles: &[Vec], is_input_handle_scalar: &[bool], ) -> Result { - assert_eq!(input_handles.len(), is_input_handle_scalar.len()); - let fhe_op: SupportedFheOperations = fhe_operation.try_into()?; + + assert_eq!(input_handles.len(), is_input_handle_scalar.len()); // TODO: figure out typing system with constants let fhe_bool_type = 0; @@ -328,37 +377,41 @@ pub fn check_fhe_operand_types( let is_scalar = scalar_operands.len() > 0; - if scalar_operands.len() > 1 { - return Err(FhevmError::FheOperationOnlyOneOperandCanBeScalar { - fhe_operation, - fhe_operation_name: format!("{:?}", fhe_op), - scalar_operand_count: scalar_operands.len(), - max_scalar_operands: 1, - }); - } - - if is_scalar { - assert_eq!( - scalar_operands.len(), - 1, - "We checked already that not more than 1 scalar operand can be present" - ); - - if !does_fhe_operation_support_scalar(&fhe_op) { - return Err(FhevmError::FheOperationDoesntSupportScalar { + // do this check for only random ops because + // all random ops inputs are scalar + if !fhe_op.is_random() { + if scalar_operands.len() > 1 { + return Err(FhevmError::FheOperationOnlyOneOperandCanBeScalar { fhe_operation, fhe_operation_name: format!("{:?}", fhe_op), - scalar_requested: is_scalar, - scalar_supported: false, + scalar_operand_count: scalar_operands.len(), + max_scalar_operands: 1, }); } - let scalar_input_index = scalar_operands[0].0; - if scalar_input_index != 1 { - return Err(FhevmError::FheOperationOnlySecondOperandCanBeScalar { - scalar_input_index, - only_allowed_scalar_input_index: 1, - }); + if is_scalar { + assert_eq!( + scalar_operands.len(), + 1, + "We checked already that not more than 1 scalar operand can be present" + ); + + if !does_fhe_operation_support_scalar(&fhe_op) { + return Err(FhevmError::FheOperationDoesntSupportScalar { + fhe_operation, + fhe_operation_name: format!("{:?}", fhe_op), + scalar_requested: is_scalar, + scalar_supported: false, + }); + } + + let scalar_input_index = scalar_operands[0].0; + if scalar_input_index != 1 { + return Err(FhevmError::FheOperationOnlySecondOperandCanBeScalar { + scalar_input_index, + only_allowed_scalar_input_index: 1, + }); + } } } @@ -549,6 +602,97 @@ pub fn check_fhe_operand_types( } } } + SupportedFheOperations::FheRand => { + // counter and output type + let expected_operands = 2; + if input_types.len() != expected_operands { + return Err(FhevmError::UnexpectedOperandCountForFheOperation { + fhe_operation, + fhe_operation_name: format!("{:?}", fhe_op), + expected_operands, + got_operands: input_types.len(), + }); + } + + let scalar_operands = is_input_handle_scalar.iter().filter(|i| **i).count(); + if scalar_operands < expected_operands { + return Err( + FhevmError::RandOperationInputsMustAllBeScalar { + fhe_operation, + fhe_operation_name: format!("{:?}", fhe_op), + scalar_operand_count: scalar_operands, + expected_scalar_operand_count: expected_operands, + }, + ); + } + + let rand_type = &input_handles[1]; + if rand_type.len() != 1 { + return Err( + FhevmError::UnexpectedRandOperandSizeForOutputType { + fhe_operation, + fhe_operation_name: format!("{:?}", fhe_op), + expected_operand_bytes: 1, + got_bytes: rand_type.len(), + }, + ); + } + + validate_fhe_type(rand_type[0] as i32)?; + + Ok(rand_type[0] as i16) + } + SupportedFheOperations::FheRandBounded => { + // counter, bound and output type + let expected_operands = 3; + if input_types.len() != expected_operands { + return Err(FhevmError::UnexpectedOperandCountForFheOperation { + fhe_operation, + fhe_operation_name: format!("{:?}", fhe_op), + expected_operands, + got_operands: input_types.len(), + }); + } + + let scalar_operands = is_input_handle_scalar.iter().filter(|i| **i).count(); + if scalar_operands < expected_operands { + return Err( + FhevmError::RandOperationInputsMustAllBeScalar { + fhe_operation, + fhe_operation_name: format!("{:?}", fhe_op), + scalar_operand_count: scalar_operands, + expected_scalar_operand_count: expected_operands, + }, + ); + } + + let upper_bound = &input_handles[1]; + if upper_bound.is_empty() && upper_bound.iter().all(|i| *i == 0) { + return Err( + FhevmError::RandOperationUpperBoundCannotBeZero { + fhe_operation, + fhe_operation_name: format!("{:?}", fhe_op), + upper_bound_value: format!("0x{}", hex::encode(upper_bound)), + }, + ); + } + + let rand_type = &input_handles[2]; + if rand_type.len() != 1 { + return Err( + FhevmError::UnexpectedRandOperandSizeForOutputType { + fhe_operation, + fhe_operation_name: format!("{:?}", fhe_op), + expected_operand_bytes: 1, + got_bytes: rand_type.len(), + }, + ); + } + + validate_fhe_type(rand_type[0] as i32)?; + + Ok(rand_type[0] as i16) + } other => { panic!("Unexpected branch: {:?}", other) } @@ -604,6 +748,8 @@ fn trim_to_160bit(inp: &U256) -> U256 { pub fn perform_fhe_operation( fhe_operation_int: i16, input_operands: &[SupportedFheCiphertexts], + // for deterministc randomness functions + entropy: &[u8; 32], ) -> Result { let fhe_operation: SupportedFheOperations = fhe_operation_int.try_into()?; match fhe_operation { @@ -2617,8 +2763,92 @@ pub fn perform_fhe_operation( panic!("unknown cast pair") } }, - SupportedFheOperations::FheRand => todo!("Implement FheRand"), - SupportedFheOperations::FheRandBounded => todo!("Implement FheRandBounded"), + SupportedFheOperations::FheRand => { + let mut randomness_array = entropy.to_vec(); + randomness_array.extend_from_slice(&[0; 32]); + assert_eq!(randomness_array.len(), 64); + let SupportedFheCiphertexts::Scalar(rand_counter) = &input_operands[0] else { + panic!("we should have checked we have only scalar operands here") + }; + let SupportedFheCiphertexts::Scalar(to_type) = &input_operands[1] else { + panic!("we should have checked we have only scalar operands here") + }; + let (to_type, _) = to_type.to_low_high_u128(); + rand_counter.copy_to_be_byte_slice(&mut randomness_array[32..64]); + let digest = Keccak256::digest(&randomness_array); + let mut fixed_digest: [u8; 32] = [0; 32]; + fixed_digest.copy_from_slice(digest.as_slice()); + let mut generator = rand_chacha::ChaCha20Rng::from_seed(fixed_digest); + // 256 bytes biggest number we support + let bytes_size = type_bytes_size(to_type as i16); + let mut random_bytes: Vec = Vec::with_capacity(bytes_size); + for _ in 0..bytes_size { + random_bytes.push(generator.gen()); + } + + Ok(trivial_encrypt_be_bytes(to_type as i16, &random_bytes)) + }, + SupportedFheOperations::FheRandBounded => { + let mut randomness_array = entropy.to_vec(); + randomness_array.extend_from_slice(&[0; 32]); + assert_eq!(randomness_array.len(), 64); + let SupportedFheCiphertexts::Scalar(rand_counter) = &input_operands[0] else { + panic!("we should have checked we have only scalar operands here") + }; + let SupportedFheCiphertexts::Scalar(upper_bound) = &input_operands[1] else { + panic!("we should have checked we have only scalar operands here") + }; + let SupportedFheCiphertexts::Scalar(to_type) = &input_operands[2] else { + panic!("we should have checked we have only scalar operands here") + }; + let (to_type, _) = to_type.to_low_high_u128(); + rand_counter.copy_to_be_byte_slice(&mut randomness_array[32..64]); + let digest = Keccak256::digest(&randomness_array); + let mut fixed_digest: [u8; 32] = [0; 32]; + fixed_digest.copy_from_slice(digest.as_slice()); + let mut generator = rand_chacha::ChaCha20Rng::from_seed(fixed_digest); + // 256 bytes biggest number we support + let bytes_size = type_bytes_size(to_type as i16); + let mut random_bytes: Vec = Vec::with_capacity(bytes_size); + for _ in 0..bytes_size { + random_bytes.push(generator.gen()); + } + + let mut upper_bound_bytes: [u8; 32] = [0; 32]; + upper_bound.copy_to_be_byte_slice(&mut upper_bound_bytes); + + // bound restriction + let big_int = BigInt::from_bytes_be(bigdecimal::num_bigint::Sign::Plus, &random_bytes); + let bound_big_int = BigInt::from_bytes_be(bigdecimal::num_bigint::Sign::Plus, &upper_bound_bytes); + + let final_big_int = + if bound_big_int > BigInt::ZERO { + big_int.clone() % bound_big_int.clone() + } else { + 0.into() + }; + let (_, final_bytes) = final_big_int.to_bytes_be(); + Ok(trivial_encrypt_be_bytes(to_type as i16, &final_bytes)) + }, SupportedFheOperations::FheGetInputCiphertext => todo!("Implement FheGetInputCiphertext"), } } + +pub fn type_bytes_size(the_type: i16) -> usize { + match the_type { + 0 => 1, + 2 => 1, + 3 => 2, + 4 => 4, + 5 => 8, + 6 => 16, + 7 => 20, + 8 => 32, + 9 => 64, + 10 => 128, + 11 => 256, + other => { + panic!("unknown type to trim to: {other}") + } + } +} \ No newline at end of file diff --git a/fhevm-engine/fhevm-engine-common/src/types.rs b/fhevm-engine/fhevm-engine-common/src/types.rs index b201dbf0..28ed2b47 100644 --- a/fhevm-engine/fhevm-engine-common/src/types.rs +++ b/fhevm-engine/fhevm-engine-common/src/types.rs @@ -81,6 +81,23 @@ pub enum FhevmError { expected_scalar_operand_bytes: usize, got_bytes: usize, }, + UnexpectedRandOperandSizeForOutputType { + fhe_operation: i32, + fhe_operation_name: String, + expected_operand_bytes: usize, + got_bytes: usize, + }, + RandOperationUpperBoundCannotBeZero { + fhe_operation: i32, + fhe_operation_name: String, + upper_bound_value: String, + }, + RandOperationInputsMustAllBeScalar { + fhe_operation: i32, + fhe_operation_name: String, + scalar_operand_count: usize, + expected_scalar_operand_count: usize, + }, BadInputs, MissingTfheRsData, InvalidHandle, @@ -202,6 +219,29 @@ impl std::fmt::Display for FhevmError { } => { write!(f, "only one operand can be scalar, fhe operation: {fhe_operation}, fhe operation name: {fhe_operation_name}, second operand count: {scalar_operand_count}, max scalar operands: {max_scalar_operands}") } + Self::UnexpectedRandOperandSizeForOutputType { + fhe_operation, + fhe_operation_name, + expected_operand_bytes, + got_bytes, + } => { + write!(f, "operation must have only one byte for output operand type {fhe_operation} ({fhe_operation_name}) expects bytes {}, received: {}", expected_operand_bytes, got_bytes) + } + Self::RandOperationUpperBoundCannotBeZero { + fhe_operation, + fhe_operation_name, + upper_bound_value, + } => { + write!(f, "rand bounded operation cannot receive zero as upper bound {fhe_operation} ({fhe_operation_name}) received: {}", upper_bound_value) + }, + Self::RandOperationInputsMustAllBeScalar { + fhe_operation, + fhe_operation_name, + scalar_operand_count, + expected_scalar_operand_count, + } => { + write!(f, "operation must have all operands as scalar {fhe_operation} ({fhe_operation_name}) expected scalar operands {}, received: {}", expected_scalar_operand_count, scalar_operand_count) + } Self::BadInputs => { write!(f, "Bad inputs") } @@ -421,6 +461,12 @@ impl SupportedFheCiphertexts { 9 => Ok(SupportedFheCiphertexts::FheBytes64( list.get(0)?.ok_or(FhevmError::MissingTfheRsData)?, )), + 10 => Ok(SupportedFheCiphertexts::FheBytes128( + list.get(0)?.ok_or(FhevmError::MissingTfheRsData)?, + )), + 11 => Ok(SupportedFheCiphertexts::FheBytes128( + list.get(0)?.ok_or(FhevmError::MissingTfheRsData)?, + )), _ => Err(FhevmError::UnknownFheType(ct_type as i32).into()), } } @@ -491,6 +537,14 @@ impl SupportedFheOperations { } } + pub fn is_random(&self) -> bool { + match self { + SupportedFheOperations::FheRand + | SupportedFheOperations::FheRandBounded => true, + _ => false, + } + } + pub fn supports_bool_inputs(&self) -> bool { match self { SupportedFheOperations::FheEq diff --git a/fhevm-engine/fhevm-go-coproc/fhevm/api.go b/fhevm-engine/fhevm-go-coproc/fhevm/api.go index d30f29aa..fbecb222 100644 --- a/fhevm-engine/fhevm-go-coproc/fhevm/api.go +++ b/fhevm-engine/fhevm-go-coproc/fhevm/api.go @@ -8,13 +8,10 @@ import ( "errors" "fmt" "os" - "strings" "sync" "time" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - ethCrypto "github.com/ethereum/go-ethereum/crypto" _ "github.com/mattn/go-sqlite3" "google.golang.org/grpc" "google.golang.org/grpc/metadata" @@ -32,6 +29,7 @@ const ( FheUint64 FheUintType = 5 FheUint128 FheUintType = 6 FheUint160 FheUintType = 7 + FheUint256 FheUintType = 8 FheUserBytes FheUintType = 255 ) @@ -112,8 +110,12 @@ type CoprocessorApi interface { type SegmentId int +type ExtraData struct { + RandomCounter common.Hash +} + type CoprocessorSession interface { - Execute(input []byte, output []byte) error + Execute(input []byte, ed ExtraData, output []byte) error ContractAddress() common.Address NextSegment() SegmentId InvalidateSinceSegment(id SegmentId) SegmentId @@ -127,9 +129,8 @@ type ComputationStore interface { } type ApiImpl struct { - store *SqliteCiphertextStore - address common.Address - coprocessorKey *ecdsa.PrivateKey + store *SqliteComputationStore + address common.Address } type SessionImpl struct { @@ -161,7 +162,7 @@ type SessionComputationStore struct { segmentCount int } -type SqliteCiphertextStore struct { +type SqliteComputationStore struct { dbMutex sync.Mutex dbConn *sql.DB coprocessorUrl string @@ -181,9 +182,8 @@ type ciphertextSegment struct { func (coprocApi *ApiImpl) CreateSession() CoprocessorSession { return &SessionImpl{ - address: coprocApi.address, - coprocessorKey: coprocApi.coprocessorKey, - isCommitted: false, + address: coprocApi.address, + isCommitted: false, sessionStore: &SessionComputationStore{ isCommitted: false, inserts: make([]ComputationToInsert, 0), @@ -212,7 +212,7 @@ func (sessionApi *SessionImpl) Commit() error { return nil } -func (sessionApi *SessionImpl) Execute(data []byte, output []byte) error { +func (sessionApi *SessionImpl) Execute(data []byte, ed ExtraData, output []byte) error { if len(data) < 4 { return fmt.Errorf("input data must be at least 4 bytes for signature, got %d", len(data)) } @@ -226,7 +226,7 @@ func (sessionApi *SessionImpl) Execute(data []byte, output []byte) error { if len(output) >= 32 { // where to get output handle from? outputHandle := output[0:32] - return method.runFunction(sessionApi, callData, outputHandle) + return method.runFunction(sessionApi, callData, ed, outputHandle) } else { return errors.New("no output data provided") } @@ -278,7 +278,7 @@ func (dbApi *SessionComputationStore) InsertComputation(computation ComputationT func (dbApi *SessionComputationStore) Commit() error { if dbApi.isCommitted { - return errors.New("session ciphertext store already committed") + return errors.New("session computation store already committed") } dbApi.isCommitted = true @@ -290,7 +290,7 @@ func (dbApi *SessionComputationStore) Commit() error { } } - fmt.Printf("Inserting %d ciphertexts into database\n", len(finalInserts)) + fmt.Printf("Inserting %d computations into database\n", len(finalInserts)) err := dbApi.underlyingCiphertextStore.InsertComputationBatch(finalInserts) if err != nil { @@ -325,7 +325,7 @@ func computationToAsyncComputation(computation ComputationToInsert) AsyncComputa } } -func (dbApi *SqliteCiphertextStore) InsertComputation(computation ComputationToInsert) error { +func (dbApi *SqliteComputationStore) InsertComputation(computation ComputationToInsert) error { dbApi.dbMutex.Lock() defer dbApi.dbMutex.Unlock() @@ -345,7 +345,7 @@ func (dbApi *SqliteCiphertextStore) InsertComputation(computation ComputationToI return nil } -func (dbApi *SqliteCiphertextStore) InsertComputationBatch(computations []ComputationToInsert) error { +func (dbApi *SqliteComputationStore) InsertComputationBatch(computations []ComputationToInsert) error { dbApi.dbMutex.Lock() defer dbApi.dbMutex.Unlock() @@ -382,11 +382,6 @@ func InitCoprocessor() (CoprocessorApi, error) { } fhevmContractAddress := common.HexToAddress(contractAddr) - keyFile, hasKey := os.LookupEnv("FHEVM_COPROCESSOR_PRIVATE_KEY_FILE") - if !hasKey { - return nil, errors.New("FHEVM_CIPHERTEXTS_DB is set but FHEVM_COPROCESSOR_PRIVATE_KEY_FILE is not set") - } - coprocUrl, hasUrl := os.LookupEnv("FHEVM_COPROCESSOR_URL") if !hasUrl { return nil, errors.New("FHEVM_COPROCESSOR_URL is not configured") @@ -402,49 +397,9 @@ func InitCoprocessor() (CoprocessorApi, error) { return nil, err } - keyBytes, err := os.ReadFile(keyFile) - if err != nil { - if !os.IsNotExist(err) { - return nil, err - } - // key file doesn't exist, generate - fmt.Printf("Key file not found in %s, generating and saving\n", keyFile) - privKey, err := ethCrypto.GenerateKey() - if err != nil { - return nil, err - } - keyBytes = []byte(hexutil.Encode(ethCrypto.FromECDSA(privKey))) - err = os.WriteFile(keyFile, keyBytes, 0o600) - if err != nil { - return nil, err - } - fmt.Printf("Generated and saved ECDSA private key in %s\n", keyFile) - } - - privKeyString := strings.TrimSpace(string(keyBytes)) - privKeyBytes, err := hexutil.Decode(privKeyString) - if err != nil { - return nil, err - } - - privKey, err := ethCrypto.ToECDSA(privKeyBytes) - if err != nil { - return nil, err - } - - pubKey := privKey.Public() - publicKeyECDSA, ok := pubKey.(*ecdsa.PublicKey) - if !ok { - return nil, errors.New("can't get ethereum public key from private") - } - - pubKeyAddr := ethCrypto.PubkeyToAddress(*publicKeyECDSA) - fmt.Printf("Public coprocessor eth address: %s\n", pubKeyAddr) - apiImpl := ApiImpl{ - store: ciphertextDb, - address: fhevmContractAddress, - coprocessorKey: privKey, + store: ciphertextDb, + address: fhevmContractAddress, } // background job to submit computations to coprocessor @@ -482,45 +437,53 @@ func scheduleCoprocessorFlushes(impl *ApiImpl) { }() } -func flushWorkItemsToCoprocessor(store *SqliteCiphertextStore) (int, error) { +func queryCurrentComputeRequests(store *SqliteComputationStore) (*AsyncComputeRequest, [][]byte, error) { var asyncCompReq *AsyncComputeRequest handlesToMarkDone := make([][]byte, 0) - { - store.dbMutex.Lock() - defer store.dbMutex.Unlock() - // query all ciphertexts - response, err := store.dbConn.Query("SELECT output_handle, payload FROM computations WHERE is_sent = 0 LIMIT 700") + store.dbMutex.Lock() + defer store.dbMutex.Unlock() + + // query all ciphertexts + response, err := store.dbConn.Query("SELECT output_handle, payload FROM computations WHERE is_sent = 0 LIMIT 700") + if err != nil { + return nil, handlesToMarkDone, err + } + + requests := make([]*AsyncComputation, 0, 16) + for response.Next() { + var outputHandle, payload []byte + err = response.Scan(&outputHandle, &payload) if err != nil { - return 0, err + return nil, handlesToMarkDone, err + } + var ac AsyncComputation + err = proto.Unmarshal(payload, &ac) + if err != nil { + return nil, handlesToMarkDone, err } - requests := make([]*AsyncComputation, 0, 16) - for response.Next() { - var outputHandle, payload []byte - err = response.Scan(&outputHandle, &payload) - if err != nil { - return 0, err - } - var ac AsyncComputation - err = proto.Unmarshal(payload, &ac) - if err != nil { - return 0, err - } + requests = append(requests, &ac) + handlesToMarkDone = append(handlesToMarkDone, outputHandle) + } - requests = append(requests, &ac) - handlesToMarkDone = append(handlesToMarkDone, outputHandle) - } + if response.Err() != nil { + return nil, handlesToMarkDone, err + } - if response.Err() != nil { - return 0, err + if len(requests) > 0 { + asyncCompReq = &AsyncComputeRequest{ + Computations: requests, } + } - if len(requests) > 0 { - asyncCompReq = &AsyncComputeRequest{ - Computations: requests, - } - } + return asyncCompReq, handlesToMarkDone, nil +} + +func flushWorkItemsToCoprocessor(store *SqliteComputationStore) (int, error) { + asyncCompReq, handlesToMarkDone, err := queryCurrentComputeRequests(store) + if err != nil { + return 0, err } if asyncCompReq != nil { @@ -562,7 +525,7 @@ func flushWorkItemsToCoprocessor(store *SqliteCiphertextStore) (int, error) { return 0, nil } -func CreateSqliteCiphertextStore(dbPath string, ctx context.Context, coprocUrl, coprocApiKey string) (*SqliteCiphertextStore, error) { +func CreateSqliteCiphertextStore(dbPath string, ctx context.Context, coprocUrl, coprocApiKey string) (*SqliteComputationStore, error) { dbConn, err := sql.Open("sqlite3", dbPath) if err != nil { return nil, err @@ -573,7 +536,7 @@ func CreateSqliteCiphertextStore(dbPath string, ctx context.Context, coprocUrl, return nil, err } - return &SqliteCiphertextStore{ + return &SqliteComputationStore{ dbConn: dbConn, dbMutex: sync.Mutex{}, coprocessorUrl: coprocUrl, diff --git a/fhevm-engine/fhevm-go-coproc/fhevm/common.pb.go b/fhevm-engine/fhevm-go-coproc/fhevm/common.pb.go index 80971e17..c2ee8c95 100644 --- a/fhevm-engine/fhevm-go-coproc/fhevm/common.pb.go +++ b/fhevm-engine/fhevm-go-coproc/fhevm/common.pb.go @@ -23,32 +23,33 @@ const ( type FheOperation int32 const ( - FheOperation_FHE_ADD FheOperation = 0 - FheOperation_FHE_SUB FheOperation = 1 - FheOperation_FHE_MUL FheOperation = 2 - FheOperation_FHE_DIV FheOperation = 3 - FheOperation_FHE_REM FheOperation = 4 - FheOperation_FHE_BIT_AND FheOperation = 5 - FheOperation_FHE_BIT_OR FheOperation = 6 - FheOperation_FHE_BIT_XOR FheOperation = 7 - FheOperation_FHE_SHL FheOperation = 8 - FheOperation_FHE_SHR FheOperation = 9 - FheOperation_FHE_ROTL FheOperation = 10 - FheOperation_FHE_ROTR FheOperation = 11 - FheOperation_FHE_EQ FheOperation = 12 - FheOperation_FHE_NE FheOperation = 13 - FheOperation_FHE_GE FheOperation = 14 - FheOperation_FHE_GT FheOperation = 15 - FheOperation_FHE_LE FheOperation = 16 - FheOperation_FHE_LT FheOperation = 17 - FheOperation_FHE_MIN FheOperation = 18 - FheOperation_FHE_MAX FheOperation = 19 - FheOperation_FHE_NEG FheOperation = 20 - FheOperation_FHE_NOT FheOperation = 21 - FheOperation_FHE_CAST FheOperation = 23 - FheOperation_FHE_IF_THEN_ELSE FheOperation = 25 - FheOperation_FHE_RAND FheOperation = 26 - FheOperation_FHE_RAND_BOUNDED FheOperation = 27 + FheOperation_FHE_ADD FheOperation = 0 + FheOperation_FHE_SUB FheOperation = 1 + FheOperation_FHE_MUL FheOperation = 2 + FheOperation_FHE_DIV FheOperation = 3 + FheOperation_FHE_REM FheOperation = 4 + FheOperation_FHE_BIT_AND FheOperation = 5 + FheOperation_FHE_BIT_OR FheOperation = 6 + FheOperation_FHE_BIT_XOR FheOperation = 7 + FheOperation_FHE_SHL FheOperation = 8 + FheOperation_FHE_SHR FheOperation = 9 + FheOperation_FHE_ROTL FheOperation = 10 + FheOperation_FHE_ROTR FheOperation = 11 + FheOperation_FHE_EQ FheOperation = 12 + FheOperation_FHE_NE FheOperation = 13 + FheOperation_FHE_GE FheOperation = 14 + FheOperation_FHE_GT FheOperation = 15 + FheOperation_FHE_LE FheOperation = 16 + FheOperation_FHE_LT FheOperation = 17 + FheOperation_FHE_MIN FheOperation = 18 + FheOperation_FHE_MAX FheOperation = 19 + FheOperation_FHE_NEG FheOperation = 20 + FheOperation_FHE_NOT FheOperation = 21 + FheOperation_FHE_CAST FheOperation = 23 + FheOperation_FHE_IF_THEN_ELSE FheOperation = 25 + FheOperation_FHE_RAND FheOperation = 26 + FheOperation_FHE_RAND_BOUNDED FheOperation = 27 + FheOperation_FHE_GET_CIPHERTEXT FheOperation = 32 ) // Enum value maps for FheOperation. @@ -80,34 +81,36 @@ var ( 25: "FHE_IF_THEN_ELSE", 26: "FHE_RAND", 27: "FHE_RAND_BOUNDED", + 32: "FHE_GET_CIPHERTEXT", } FheOperation_value = map[string]int32{ - "FHE_ADD": 0, - "FHE_SUB": 1, - "FHE_MUL": 2, - "FHE_DIV": 3, - "FHE_REM": 4, - "FHE_BIT_AND": 5, - "FHE_BIT_OR": 6, - "FHE_BIT_XOR": 7, - "FHE_SHL": 8, - "FHE_SHR": 9, - "FHE_ROTL": 10, - "FHE_ROTR": 11, - "FHE_EQ": 12, - "FHE_NE": 13, - "FHE_GE": 14, - "FHE_GT": 15, - "FHE_LE": 16, - "FHE_LT": 17, - "FHE_MIN": 18, - "FHE_MAX": 19, - "FHE_NEG": 20, - "FHE_NOT": 21, - "FHE_CAST": 23, - "FHE_IF_THEN_ELSE": 25, - "FHE_RAND": 26, - "FHE_RAND_BOUNDED": 27, + "FHE_ADD": 0, + "FHE_SUB": 1, + "FHE_MUL": 2, + "FHE_DIV": 3, + "FHE_REM": 4, + "FHE_BIT_AND": 5, + "FHE_BIT_OR": 6, + "FHE_BIT_XOR": 7, + "FHE_SHL": 8, + "FHE_SHR": 9, + "FHE_ROTL": 10, + "FHE_ROTR": 11, + "FHE_EQ": 12, + "FHE_NE": 13, + "FHE_GE": 14, + "FHE_GT": 15, + "FHE_LE": 16, + "FHE_LT": 17, + "FHE_MIN": 18, + "FHE_MAX": 19, + "FHE_NEG": 20, + "FHE_NOT": 21, + "FHE_CAST": 23, + "FHE_IF_THEN_ELSE": 25, + "FHE_RAND": 26, + "FHE_RAND_BOUNDED": 27, + "FHE_GET_CIPHERTEXT": 32, } ) @@ -142,7 +145,7 @@ var File_common_proto protoreflect.FileDescriptor var file_common_proto_rawDesc = []byte{ 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, - 0x66, 0x68, 0x65, 0x76, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2a, 0xfb, 0x02, 0x0a, + 0x66, 0x68, 0x65, 0x76, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2a, 0x93, 0x03, 0x0a, 0x0c, 0x46, 0x68, 0x65, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0b, 0x0a, 0x07, 0x46, 0x48, 0x45, 0x5f, 0x41, 0x44, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x46, 0x48, 0x45, 0x5f, 0x53, 0x55, 0x42, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x46, 0x48, 0x45, 0x5f, 0x4d, @@ -166,11 +169,12 @@ var file_common_proto_rawDesc = []byte{ 0x12, 0x14, 0x0a, 0x10, 0x46, 0x48, 0x45, 0x5f, 0x49, 0x46, 0x5f, 0x54, 0x48, 0x45, 0x4e, 0x5f, 0x45, 0x4c, 0x53, 0x45, 0x10, 0x19, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x48, 0x45, 0x5f, 0x52, 0x41, 0x4e, 0x44, 0x10, 0x1a, 0x12, 0x14, 0x0a, 0x10, 0x46, 0x48, 0x45, 0x5f, 0x52, 0x41, 0x4e, 0x44, - 0x5f, 0x42, 0x4f, 0x55, 0x4e, 0x44, 0x45, 0x44, 0x10, 0x1b, 0x42, 0x2d, 0x0a, 0x13, 0x69, 0x6f, - 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x66, 0x68, 0x65, 0x76, 0x6d, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, - 0x6e, 0x42, 0x0b, 0x46, 0x68, 0x65, 0x76, 0x6d, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x50, 0x01, - 0x5a, 0x07, 0x2e, 0x2f, 0x66, 0x68, 0x65, 0x76, 0x6d, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x5f, 0x42, 0x4f, 0x55, 0x4e, 0x44, 0x45, 0x44, 0x10, 0x1b, 0x12, 0x16, 0x0a, 0x12, 0x46, 0x48, + 0x45, 0x5f, 0x47, 0x45, 0x54, 0x5f, 0x43, 0x49, 0x50, 0x48, 0x45, 0x52, 0x54, 0x45, 0x58, 0x54, + 0x10, 0x20, 0x42, 0x2d, 0x0a, 0x13, 0x69, 0x6f, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x66, 0x68, + 0x65, 0x76, 0x6d, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x42, 0x0b, 0x46, 0x68, 0x65, 0x76, 0x6d, + 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x50, 0x01, 0x5a, 0x07, 0x2e, 0x2f, 0x66, 0x68, 0x65, 0x76, + 0x6d, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/fhevm-engine/fhevm-go-coproc/fhevm/coprocessor.pb.go b/fhevm-engine/fhevm-go-coproc/fhevm/coprocessor.pb.go index 1d61d21c..a913d199 100644 --- a/fhevm-engine/fhevm-go-coproc/fhevm/coprocessor.pb.go +++ b/fhevm-engine/fhevm-go-coproc/fhevm/coprocessor.pb.go @@ -20,16 +20,16 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -type DebugEncryptRequest struct { +type TrivialEncryptBatch struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Values []*DebugEncryptRequestSingle `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty"` + Values []*TrivialEncryptRequestSingle `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty"` } -func (x *DebugEncryptRequest) Reset() { - *x = DebugEncryptRequest{} +func (x *TrivialEncryptBatch) Reset() { + *x = TrivialEncryptBatch{} if protoimpl.UnsafeEnabled { mi := &file_coprocessor_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -37,13 +37,13 @@ func (x *DebugEncryptRequest) Reset() { } } -func (x *DebugEncryptRequest) String() string { +func (x *TrivialEncryptBatch) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DebugEncryptRequest) ProtoMessage() {} +func (*TrivialEncryptBatch) ProtoMessage() {} -func (x *DebugEncryptRequest) ProtoReflect() protoreflect.Message { +func (x *TrivialEncryptBatch) ProtoReflect() protoreflect.Message { mi := &file_coprocessor_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -55,30 +55,30 @@ func (x *DebugEncryptRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use DebugEncryptRequest.ProtoReflect.Descriptor instead. -func (*DebugEncryptRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use TrivialEncryptBatch.ProtoReflect.Descriptor instead. +func (*TrivialEncryptBatch) Descriptor() ([]byte, []int) { return file_coprocessor_proto_rawDescGZIP(), []int{0} } -func (x *DebugEncryptRequest) GetValues() []*DebugEncryptRequestSingle { +func (x *TrivialEncryptBatch) GetValues() []*TrivialEncryptRequestSingle { if x != nil { return x.Values } return nil } -type DebugEncryptRequestSingle struct { +type TrivialEncryptRequestSingle struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Handle []byte `protobuf:"bytes,1,opt,name=handle,proto3" json:"handle,omitempty"` - LeValue []byte `protobuf:"bytes,2,opt,name=le_value,json=leValue,proto3" json:"le_value,omitempty"` + BeValue []byte `protobuf:"bytes,2,opt,name=be_value,json=beValue,proto3" json:"be_value,omitempty"` OutputType int32 `protobuf:"varint,3,opt,name=output_type,json=outputType,proto3" json:"output_type,omitempty"` } -func (x *DebugEncryptRequestSingle) Reset() { - *x = DebugEncryptRequestSingle{} +func (x *TrivialEncryptRequestSingle) Reset() { + *x = TrivialEncryptRequestSingle{} if protoimpl.UnsafeEnabled { mi := &file_coprocessor_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -86,13 +86,13 @@ func (x *DebugEncryptRequestSingle) Reset() { } } -func (x *DebugEncryptRequestSingle) String() string { +func (x *TrivialEncryptRequestSingle) String() string { return protoimpl.X.MessageStringOf(x) } -func (*DebugEncryptRequestSingle) ProtoMessage() {} +func (*TrivialEncryptRequestSingle) ProtoMessage() {} -func (x *DebugEncryptRequestSingle) ProtoReflect() protoreflect.Message { +func (x *TrivialEncryptRequestSingle) ProtoReflect() protoreflect.Message { mi := &file_coprocessor_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -104,181 +104,32 @@ func (x *DebugEncryptRequestSingle) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use DebugEncryptRequestSingle.ProtoReflect.Descriptor instead. -func (*DebugEncryptRequestSingle) Descriptor() ([]byte, []int) { +// Deprecated: Use TrivialEncryptRequestSingle.ProtoReflect.Descriptor instead. +func (*TrivialEncryptRequestSingle) Descriptor() ([]byte, []int) { return file_coprocessor_proto_rawDescGZIP(), []int{1} } -func (x *DebugEncryptRequestSingle) GetHandle() []byte { +func (x *TrivialEncryptRequestSingle) GetHandle() []byte { if x != nil { return x.Handle } return nil } -func (x *DebugEncryptRequestSingle) GetLeValue() []byte { +func (x *TrivialEncryptRequestSingle) GetBeValue() []byte { if x != nil { - return x.LeValue + return x.BeValue } return nil } -func (x *DebugEncryptRequestSingle) GetOutputType() int32 { +func (x *TrivialEncryptRequestSingle) GetOutputType() int32 { if x != nil { return x.OutputType } return 0 } -type DebugDecryptRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Handles [][]byte `protobuf:"bytes,1,rep,name=handles,proto3" json:"handles,omitempty"` -} - -func (x *DebugDecryptRequest) Reset() { - *x = DebugDecryptRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_coprocessor_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *DebugDecryptRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DebugDecryptRequest) ProtoMessage() {} - -func (x *DebugDecryptRequest) ProtoReflect() protoreflect.Message { - mi := &file_coprocessor_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DebugDecryptRequest.ProtoReflect.Descriptor instead. -func (*DebugDecryptRequest) Descriptor() ([]byte, []int) { - return file_coprocessor_proto_rawDescGZIP(), []int{2} -} - -func (x *DebugDecryptRequest) GetHandles() [][]byte { - if x != nil { - return x.Handles - } - return nil -} - -type DebugDecryptResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Values []*DebugDecryptResponseSingle `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty"` -} - -func (x *DebugDecryptResponse) Reset() { - *x = DebugDecryptResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_coprocessor_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *DebugDecryptResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DebugDecryptResponse) ProtoMessage() {} - -func (x *DebugDecryptResponse) ProtoReflect() protoreflect.Message { - mi := &file_coprocessor_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DebugDecryptResponse.ProtoReflect.Descriptor instead. -func (*DebugDecryptResponse) Descriptor() ([]byte, []int) { - return file_coprocessor_proto_rawDescGZIP(), []int{3} -} - -func (x *DebugDecryptResponse) GetValues() []*DebugDecryptResponseSingle { - if x != nil { - return x.Values - } - return nil -} - -type DebugDecryptResponseSingle struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - OutputType int32 `protobuf:"varint,1,opt,name=output_type,json=outputType,proto3" json:"output_type,omitempty"` - Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` -} - -func (x *DebugDecryptResponseSingle) Reset() { - *x = DebugDecryptResponseSingle{} - if protoimpl.UnsafeEnabled { - mi := &file_coprocessor_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *DebugDecryptResponseSingle) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*DebugDecryptResponseSingle) ProtoMessage() {} - -func (x *DebugDecryptResponseSingle) ProtoReflect() protoreflect.Message { - mi := &file_coprocessor_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use DebugDecryptResponseSingle.ProtoReflect.Descriptor instead. -func (*DebugDecryptResponseSingle) Descriptor() ([]byte, []int) { - return file_coprocessor_proto_rawDescGZIP(), []int{4} -} - -func (x *DebugDecryptResponseSingle) GetOutputType() int32 { - if x != nil { - return x.OutputType - } - return 0 -} - -func (x *DebugDecryptResponseSingle) GetValue() string { - if x != nil { - return x.Value - } - return "" -} - type AsyncComputation struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -292,7 +143,7 @@ type AsyncComputation struct { func (x *AsyncComputation) Reset() { *x = AsyncComputation{} if protoimpl.UnsafeEnabled { - mi := &file_coprocessor_proto_msgTypes[5] + mi := &file_coprocessor_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -305,7 +156,7 @@ func (x *AsyncComputation) String() string { func (*AsyncComputation) ProtoMessage() {} func (x *AsyncComputation) ProtoReflect() protoreflect.Message { - mi := &file_coprocessor_proto_msgTypes[5] + mi := &file_coprocessor_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -318,7 +169,7 @@ func (x *AsyncComputation) ProtoReflect() protoreflect.Message { // Deprecated: Use AsyncComputation.ProtoReflect.Descriptor instead. func (*AsyncComputation) Descriptor() ([]byte, []int) { - return file_coprocessor_proto_rawDescGZIP(), []int{5} + return file_coprocessor_proto_rawDescGZIP(), []int{2} } func (x *AsyncComputation) GetOperation() FheOperation { @@ -357,7 +208,7 @@ type AsyncComputationInput struct { func (x *AsyncComputationInput) Reset() { *x = AsyncComputationInput{} if protoimpl.UnsafeEnabled { - mi := &file_coprocessor_proto_msgTypes[6] + mi := &file_coprocessor_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -370,7 +221,7 @@ func (x *AsyncComputationInput) String() string { func (*AsyncComputationInput) ProtoMessage() {} func (x *AsyncComputationInput) ProtoReflect() protoreflect.Message { - mi := &file_coprocessor_proto_msgTypes[6] + mi := &file_coprocessor_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -383,7 +234,7 @@ func (x *AsyncComputationInput) ProtoReflect() protoreflect.Message { // Deprecated: Use AsyncComputationInput.ProtoReflect.Descriptor instead. func (*AsyncComputationInput) Descriptor() ([]byte, []int) { - return file_coprocessor_proto_rawDescGZIP(), []int{6} + return file_coprocessor_proto_rawDescGZIP(), []int{3} } func (m *AsyncComputationInput) GetInput() isAsyncComputationInput_Input { @@ -423,82 +274,21 @@ func (*AsyncComputationInput_InputHandle) isAsyncComputationInput_Input() {} func (*AsyncComputationInput_Scalar) isAsyncComputationInput_Input() {} -type CiphertextToUpload struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - CiphertextType int32 `protobuf:"varint,1,opt,name=ciphertext_type,json=ciphertextType,proto3" json:"ciphertext_type,omitempty"` - CiphertextHandle []byte `protobuf:"bytes,2,opt,name=ciphertext_handle,json=ciphertextHandle,proto3" json:"ciphertext_handle,omitempty"` - CiphertextBytes []byte `protobuf:"bytes,3,opt,name=ciphertext_bytes,json=ciphertextBytes,proto3" json:"ciphertext_bytes,omitempty"` -} - -func (x *CiphertextToUpload) Reset() { - *x = CiphertextToUpload{} - if protoimpl.UnsafeEnabled { - mi := &file_coprocessor_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CiphertextToUpload) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CiphertextToUpload) ProtoMessage() {} - -func (x *CiphertextToUpload) ProtoReflect() protoreflect.Message { - mi := &file_coprocessor_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CiphertextToUpload.ProtoReflect.Descriptor instead. -func (*CiphertextToUpload) Descriptor() ([]byte, []int) { - return file_coprocessor_proto_rawDescGZIP(), []int{7} -} - -func (x *CiphertextToUpload) GetCiphertextType() int32 { - if x != nil { - return x.CiphertextType - } - return 0 -} - -func (x *CiphertextToUpload) GetCiphertextHandle() []byte { - if x != nil { - return x.CiphertextHandle - } - return nil -} - -func (x *CiphertextToUpload) GetCiphertextBytes() []byte { - if x != nil { - return x.CiphertextBytes - } - return nil -} - type InputToUpload struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - InputPayload []byte `protobuf:"bytes,1,opt,name=input_payload,json=inputPayload,proto3" json:"input_payload,omitempty"` - Signature []byte `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty"` + InputPayload []byte `protobuf:"bytes,1,opt,name=input_payload,json=inputPayload,proto3" json:"input_payload,omitempty"` + ContractAddress string `protobuf:"bytes,2,opt,name=contract_address,json=contractAddress,proto3" json:"contract_address,omitempty"` + CallerAddress string `protobuf:"bytes,3,opt,name=caller_address,json=callerAddress,proto3" json:"caller_address,omitempty"` + Signature []byte `protobuf:"bytes,4,opt,name=signature,proto3" json:"signature,omitempty"` } func (x *InputToUpload) Reset() { *x = InputToUpload{} if protoimpl.UnsafeEnabled { - mi := &file_coprocessor_proto_msgTypes[8] + mi := &file_coprocessor_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -511,7 +301,7 @@ func (x *InputToUpload) String() string { func (*InputToUpload) ProtoMessage() {} func (x *InputToUpload) ProtoReflect() protoreflect.Message { - mi := &file_coprocessor_proto_msgTypes[8] + mi := &file_coprocessor_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -524,7 +314,7 @@ func (x *InputToUpload) ProtoReflect() protoreflect.Message { // Deprecated: Use InputToUpload.ProtoReflect.Descriptor instead. func (*InputToUpload) Descriptor() ([]byte, []int) { - return file_coprocessor_proto_rawDescGZIP(), []int{8} + return file_coprocessor_proto_rawDescGZIP(), []int{4} } func (x *InputToUpload) GetInputPayload() []byte { @@ -534,6 +324,20 @@ func (x *InputToUpload) GetInputPayload() []byte { return nil } +func (x *InputToUpload) GetContractAddress() string { + if x != nil { + return x.ContractAddress + } + return "" +} + +func (x *InputToUpload) GetCallerAddress() string { + if x != nil { + return x.CallerAddress + } + return "" +} + func (x *InputToUpload) GetSignature() []byte { if x != nil { return x.Signature @@ -553,7 +357,7 @@ type InputCiphertextResponseHandle struct { func (x *InputCiphertextResponseHandle) Reset() { *x = InputCiphertextResponseHandle{} if protoimpl.UnsafeEnabled { - mi := &file_coprocessor_proto_msgTypes[9] + mi := &file_coprocessor_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -566,7 +370,7 @@ func (x *InputCiphertextResponseHandle) String() string { func (*InputCiphertextResponseHandle) ProtoMessage() {} func (x *InputCiphertextResponseHandle) ProtoReflect() protoreflect.Message { - mi := &file_coprocessor_proto_msgTypes[9] + mi := &file_coprocessor_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -579,7 +383,7 @@ func (x *InputCiphertextResponseHandle) ProtoReflect() protoreflect.Message { // Deprecated: Use InputCiphertextResponseHandle.ProtoReflect.Descriptor instead. func (*InputCiphertextResponseHandle) Descriptor() ([]byte, []int) { - return file_coprocessor_proto_rawDescGZIP(), []int{9} + return file_coprocessor_proto_rawDescGZIP(), []int{5} } func (x *InputCiphertextResponseHandle) GetHandle() []byte { @@ -601,13 +405,17 @@ type InputCiphertextResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - InputHandles []*InputCiphertextResponseHandle `protobuf:"bytes,1,rep,name=input_handles,json=inputHandles,proto3" json:"input_handles,omitempty"` + InputHandles []*InputCiphertextResponseHandle `protobuf:"bytes,1,rep,name=input_handles,json=inputHandles,proto3" json:"input_handles,omitempty"` + Eip712ContractAddress string `protobuf:"bytes,2,opt,name=eip712ContractAddress,proto3" json:"eip712ContractAddress,omitempty"` + Eip712CallerAddress string `protobuf:"bytes,3,opt,name=eip712CallerAddress,proto3" json:"eip712CallerAddress,omitempty"` + Eip712SignerAddress string `protobuf:"bytes,4,opt,name=eip712SignerAddress,proto3" json:"eip712SignerAddress,omitempty"` + Eip712Signature []byte `protobuf:"bytes,5,opt,name=eip712Signature,proto3" json:"eip712Signature,omitempty"` } func (x *InputCiphertextResponse) Reset() { *x = InputCiphertextResponse{} if protoimpl.UnsafeEnabled { - mi := &file_coprocessor_proto_msgTypes[10] + mi := &file_coprocessor_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -620,7 +428,7 @@ func (x *InputCiphertextResponse) String() string { func (*InputCiphertextResponse) ProtoMessage() {} func (x *InputCiphertextResponse) ProtoReflect() protoreflect.Message { - mi := &file_coprocessor_proto_msgTypes[10] + mi := &file_coprocessor_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -633,7 +441,7 @@ func (x *InputCiphertextResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use InputCiphertextResponse.ProtoReflect.Descriptor instead. func (*InputCiphertextResponse) Descriptor() ([]byte, []int) { - return file_coprocessor_proto_rawDescGZIP(), []int{10} + return file_coprocessor_proto_rawDescGZIP(), []int{6} } func (x *InputCiphertextResponse) GetInputHandles() []*InputCiphertextResponseHandle { @@ -643,6 +451,34 @@ func (x *InputCiphertextResponse) GetInputHandles() []*InputCiphertextResponseHa return nil } +func (x *InputCiphertextResponse) GetEip712ContractAddress() string { + if x != nil { + return x.Eip712ContractAddress + } + return "" +} + +func (x *InputCiphertextResponse) GetEip712CallerAddress() string { + if x != nil { + return x.Eip712CallerAddress + } + return "" +} + +func (x *InputCiphertextResponse) GetEip712SignerAddress() string { + if x != nil { + return x.Eip712SignerAddress + } + return "" +} + +func (x *InputCiphertextResponse) GetEip712Signature() []byte { + if x != nil { + return x.Eip712Signature + } + return nil +} + type InputUploadResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -654,7 +490,7 @@ type InputUploadResponse struct { func (x *InputUploadResponse) Reset() { *x = InputUploadResponse{} if protoimpl.UnsafeEnabled { - mi := &file_coprocessor_proto_msgTypes[11] + mi := &file_coprocessor_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -667,7 +503,7 @@ func (x *InputUploadResponse) String() string { func (*InputUploadResponse) ProtoMessage() {} func (x *InputUploadResponse) ProtoReflect() protoreflect.Message { - mi := &file_coprocessor_proto_msgTypes[11] + mi := &file_coprocessor_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -680,7 +516,7 @@ func (x *InputUploadResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use InputUploadResponse.ProtoReflect.Descriptor instead. func (*InputUploadResponse) Descriptor() ([]byte, []int) { - return file_coprocessor_proto_rawDescGZIP(), []int{11} + return file_coprocessor_proto_rawDescGZIP(), []int{7} } func (x *InputUploadResponse) GetUploadResponses() []*InputCiphertextResponse { @@ -702,7 +538,7 @@ type AsyncComputeRequest struct { func (x *AsyncComputeRequest) Reset() { *x = AsyncComputeRequest{} if protoimpl.UnsafeEnabled { - mi := &file_coprocessor_proto_msgTypes[12] + mi := &file_coprocessor_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -715,7 +551,7 @@ func (x *AsyncComputeRequest) String() string { func (*AsyncComputeRequest) ProtoMessage() {} func (x *AsyncComputeRequest) ProtoReflect() protoreflect.Message { - mi := &file_coprocessor_proto_msgTypes[12] + mi := &file_coprocessor_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -728,7 +564,7 @@ func (x *AsyncComputeRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AsyncComputeRequest.ProtoReflect.Descriptor instead. func (*AsyncComputeRequest) Descriptor() ([]byte, []int) { - return file_coprocessor_proto_rawDescGZIP(), []int{12} + return file_coprocessor_proto_rawDescGZIP(), []int{8} } func (x *AsyncComputeRequest) GetComputations() []*AsyncComputation { @@ -738,53 +574,6 @@ func (x *AsyncComputeRequest) GetComputations() []*AsyncComputation { return nil } -type CiphertextUploadBatch struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - InputCiphertexts []*CiphertextToUpload `protobuf:"bytes,1,rep,name=input_ciphertexts,json=inputCiphertexts,proto3" json:"input_ciphertexts,omitempty"` -} - -func (x *CiphertextUploadBatch) Reset() { - *x = CiphertextUploadBatch{} - if protoimpl.UnsafeEnabled { - mi := &file_coprocessor_proto_msgTypes[13] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *CiphertextUploadBatch) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*CiphertextUploadBatch) ProtoMessage() {} - -func (x *CiphertextUploadBatch) ProtoReflect() protoreflect.Message { - mi := &file_coprocessor_proto_msgTypes[13] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use CiphertextUploadBatch.ProtoReflect.Descriptor instead. -func (*CiphertextUploadBatch) Descriptor() ([]byte, []int) { - return file_coprocessor_proto_rawDescGZIP(), []int{13} -} - -func (x *CiphertextUploadBatch) GetInputCiphertexts() []*CiphertextToUpload { - if x != nil { - return x.InputCiphertexts - } - return nil -} - type InputUploadBatch struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -796,7 +585,7 @@ type InputUploadBatch struct { func (x *InputUploadBatch) Reset() { *x = InputUploadBatch{} if protoimpl.UnsafeEnabled { - mi := &file_coprocessor_proto_msgTypes[14] + mi := &file_coprocessor_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -809,7 +598,7 @@ func (x *InputUploadBatch) String() string { func (*InputUploadBatch) ProtoMessage() {} func (x *InputUploadBatch) ProtoReflect() protoreflect.Message { - mi := &file_coprocessor_proto_msgTypes[14] + mi := &file_coprocessor_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -822,7 +611,7 @@ func (x *InputUploadBatch) ProtoReflect() protoreflect.Message { // Deprecated: Use InputUploadBatch.ProtoReflect.Descriptor instead. func (*InputUploadBatch) Descriptor() ([]byte, []int) { - return file_coprocessor_proto_rawDescGZIP(), []int{14} + return file_coprocessor_proto_rawDescGZIP(), []int{9} } func (x *InputUploadBatch) GetInputCiphertexts() []*InputToUpload { @@ -843,7 +632,7 @@ type WaitBatch struct { func (x *WaitBatch) Reset() { *x = WaitBatch{} if protoimpl.UnsafeEnabled { - mi := &file_coprocessor_proto_msgTypes[15] + mi := &file_coprocessor_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -856,7 +645,7 @@ func (x *WaitBatch) String() string { func (*WaitBatch) ProtoMessage() {} func (x *WaitBatch) ProtoReflect() protoreflect.Message { - mi := &file_coprocessor_proto_msgTypes[15] + mi := &file_coprocessor_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -869,7 +658,7 @@ func (x *WaitBatch) ProtoReflect() protoreflect.Message { // Deprecated: Use WaitBatch.ProtoReflect.Descriptor instead. func (*WaitBatch) Descriptor() ([]byte, []int) { - return file_coprocessor_proto_rawDescGZIP(), []int{15} + return file_coprocessor_proto_rawDescGZIP(), []int{10} } func (x *WaitBatch) GetCiphertextHandles() []string { @@ -890,7 +679,7 @@ type GenericResponse struct { func (x *GenericResponse) Reset() { *x = GenericResponse{} if protoimpl.UnsafeEnabled { - mi := &file_coprocessor_proto_msgTypes[16] + mi := &file_coprocessor_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -903,7 +692,7 @@ func (x *GenericResponse) String() string { func (*GenericResponse) ProtoMessage() {} func (x *GenericResponse) ProtoReflect() protoreflect.Message { - mi := &file_coprocessor_proto_msgTypes[16] + mi := &file_coprocessor_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -916,7 +705,7 @@ func (x *GenericResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GenericResponse.ProtoReflect.Descriptor instead. func (*GenericResponse) Descriptor() ([]byte, []int) { - return file_coprocessor_proto_rawDescGZIP(), []int{16} + return file_coprocessor_proto_rawDescGZIP(), []int{11} } func (x *GenericResponse) GetResponseCode() int32 { @@ -937,7 +726,7 @@ type FhevmResponses struct { func (x *FhevmResponses) Reset() { *x = FhevmResponses{} if protoimpl.UnsafeEnabled { - mi := &file_coprocessor_proto_msgTypes[17] + mi := &file_coprocessor_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -950,7 +739,7 @@ func (x *FhevmResponses) String() string { func (*FhevmResponses) ProtoMessage() {} func (x *FhevmResponses) ProtoReflect() protoreflect.Message { - mi := &file_coprocessor_proto_msgTypes[17] + mi := &file_coprocessor_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -963,7 +752,7 @@ func (x *FhevmResponses) ProtoReflect() protoreflect.Message { // Deprecated: Use FhevmResponses.ProtoReflect.Descriptor instead. func (*FhevmResponses) Descriptor() ([]byte, []int) { - return file_coprocessor_proto_rawDescGZIP(), []int{17} + return file_coprocessor_proto_rawDescGZIP(), []int{12} } func (x *FhevmResponses) GetCiphertextHandles() []string { @@ -979,159 +768,134 @@ var file_coprocessor_proto_rawDesc = []byte{ 0x0a, 0x11, 0x63, 0x6f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x11, 0x66, 0x68, 0x65, 0x76, 0x6d, 0x2e, 0x63, 0x6f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x1a, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x5b, 0x0a, 0x13, 0x44, 0x65, 0x62, 0x75, 0x67, 0x45, 0x6e, 0x63, - 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x44, 0x0a, 0x06, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x66, 0x68, - 0x65, 0x76, 0x6d, 0x2e, 0x63, 0x6f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x2e, - 0x44, 0x65, 0x62, 0x75, 0x67, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x73, 0x22, 0x6f, 0x0a, 0x19, 0x44, 0x65, 0x62, 0x75, 0x67, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x12, 0x16, - 0x0a, 0x06, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, - 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x54, 0x79, - 0x70, 0x65, 0x22, 0x2f, 0x0a, 0x13, 0x44, 0x65, 0x62, 0x75, 0x67, 0x44, 0x65, 0x63, 0x72, 0x79, - 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x68, 0x61, 0x6e, - 0x64, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x07, 0x68, 0x61, 0x6e, 0x64, - 0x6c, 0x65, 0x73, 0x22, 0x5d, 0x0a, 0x14, 0x44, 0x65, 0x62, 0x75, 0x67, 0x44, 0x65, 0x63, 0x72, - 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x06, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x66, 0x68, - 0x65, 0x76, 0x6d, 0x2e, 0x63, 0x6f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x2e, - 0x44, 0x65, 0x62, 0x75, 0x67, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x73, 0x22, 0x53, 0x0a, 0x1a, 0x44, 0x65, 0x62, 0x75, 0x67, 0x44, 0x65, 0x63, 0x72, 0x79, - 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, - 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0xb3, 0x01, 0x0a, 0x10, 0x41, 0x73, 0x79, 0x6e, - 0x63, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x09, - 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, - 0x1a, 0x2e, 0x66, 0x68, 0x65, 0x76, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x46, - 0x68, 0x65, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x6f, 0x70, 0x65, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, - 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x6f, - 0x75, 0x74, 0x70, 0x75, 0x74, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x40, 0x0a, 0x06, 0x69, - 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x66, 0x68, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x5d, 0x0a, 0x13, 0x54, 0x72, 0x69, 0x76, 0x69, 0x61, 0x6c, 0x45, + 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x46, 0x0a, 0x06, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x66, 0x68, 0x65, 0x76, 0x6d, 0x2e, 0x63, 0x6f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x2e, + 0x54, 0x72, 0x69, 0x76, 0x69, 0x61, 0x6c, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x52, 0x06, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x73, 0x22, 0x71, 0x0a, 0x1b, 0x54, 0x72, 0x69, 0x76, 0x69, 0x61, 0x6c, 0x45, 0x6e, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x69, 0x6e, 0x67, + 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x06, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x65, + 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x62, 0x65, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6f, 0x75, 0x74, 0x70, + 0x75, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0xb3, 0x01, 0x0a, 0x10, 0x41, 0x73, 0x79, 0x6e, 0x63, + 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, 0x09, 0x6f, + 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, + 0x2e, 0x66, 0x68, 0x65, 0x76, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x46, 0x68, + 0x65, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x6f, 0x70, 0x65, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, + 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x6f, 0x75, + 0x74, 0x70, 0x75, 0x74, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x40, 0x0a, 0x06, 0x69, 0x6e, + 0x70, 0x75, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x66, 0x68, 0x65, + 0x76, 0x6d, 0x2e, 0x63, 0x6f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x2e, 0x41, + 0x73, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, + 0x6e, 0x70, 0x75, 0x74, 0x52, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x22, 0x5f, 0x0a, 0x15, 0x41, 0x73, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x49, 0x6e, 0x70, 0x75, 0x74, 0x52, 0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x22, 0x5f, 0x0a, - 0x15, 0x41, 0x73, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x23, 0x0a, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, - 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x0b, - 0x69, 0x6e, 0x70, 0x75, 0x74, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x06, 0x73, - 0x63, 0x61, 0x6c, 0x61, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x06, 0x73, - 0x63, 0x61, 0x6c, 0x61, 0x72, 0x42, 0x07, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x22, 0x95, - 0x01, 0x0a, 0x12, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x54, 0x6f, 0x55, - 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, - 0x65, 0x78, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, - 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2b, - 0x0a, 0x11, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x68, 0x61, 0x6e, - 0x64, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x63, 0x69, 0x70, 0x68, 0x65, - 0x72, 0x74, 0x65, 0x78, 0x74, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x29, 0x0a, 0x10, 0x63, - 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, - 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x22, 0x52, 0x0a, 0x0d, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x54, - 0x6f, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x70, 0x75, 0x74, - 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, - 0x69, 0x6e, 0x70, 0x75, 0x74, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1c, 0x0a, 0x09, - 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x60, 0x0a, 0x1d, 0x49, 0x6e, + 0x49, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x23, 0x0a, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x68, + 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x0b, 0x69, + 0x6e, 0x70, 0x75, 0x74, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x06, 0x73, 0x63, + 0x61, 0x6c, 0x61, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x06, 0x73, 0x63, + 0x61, 0x6c, 0x61, 0x72, 0x42, 0x07, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x22, 0xa4, 0x01, + 0x0a, 0x0d, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x54, 0x6f, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, + 0x23, 0x0a, 0x0d, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x50, 0x61, 0x79, + 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, + 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, + 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, + 0x25, 0x0a, 0x0e, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x22, 0x60, 0x0a, 0x1d, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x43, 0x69, 0x70, + 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, + 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x27, 0x0a, + 0x0f, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, + 0x78, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0xb4, 0x02, 0x0a, 0x17, 0x49, 0x6e, 0x70, 0x75, 0x74, + 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x55, 0x0a, 0x0d, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x68, 0x61, 0x6e, 0x64, + 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x66, 0x68, 0x65, 0x76, + 0x6d, 0x2e, 0x63, 0x6f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x68, - 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x68, 0x61, 0x6e, - 0x64, 0x6c, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, - 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x63, 0x69, - 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x70, 0x0a, 0x17, - 0x49, 0x6e, 0x70, 0x75, 0x74, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x0d, 0x69, 0x6e, 0x70, 0x75, 0x74, - 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x52, 0x0c, 0x69, 0x6e, 0x70, + 0x75, 0x74, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x12, 0x34, 0x0a, 0x15, 0x65, 0x69, 0x70, + 0x37, 0x31, 0x32, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x65, 0x69, 0x70, 0x37, 0x31, 0x32, + 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, + 0x30, 0x0a, 0x13, 0x65, 0x69, 0x70, 0x37, 0x31, 0x32, 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x65, 0x69, + 0x70, 0x37, 0x31, 0x32, 0x43, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x12, 0x30, 0x0a, 0x13, 0x65, 0x69, 0x70, 0x37, 0x31, 0x32, 0x53, 0x69, 0x67, 0x6e, 0x65, + 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, + 0x65, 0x69, 0x70, 0x37, 0x31, 0x32, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x12, 0x28, 0x0a, 0x0f, 0x65, 0x69, 0x70, 0x37, 0x31, 0x32, 0x53, 0x69, 0x67, + 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x65, 0x69, + 0x70, 0x37, 0x31, 0x32, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x6c, 0x0a, + 0x13, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x10, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x72, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x66, 0x68, 0x65, 0x76, 0x6d, 0x2e, 0x63, 0x6f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, - 0x78, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, - 0x52, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x22, 0x6c, - 0x0a, 0x13, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x10, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5f, - 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x2a, 0x2e, 0x66, 0x68, 0x65, 0x76, 0x6d, 0x2e, 0x63, 0x6f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, - 0x73, 0x6f, 0x72, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, - 0x65, 0x78, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0f, 0x75, 0x70, 0x6c, - 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x22, 0x5e, 0x0a, 0x13, - 0x41, 0x73, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x47, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x66, 0x68, 0x65, 0x76, + 0x78, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0f, 0x75, 0x70, 0x6c, 0x6f, + 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x22, 0x5e, 0x0a, 0x13, 0x41, + 0x73, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x47, 0x0a, 0x0c, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x66, 0x68, 0x65, 0x76, 0x6d, + 0x2e, 0x63, 0x6f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x2e, 0x41, 0x73, 0x79, + 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x63, + 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x61, 0x0a, 0x10, 0x49, + 0x6e, 0x70, 0x75, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, + 0x4d, 0x0a, 0x11, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, + 0x65, 0x78, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x66, 0x68, 0x65, + 0x76, 0x6d, 0x2e, 0x63, 0x6f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x2e, 0x49, + 0x6e, 0x70, 0x75, 0x74, 0x54, 0x6f, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x10, 0x69, 0x6e, + 0x70, 0x75, 0x74, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x73, 0x22, 0x3a, + 0x0a, 0x09, 0x57, 0x61, 0x69, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x2d, 0x0a, 0x12, 0x63, + 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, + 0x65, 0x78, 0x74, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x22, 0x35, 0x0a, 0x0f, 0x47, 0x65, + 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x22, 0x0a, + 0x0c, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x43, 0x6f, 0x64, + 0x65, 0x22, 0x3f, 0x0a, 0x0e, 0x46, 0x68, 0x65, 0x76, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, + 0x74, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x11, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x48, 0x61, 0x6e, 0x64, 0x6c, + 0x65, 0x73, 0x32, 0x9b, 0x03, 0x0a, 0x10, 0x46, 0x68, 0x65, 0x76, 0x6d, 0x43, 0x6f, 0x70, 0x72, + 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x12, 0x5c, 0x0a, 0x0c, 0x41, 0x73, 0x79, 0x6e, 0x63, + 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x12, 0x26, 0x2e, 0x66, 0x68, 0x65, 0x76, 0x6d, 0x2e, + 0x63, 0x6f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x2e, 0x41, 0x73, 0x79, 0x6e, + 0x63, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x22, 0x2e, 0x66, 0x68, 0x65, 0x76, 0x6d, 0x2e, 0x63, 0x6f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, + 0x73, 0x6f, 0x72, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5f, 0x0a, 0x10, 0x57, 0x61, 0x69, 0x74, 0x43, 0x6f, 0x6d, + 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x26, 0x2e, 0x66, 0x68, 0x65, 0x76, 0x6d, 0x2e, 0x63, 0x6f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x2e, 0x41, 0x73, - 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, - 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x6b, 0x0a, 0x15, - 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, - 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x52, 0x0a, 0x11, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x63, - 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x25, 0x2e, 0x66, 0x68, 0x65, 0x76, 0x6d, 0x2e, 0x63, 0x6f, 0x70, 0x72, 0x6f, 0x63, 0x65, - 0x73, 0x73, 0x6f, 0x72, 0x2e, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x54, - 0x6f, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x10, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x43, 0x69, - 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x73, 0x22, 0x61, 0x0a, 0x10, 0x49, 0x6e, 0x70, - 0x75, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x4d, 0x0a, - 0x11, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, - 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x66, 0x68, 0x65, 0x76, 0x6d, - 0x2e, 0x63, 0x6f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x2e, 0x49, 0x6e, 0x70, - 0x75, 0x74, 0x54, 0x6f, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x10, 0x69, 0x6e, 0x70, 0x75, - 0x74, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x73, 0x22, 0x3a, 0x0a, 0x09, - 0x57, 0x61, 0x69, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x69, 0x70, - 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, - 0x74, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x22, 0x35, 0x0a, 0x0f, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x72, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x0c, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x22, - 0x3f, 0x0a, 0x0e, 0x46, 0x68, 0x65, 0x76, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x5f, - 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x63, - 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x73, - 0x32, 0xea, 0x04, 0x0a, 0x10, 0x46, 0x68, 0x65, 0x76, 0x6d, 0x43, 0x6f, 0x70, 0x72, 0x6f, 0x63, - 0x65, 0x73, 0x73, 0x6f, 0x72, 0x12, 0x5c, 0x0a, 0x0c, 0x41, 0x73, 0x79, 0x6e, 0x63, 0x43, 0x6f, - 0x6d, 0x70, 0x75, 0x74, 0x65, 0x12, 0x26, 0x2e, 0x66, 0x68, 0x65, 0x76, 0x6d, 0x2e, 0x63, 0x6f, - 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x2e, 0x41, 0x73, 0x79, 0x6e, 0x63, 0x43, - 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, - 0x66, 0x68, 0x65, 0x76, 0x6d, 0x2e, 0x63, 0x6f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, - 0x72, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x22, 0x00, 0x12, 0x63, 0x0a, 0x11, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x69, 0x70, - 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x73, 0x12, 0x28, 0x2e, 0x66, 0x68, 0x65, 0x76, 0x6d, - 0x2e, 0x63, 0x6f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x2e, 0x43, 0x69, 0x70, - 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x74, - 0x63, 0x68, 0x1a, 0x22, 0x2e, 0x66, 0x68, 0x65, 0x76, 0x6d, 0x2e, 0x63, 0x6f, 0x70, 0x72, 0x6f, - 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x5f, 0x0a, 0x10, 0x57, 0x61, 0x69, 0x74, - 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x26, 0x2e, 0x66, - 0x68, 0x65, 0x76, 0x6d, 0x2e, 0x63, 0x6f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, - 0x2e, 0x41, 0x73, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x66, 0x68, 0x65, 0x76, 0x6d, 0x2e, 0x63, 0x6f, 0x70, - 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x2e, 0x46, 0x68, 0x65, 0x76, 0x6d, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x0c, 0x55, 0x70, 0x6c, - 0x6f, 0x61, 0x64, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x23, 0x2e, 0x66, 0x68, 0x65, 0x76, - 0x6d, 0x2e, 0x63, 0x6f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x2e, 0x49, 0x6e, - 0x70, 0x75, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x74, 0x63, 0x68, 0x1a, 0x26, - 0x2e, 0x66, 0x68, 0x65, 0x76, 0x6d, 0x2e, 0x63, 0x6f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, - 0x6f, 0x72, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x6b, 0x0a, 0x16, 0x44, 0x65, 0x62, 0x75, - 0x67, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, - 0x78, 0x74, 0x12, 0x26, 0x2e, 0x66, 0x68, 0x65, 0x76, 0x6d, 0x2e, 0x63, 0x6f, 0x70, 0x72, 0x6f, - 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x44, 0x65, 0x63, 0x72, - 0x79, 0x70, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x66, 0x68, 0x65, - 0x76, 0x6d, 0x2e, 0x63, 0x6f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x2e, 0x44, - 0x65, 0x62, 0x75, 0x67, 0x44, 0x65, 0x63, 0x72, 0x79, 0x70, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x66, 0x0a, 0x16, 0x44, 0x65, 0x62, 0x75, 0x67, 0x45, 0x6e, - 0x63, 0x72, 0x79, 0x70, 0x74, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x12, - 0x26, 0x2e, 0x66, 0x68, 0x65, 0x76, 0x6d, 0x2e, 0x63, 0x6f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, - 0x73, 0x6f, 0x72, 0x2e, 0x44, 0x65, 0x62, 0x75, 0x67, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x66, 0x68, 0x65, 0x76, 0x6d, 0x2e, - 0x63, 0x6f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x2e, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x37, 0x0a, - 0x18, 0x69, 0x6f, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x66, 0x68, 0x65, 0x76, 0x6d, 0x63, 0x6f, - 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x42, 0x10, 0x46, 0x68, 0x65, 0x76, 0x6d, - 0x43, 0x6f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x50, 0x01, 0x5a, 0x07, 0x2e, - 0x2f, 0x66, 0x68, 0x65, 0x76, 0x6d, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x21, 0x2e, 0x66, 0x68, 0x65, 0x76, 0x6d, 0x2e, 0x63, 0x6f, 0x70, 0x72, 0x6f, 0x63, + 0x65, 0x73, 0x73, 0x6f, 0x72, 0x2e, 0x46, 0x68, 0x65, 0x76, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x73, 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x0c, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, + 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x23, 0x2e, 0x66, 0x68, 0x65, 0x76, 0x6d, 0x2e, 0x63, + 0x6f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x2e, 0x49, 0x6e, 0x70, 0x75, 0x74, + 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x61, 0x74, 0x63, 0x68, 0x1a, 0x26, 0x2e, 0x66, 0x68, + 0x65, 0x76, 0x6d, 0x2e, 0x63, 0x6f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x2e, + 0x49, 0x6e, 0x70, 0x75, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x69, 0x0a, 0x19, 0x54, 0x72, 0x69, 0x76, 0x69, 0x61, 0x6c, + 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, + 0x74, 0x73, 0x12, 0x26, 0x2e, 0x66, 0x68, 0x65, 0x76, 0x6d, 0x2e, 0x63, 0x6f, 0x70, 0x72, 0x6f, + 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x2e, 0x54, 0x72, 0x69, 0x76, 0x69, 0x61, 0x6c, 0x45, 0x6e, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x42, 0x61, 0x74, 0x63, 0x68, 0x1a, 0x22, 0x2e, 0x66, 0x68, 0x65, + 0x76, 0x6d, 0x2e, 0x63, 0x6f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x2e, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x42, 0x37, 0x0a, 0x18, 0x69, 0x6f, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x66, 0x68, 0x65, 0x76, + 0x6d, 0x63, 0x6f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x42, 0x10, 0x46, 0x68, + 0x65, 0x76, 0x6d, 0x43, 0x6f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x6f, 0x72, 0x50, 0x01, + 0x5a, 0x07, 0x2e, 0x2f, 0x66, 0x68, 0x65, 0x76, 0x6d, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -1146,55 +910,44 @@ func file_coprocessor_proto_rawDescGZIP() []byte { return file_coprocessor_proto_rawDescData } -var file_coprocessor_proto_msgTypes = make([]protoimpl.MessageInfo, 18) +var file_coprocessor_proto_msgTypes = make([]protoimpl.MessageInfo, 13) var file_coprocessor_proto_goTypes = []any{ - (*DebugEncryptRequest)(nil), // 0: fhevm.coprocessor.DebugEncryptRequest - (*DebugEncryptRequestSingle)(nil), // 1: fhevm.coprocessor.DebugEncryptRequestSingle - (*DebugDecryptRequest)(nil), // 2: fhevm.coprocessor.DebugDecryptRequest - (*DebugDecryptResponse)(nil), // 3: fhevm.coprocessor.DebugDecryptResponse - (*DebugDecryptResponseSingle)(nil), // 4: fhevm.coprocessor.DebugDecryptResponseSingle - (*AsyncComputation)(nil), // 5: fhevm.coprocessor.AsyncComputation - (*AsyncComputationInput)(nil), // 6: fhevm.coprocessor.AsyncComputationInput - (*CiphertextToUpload)(nil), // 7: fhevm.coprocessor.CiphertextToUpload - (*InputToUpload)(nil), // 8: fhevm.coprocessor.InputToUpload - (*InputCiphertextResponseHandle)(nil), // 9: fhevm.coprocessor.InputCiphertextResponseHandle - (*InputCiphertextResponse)(nil), // 10: fhevm.coprocessor.InputCiphertextResponse - (*InputUploadResponse)(nil), // 11: fhevm.coprocessor.InputUploadResponse - (*AsyncComputeRequest)(nil), // 12: fhevm.coprocessor.AsyncComputeRequest - (*CiphertextUploadBatch)(nil), // 13: fhevm.coprocessor.CiphertextUploadBatch - (*InputUploadBatch)(nil), // 14: fhevm.coprocessor.InputUploadBatch - (*WaitBatch)(nil), // 15: fhevm.coprocessor.WaitBatch - (*GenericResponse)(nil), // 16: fhevm.coprocessor.GenericResponse - (*FhevmResponses)(nil), // 17: fhevm.coprocessor.FhevmResponses - (FheOperation)(0), // 18: fhevm.common.FheOperation + (*TrivialEncryptBatch)(nil), // 0: fhevm.coprocessor.TrivialEncryptBatch + (*TrivialEncryptRequestSingle)(nil), // 1: fhevm.coprocessor.TrivialEncryptRequestSingle + (*AsyncComputation)(nil), // 2: fhevm.coprocessor.AsyncComputation + (*AsyncComputationInput)(nil), // 3: fhevm.coprocessor.AsyncComputationInput + (*InputToUpload)(nil), // 4: fhevm.coprocessor.InputToUpload + (*InputCiphertextResponseHandle)(nil), // 5: fhevm.coprocessor.InputCiphertextResponseHandle + (*InputCiphertextResponse)(nil), // 6: fhevm.coprocessor.InputCiphertextResponse + (*InputUploadResponse)(nil), // 7: fhevm.coprocessor.InputUploadResponse + (*AsyncComputeRequest)(nil), // 8: fhevm.coprocessor.AsyncComputeRequest + (*InputUploadBatch)(nil), // 9: fhevm.coprocessor.InputUploadBatch + (*WaitBatch)(nil), // 10: fhevm.coprocessor.WaitBatch + (*GenericResponse)(nil), // 11: fhevm.coprocessor.GenericResponse + (*FhevmResponses)(nil), // 12: fhevm.coprocessor.FhevmResponses + (FheOperation)(0), // 13: fhevm.common.FheOperation } var file_coprocessor_proto_depIdxs = []int32{ - 1, // 0: fhevm.coprocessor.DebugEncryptRequest.values:type_name -> fhevm.coprocessor.DebugEncryptRequestSingle - 4, // 1: fhevm.coprocessor.DebugDecryptResponse.values:type_name -> fhevm.coprocessor.DebugDecryptResponseSingle - 18, // 2: fhevm.coprocessor.AsyncComputation.operation:type_name -> fhevm.common.FheOperation - 6, // 3: fhevm.coprocessor.AsyncComputation.inputs:type_name -> fhevm.coprocessor.AsyncComputationInput - 9, // 4: fhevm.coprocessor.InputCiphertextResponse.input_handles:type_name -> fhevm.coprocessor.InputCiphertextResponseHandle - 10, // 5: fhevm.coprocessor.InputUploadResponse.upload_responses:type_name -> fhevm.coprocessor.InputCiphertextResponse - 5, // 6: fhevm.coprocessor.AsyncComputeRequest.computations:type_name -> fhevm.coprocessor.AsyncComputation - 7, // 7: fhevm.coprocessor.CiphertextUploadBatch.input_ciphertexts:type_name -> fhevm.coprocessor.CiphertextToUpload - 8, // 8: fhevm.coprocessor.InputUploadBatch.input_ciphertexts:type_name -> fhevm.coprocessor.InputToUpload - 12, // 9: fhevm.coprocessor.FhevmCoprocessor.AsyncCompute:input_type -> fhevm.coprocessor.AsyncComputeRequest - 13, // 10: fhevm.coprocessor.FhevmCoprocessor.UploadCiphertexts:input_type -> fhevm.coprocessor.CiphertextUploadBatch - 12, // 11: fhevm.coprocessor.FhevmCoprocessor.WaitComputations:input_type -> fhevm.coprocessor.AsyncComputeRequest - 14, // 12: fhevm.coprocessor.FhevmCoprocessor.UploadInputs:input_type -> fhevm.coprocessor.InputUploadBatch - 2, // 13: fhevm.coprocessor.FhevmCoprocessor.DebugDecryptCiphertext:input_type -> fhevm.coprocessor.DebugDecryptRequest - 0, // 14: fhevm.coprocessor.FhevmCoprocessor.DebugEncryptCiphertext:input_type -> fhevm.coprocessor.DebugEncryptRequest - 16, // 15: fhevm.coprocessor.FhevmCoprocessor.AsyncCompute:output_type -> fhevm.coprocessor.GenericResponse - 16, // 16: fhevm.coprocessor.FhevmCoprocessor.UploadCiphertexts:output_type -> fhevm.coprocessor.GenericResponse - 17, // 17: fhevm.coprocessor.FhevmCoprocessor.WaitComputations:output_type -> fhevm.coprocessor.FhevmResponses - 11, // 18: fhevm.coprocessor.FhevmCoprocessor.UploadInputs:output_type -> fhevm.coprocessor.InputUploadResponse - 3, // 19: fhevm.coprocessor.FhevmCoprocessor.DebugDecryptCiphertext:output_type -> fhevm.coprocessor.DebugDecryptResponse - 16, // 20: fhevm.coprocessor.FhevmCoprocessor.DebugEncryptCiphertext:output_type -> fhevm.coprocessor.GenericResponse - 15, // [15:21] is the sub-list for method output_type - 9, // [9:15] is the sub-list for method input_type - 9, // [9:9] is the sub-list for extension type_name - 9, // [9:9] is the sub-list for extension extendee - 0, // [0:9] is the sub-list for field type_name + 1, // 0: fhevm.coprocessor.TrivialEncryptBatch.values:type_name -> fhevm.coprocessor.TrivialEncryptRequestSingle + 13, // 1: fhevm.coprocessor.AsyncComputation.operation:type_name -> fhevm.common.FheOperation + 3, // 2: fhevm.coprocessor.AsyncComputation.inputs:type_name -> fhevm.coprocessor.AsyncComputationInput + 5, // 3: fhevm.coprocessor.InputCiphertextResponse.input_handles:type_name -> fhevm.coprocessor.InputCiphertextResponseHandle + 6, // 4: fhevm.coprocessor.InputUploadResponse.upload_responses:type_name -> fhevm.coprocessor.InputCiphertextResponse + 2, // 5: fhevm.coprocessor.AsyncComputeRequest.computations:type_name -> fhevm.coprocessor.AsyncComputation + 4, // 6: fhevm.coprocessor.InputUploadBatch.input_ciphertexts:type_name -> fhevm.coprocessor.InputToUpload + 8, // 7: fhevm.coprocessor.FhevmCoprocessor.AsyncCompute:input_type -> fhevm.coprocessor.AsyncComputeRequest + 8, // 8: fhevm.coprocessor.FhevmCoprocessor.WaitComputations:input_type -> fhevm.coprocessor.AsyncComputeRequest + 9, // 9: fhevm.coprocessor.FhevmCoprocessor.UploadInputs:input_type -> fhevm.coprocessor.InputUploadBatch + 0, // 10: fhevm.coprocessor.FhevmCoprocessor.TrivialEncryptCiphertexts:input_type -> fhevm.coprocessor.TrivialEncryptBatch + 11, // 11: fhevm.coprocessor.FhevmCoprocessor.AsyncCompute:output_type -> fhevm.coprocessor.GenericResponse + 12, // 12: fhevm.coprocessor.FhevmCoprocessor.WaitComputations:output_type -> fhevm.coprocessor.FhevmResponses + 7, // 13: fhevm.coprocessor.FhevmCoprocessor.UploadInputs:output_type -> fhevm.coprocessor.InputUploadResponse + 11, // 14: fhevm.coprocessor.FhevmCoprocessor.TrivialEncryptCiphertexts:output_type -> fhevm.coprocessor.GenericResponse + 11, // [11:15] is the sub-list for method output_type + 7, // [7:11] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name } func init() { file_coprocessor_proto_init() } @@ -1205,7 +958,7 @@ func file_coprocessor_proto_init() { file_common_proto_init() if !protoimpl.UnsafeEnabled { file_coprocessor_proto_msgTypes[0].Exporter = func(v any, i int) any { - switch v := v.(*DebugEncryptRequest); i { + switch v := v.(*TrivialEncryptBatch); i { case 0: return &v.state case 1: @@ -1217,7 +970,7 @@ func file_coprocessor_proto_init() { } } file_coprocessor_proto_msgTypes[1].Exporter = func(v any, i int) any { - switch v := v.(*DebugEncryptRequestSingle); i { + switch v := v.(*TrivialEncryptRequestSingle); i { case 0: return &v.state case 1: @@ -1229,42 +982,6 @@ func file_coprocessor_proto_init() { } } file_coprocessor_proto_msgTypes[2].Exporter = func(v any, i int) any { - switch v := v.(*DebugDecryptRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_coprocessor_proto_msgTypes[3].Exporter = func(v any, i int) any { - switch v := v.(*DebugDecryptResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_coprocessor_proto_msgTypes[4].Exporter = func(v any, i int) any { - switch v := v.(*DebugDecryptResponseSingle); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_coprocessor_proto_msgTypes[5].Exporter = func(v any, i int) any { switch v := v.(*AsyncComputation); i { case 0: return &v.state @@ -1276,7 +993,7 @@ func file_coprocessor_proto_init() { return nil } } - file_coprocessor_proto_msgTypes[6].Exporter = func(v any, i int) any { + file_coprocessor_proto_msgTypes[3].Exporter = func(v any, i int) any { switch v := v.(*AsyncComputationInput); i { case 0: return &v.state @@ -1288,19 +1005,7 @@ func file_coprocessor_proto_init() { return nil } } - file_coprocessor_proto_msgTypes[7].Exporter = func(v any, i int) any { - switch v := v.(*CiphertextToUpload); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_coprocessor_proto_msgTypes[8].Exporter = func(v any, i int) any { + file_coprocessor_proto_msgTypes[4].Exporter = func(v any, i int) any { switch v := v.(*InputToUpload); i { case 0: return &v.state @@ -1312,7 +1017,7 @@ func file_coprocessor_proto_init() { return nil } } - file_coprocessor_proto_msgTypes[9].Exporter = func(v any, i int) any { + file_coprocessor_proto_msgTypes[5].Exporter = func(v any, i int) any { switch v := v.(*InputCiphertextResponseHandle); i { case 0: return &v.state @@ -1324,7 +1029,7 @@ func file_coprocessor_proto_init() { return nil } } - file_coprocessor_proto_msgTypes[10].Exporter = func(v any, i int) any { + file_coprocessor_proto_msgTypes[6].Exporter = func(v any, i int) any { switch v := v.(*InputCiphertextResponse); i { case 0: return &v.state @@ -1336,7 +1041,7 @@ func file_coprocessor_proto_init() { return nil } } - file_coprocessor_proto_msgTypes[11].Exporter = func(v any, i int) any { + file_coprocessor_proto_msgTypes[7].Exporter = func(v any, i int) any { switch v := v.(*InputUploadResponse); i { case 0: return &v.state @@ -1348,7 +1053,7 @@ func file_coprocessor_proto_init() { return nil } } - file_coprocessor_proto_msgTypes[12].Exporter = func(v any, i int) any { + file_coprocessor_proto_msgTypes[8].Exporter = func(v any, i int) any { switch v := v.(*AsyncComputeRequest); i { case 0: return &v.state @@ -1360,19 +1065,7 @@ func file_coprocessor_proto_init() { return nil } } - file_coprocessor_proto_msgTypes[13].Exporter = func(v any, i int) any { - switch v := v.(*CiphertextUploadBatch); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_coprocessor_proto_msgTypes[14].Exporter = func(v any, i int) any { + file_coprocessor_proto_msgTypes[9].Exporter = func(v any, i int) any { switch v := v.(*InputUploadBatch); i { case 0: return &v.state @@ -1384,7 +1077,7 @@ func file_coprocessor_proto_init() { return nil } } - file_coprocessor_proto_msgTypes[15].Exporter = func(v any, i int) any { + file_coprocessor_proto_msgTypes[10].Exporter = func(v any, i int) any { switch v := v.(*WaitBatch); i { case 0: return &v.state @@ -1396,7 +1089,7 @@ func file_coprocessor_proto_init() { return nil } } - file_coprocessor_proto_msgTypes[16].Exporter = func(v any, i int) any { + file_coprocessor_proto_msgTypes[11].Exporter = func(v any, i int) any { switch v := v.(*GenericResponse); i { case 0: return &v.state @@ -1408,7 +1101,7 @@ func file_coprocessor_proto_init() { return nil } } - file_coprocessor_proto_msgTypes[17].Exporter = func(v any, i int) any { + file_coprocessor_proto_msgTypes[12].Exporter = func(v any, i int) any { switch v := v.(*FhevmResponses); i { case 0: return &v.state @@ -1421,7 +1114,7 @@ func file_coprocessor_proto_init() { } } } - file_coprocessor_proto_msgTypes[6].OneofWrappers = []any{ + file_coprocessor_proto_msgTypes[3].OneofWrappers = []any{ (*AsyncComputationInput_InputHandle)(nil), (*AsyncComputationInput_Scalar)(nil), } @@ -1431,7 +1124,7 @@ func file_coprocessor_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_coprocessor_proto_rawDesc, NumEnums: 0, - NumMessages: 18, + NumMessages: 13, NumExtensions: 0, NumServices: 1, }, diff --git a/fhevm-engine/fhevm-go-coproc/fhevm/coprocessor_grpc.pb.go b/fhevm-engine/fhevm-go-coproc/fhevm/coprocessor_grpc.pb.go new file mode 100644 index 00000000..2d3bfc07 --- /dev/null +++ b/fhevm-engine/fhevm-go-coproc/fhevm/coprocessor_grpc.pb.go @@ -0,0 +1,235 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.5.1 +// - protoc v5.27.3 +// source: coprocessor.proto + +package fhevm + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + FhevmCoprocessor_AsyncCompute_FullMethodName = "/fhevm.coprocessor.FhevmCoprocessor/AsyncCompute" + FhevmCoprocessor_WaitComputations_FullMethodName = "/fhevm.coprocessor.FhevmCoprocessor/WaitComputations" + FhevmCoprocessor_UploadInputs_FullMethodName = "/fhevm.coprocessor.FhevmCoprocessor/UploadInputs" + FhevmCoprocessor_TrivialEncryptCiphertexts_FullMethodName = "/fhevm.coprocessor.FhevmCoprocessor/TrivialEncryptCiphertexts" +) + +// FhevmCoprocessorClient is the client API for FhevmCoprocessor service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type FhevmCoprocessorClient interface { + AsyncCompute(ctx context.Context, in *AsyncComputeRequest, opts ...grpc.CallOption) (*GenericResponse, error) + WaitComputations(ctx context.Context, in *AsyncComputeRequest, opts ...grpc.CallOption) (*FhevmResponses, error) + UploadInputs(ctx context.Context, in *InputUploadBatch, opts ...grpc.CallOption) (*InputUploadResponse, error) + TrivialEncryptCiphertexts(ctx context.Context, in *TrivialEncryptBatch, opts ...grpc.CallOption) (*GenericResponse, error) +} + +type fhevmCoprocessorClient struct { + cc grpc.ClientConnInterface +} + +func NewFhevmCoprocessorClient(cc grpc.ClientConnInterface) FhevmCoprocessorClient { + return &fhevmCoprocessorClient{cc} +} + +func (c *fhevmCoprocessorClient) AsyncCompute(ctx context.Context, in *AsyncComputeRequest, opts ...grpc.CallOption) (*GenericResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GenericResponse) + err := c.cc.Invoke(ctx, FhevmCoprocessor_AsyncCompute_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *fhevmCoprocessorClient) WaitComputations(ctx context.Context, in *AsyncComputeRequest, opts ...grpc.CallOption) (*FhevmResponses, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(FhevmResponses) + err := c.cc.Invoke(ctx, FhevmCoprocessor_WaitComputations_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *fhevmCoprocessorClient) UploadInputs(ctx context.Context, in *InputUploadBatch, opts ...grpc.CallOption) (*InputUploadResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(InputUploadResponse) + err := c.cc.Invoke(ctx, FhevmCoprocessor_UploadInputs_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *fhevmCoprocessorClient) TrivialEncryptCiphertexts(ctx context.Context, in *TrivialEncryptBatch, opts ...grpc.CallOption) (*GenericResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GenericResponse) + err := c.cc.Invoke(ctx, FhevmCoprocessor_TrivialEncryptCiphertexts_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// FhevmCoprocessorServer is the server API for FhevmCoprocessor service. +// All implementations must embed UnimplementedFhevmCoprocessorServer +// for forward compatibility. +type FhevmCoprocessorServer interface { + AsyncCompute(context.Context, *AsyncComputeRequest) (*GenericResponse, error) + WaitComputations(context.Context, *AsyncComputeRequest) (*FhevmResponses, error) + UploadInputs(context.Context, *InputUploadBatch) (*InputUploadResponse, error) + TrivialEncryptCiphertexts(context.Context, *TrivialEncryptBatch) (*GenericResponse, error) + mustEmbedUnimplementedFhevmCoprocessorServer() +} + +// UnimplementedFhevmCoprocessorServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedFhevmCoprocessorServer struct{} + +func (UnimplementedFhevmCoprocessorServer) AsyncCompute(context.Context, *AsyncComputeRequest) (*GenericResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AsyncCompute not implemented") +} +func (UnimplementedFhevmCoprocessorServer) WaitComputations(context.Context, *AsyncComputeRequest) (*FhevmResponses, error) { + return nil, status.Errorf(codes.Unimplemented, "method WaitComputations not implemented") +} +func (UnimplementedFhevmCoprocessorServer) UploadInputs(context.Context, *InputUploadBatch) (*InputUploadResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UploadInputs not implemented") +} +func (UnimplementedFhevmCoprocessorServer) TrivialEncryptCiphertexts(context.Context, *TrivialEncryptBatch) (*GenericResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method TrivialEncryptCiphertexts not implemented") +} +func (UnimplementedFhevmCoprocessorServer) mustEmbedUnimplementedFhevmCoprocessorServer() {} +func (UnimplementedFhevmCoprocessorServer) testEmbeddedByValue() {} + +// UnsafeFhevmCoprocessorServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to FhevmCoprocessorServer will +// result in compilation errors. +type UnsafeFhevmCoprocessorServer interface { + mustEmbedUnimplementedFhevmCoprocessorServer() +} + +func RegisterFhevmCoprocessorServer(s grpc.ServiceRegistrar, srv FhevmCoprocessorServer) { + // If the following call pancis, it indicates UnimplementedFhevmCoprocessorServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&FhevmCoprocessor_ServiceDesc, srv) +} + +func _FhevmCoprocessor_AsyncCompute_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AsyncComputeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FhevmCoprocessorServer).AsyncCompute(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: FhevmCoprocessor_AsyncCompute_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FhevmCoprocessorServer).AsyncCompute(ctx, req.(*AsyncComputeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _FhevmCoprocessor_WaitComputations_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AsyncComputeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FhevmCoprocessorServer).WaitComputations(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: FhevmCoprocessor_WaitComputations_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FhevmCoprocessorServer).WaitComputations(ctx, req.(*AsyncComputeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _FhevmCoprocessor_UploadInputs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InputUploadBatch) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FhevmCoprocessorServer).UploadInputs(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: FhevmCoprocessor_UploadInputs_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FhevmCoprocessorServer).UploadInputs(ctx, req.(*InputUploadBatch)) + } + return interceptor(ctx, in, info, handler) +} + +func _FhevmCoprocessor_TrivialEncryptCiphertexts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TrivialEncryptBatch) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FhevmCoprocessorServer).TrivialEncryptCiphertexts(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: FhevmCoprocessor_TrivialEncryptCiphertexts_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FhevmCoprocessorServer).TrivialEncryptCiphertexts(ctx, req.(*TrivialEncryptBatch)) + } + return interceptor(ctx, in, info, handler) +} + +// FhevmCoprocessor_ServiceDesc is the grpc.ServiceDesc for FhevmCoprocessor service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var FhevmCoprocessor_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "fhevm.coprocessor.FhevmCoprocessor", + HandlerType: (*FhevmCoprocessorServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "AsyncCompute", + Handler: _FhevmCoprocessor_AsyncCompute_Handler, + }, + { + MethodName: "WaitComputations", + Handler: _FhevmCoprocessor_WaitComputations_Handler, + }, + { + MethodName: "UploadInputs", + Handler: _FhevmCoprocessor_UploadInputs_Handler, + }, + { + MethodName: "TrivialEncryptCiphertexts", + Handler: _FhevmCoprocessor_TrivialEncryptCiphertexts_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "coprocessor.proto", +} diff --git a/fhevm-engine/fhevm-go-coproc/fhevm/fhelib.go b/fhevm-engine/fhevm-go-coproc/fhevm/fhelib.go index 236223b1..45084c3f 100644 --- a/fhevm-engine/fhevm-go-coproc/fhevm/fhelib.go +++ b/fhevm-engine/fhevm-go-coproc/fhevm/fhelib.go @@ -12,7 +12,7 @@ type FheLibMethod struct { Name string // types of the arguments that the fhelib function take. format is "(type1,type2...)" (e.g "(uint256,bytes1)") ArgTypes string - runFunction func(sess CoprocessorSession, input []byte, outputHandle []byte) error + runFunction func(sess CoprocessorSession, input []byte, ed ExtraData, outputHandle []byte) error ScalarSupport bool NonScalarDisabled bool } diff --git a/fhevm-engine/fhevm-go-coproc/fhevm/fhelib_ops.go b/fhevm-engine/fhevm-go-coproc/fhevm/fhelib_ops.go index c9ed5099..d7ea39a2 100644 --- a/fhevm-engine/fhevm-go-coproc/fhevm/fhelib_ops.go +++ b/fhevm-engine/fhevm-go-coproc/fhevm/fhelib_ops.go @@ -10,7 +10,7 @@ func handleType(handle []byte) FheUintType { return FheUintType(handle[30]) } -func fheAddRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte) error { +func fheAddRun(sess CoprocessorSession, unslicedInput []byte, _ ExtraData, outputHandle []byte) error { if len(unslicedInput) < 65 { return fmt.Errorf("expected at least 65 bytes as input, got %d", len(unslicedInput)) } @@ -80,7 +80,7 @@ func fheAddRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byt } } -func fheSubRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte) error { +func fheSubRun(sess CoprocessorSession, unslicedInput []byte, _ ExtraData, outputHandle []byte) error { if len(unslicedInput) < 65 { return fmt.Errorf("expected at least 65 bytes as input, got %d", len(unslicedInput)) } @@ -150,7 +150,7 @@ func fheSubRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byt } } -func fheMulRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte) error { +func fheMulRun(sess CoprocessorSession, unslicedInput []byte, _ ExtraData, outputHandle []byte) error { if len(unslicedInput) < 65 { return fmt.Errorf("expected at least 65 bytes as input, got %d", len(unslicedInput)) } @@ -220,7 +220,7 @@ func fheMulRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byt } } -func fheRemRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte) error { +func fheRemRun(sess CoprocessorSession, unslicedInput []byte, _ ExtraData, outputHandle []byte) error { if len(unslicedInput) < 65 { return fmt.Errorf("expected at least 65 bytes as input, got %d", len(unslicedInput)) } @@ -289,7 +289,7 @@ func fheRemRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byt } } -func fheBitAndRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte) error { +func fheBitAndRun(sess CoprocessorSession, unslicedInput []byte, _ ExtraData, outputHandle []byte) error { if len(unslicedInput) < 65 { return fmt.Errorf("expected at least 65 bytes as input, got %d", len(unslicedInput)) } @@ -333,7 +333,7 @@ func fheBitAndRun(sess CoprocessorSession, unslicedInput []byte, outputHandle [] } } -func fheBitOrRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte) error { +func fheBitOrRun(sess CoprocessorSession, unslicedInput []byte, _ ExtraData, outputHandle []byte) error { if len(unslicedInput) < 65 { return fmt.Errorf("expected at least 65 bytes as input, got %d", len(unslicedInput)) } @@ -377,7 +377,7 @@ func fheBitOrRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []b } } -func fheBitXorRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte) error { +func fheBitXorRun(sess CoprocessorSession, unslicedInput []byte, _ ExtraData, outputHandle []byte) error { if len(unslicedInput) < 65 { return fmt.Errorf("expected at least 65 bytes as input, got %d", len(unslicedInput)) } @@ -421,7 +421,7 @@ func fheBitXorRun(sess CoprocessorSession, unslicedInput []byte, outputHandle [] } } -func fheShlRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte) error { +func fheShlRun(sess CoprocessorSession, unslicedInput []byte, _ ExtraData, outputHandle []byte) error { if len(unslicedInput) < 65 { return fmt.Errorf("expected at least 65 bytes as input, got %d", len(unslicedInput)) } @@ -489,7 +489,7 @@ func fheShlRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byt } } -func fheShrRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte) error { +func fheShrRun(sess CoprocessorSession, unslicedInput []byte, _ ExtraData, outputHandle []byte) error { if len(unslicedInput) < 65 { return fmt.Errorf("expected at least 65 bytes as input, got %d", len(unslicedInput)) } @@ -557,7 +557,7 @@ func fheShrRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byt } } -func fheRotlRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte) error { +func fheRotlRun(sess CoprocessorSession, unslicedInput []byte, _ ExtraData, outputHandle []byte) error { if len(unslicedInput) < 65 { return fmt.Errorf("expected at least 65 bytes as input, got %d", len(unslicedInput)) } @@ -625,7 +625,7 @@ func fheRotlRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []by } } -func fheRotrRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte) error { +func fheRotrRun(sess CoprocessorSession, unslicedInput []byte, _ ExtraData, outputHandle []byte) error { if len(unslicedInput) < 65 { return fmt.Errorf("expected at least 65 bytes as input, got %d", len(unslicedInput)) } @@ -693,7 +693,7 @@ func fheRotrRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []by } } -func fheEqRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte) error { +func fheEqRun(sess CoprocessorSession, unslicedInput []byte, _ ExtraData, outputHandle []byte) error { if len(unslicedInput) < 65 { return fmt.Errorf("expected at least 65 bytes as input, got %d", len(unslicedInput)) } @@ -761,7 +761,7 @@ func fheEqRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte } } -func fheNeRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte) error { +func fheNeRun(sess CoprocessorSession, unslicedInput []byte, _ ExtraData, outputHandle []byte) error { if len(unslicedInput) < 65 { return fmt.Errorf("expected at least 65 bytes as input, got %d", len(unslicedInput)) } @@ -829,7 +829,7 @@ func fheNeRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte } } -func fheGeRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte) error { +func fheGeRun(sess CoprocessorSession, unslicedInput []byte, _ ExtraData, outputHandle []byte) error { if len(unslicedInput) < 65 { return fmt.Errorf("expected at least 65 bytes as input, got %d", len(unslicedInput)) } @@ -897,7 +897,7 @@ func fheGeRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte } } -func fheGtRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte) error { +func fheGtRun(sess CoprocessorSession, unslicedInput []byte, _ ExtraData, outputHandle []byte) error { if len(unslicedInput) < 65 { return fmt.Errorf("expected at least 65 bytes as input, got %d", len(unslicedInput)) } @@ -965,7 +965,7 @@ func fheGtRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte } } -func fheLeRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte) error { +func fheLeRun(sess CoprocessorSession, unslicedInput []byte, _ ExtraData, outputHandle []byte) error { if len(unslicedInput) < 65 { return fmt.Errorf("expected at least 65 bytes as input, got %d", len(unslicedInput)) } @@ -1033,7 +1033,7 @@ func fheLeRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte } } -func fheLtRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte) error { +func fheLtRun(sess CoprocessorSession, unslicedInput []byte, _ ExtraData, outputHandle []byte) error { if len(unslicedInput) < 65 { return fmt.Errorf("expected at least 65 bytes as input, got %d", len(unslicedInput)) } @@ -1101,7 +1101,7 @@ func fheLtRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte } } -func fheMinRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte) error { +func fheMinRun(sess CoprocessorSession, unslicedInput []byte, _ ExtraData, outputHandle []byte) error { if len(unslicedInput) < 65 { return fmt.Errorf("expected at least 65 bytes as input, got %d", len(unslicedInput)) } @@ -1169,7 +1169,7 @@ func fheMinRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byt } } -func fheMaxRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte) error { +func fheMaxRun(sess CoprocessorSession, unslicedInput []byte, _ ExtraData, outputHandle []byte) error { if len(unslicedInput) < 65 { return fmt.Errorf("expected at least 65 bytes as input, got %d", len(unslicedInput)) } @@ -1237,7 +1237,7 @@ func fheMaxRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byt } } -func fheNegRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte) error { +func fheNegRun(sess CoprocessorSession, unslicedInput []byte, _ ExtraData, outputHandle []byte) error { if len(unslicedInput) < 32 { return fmt.Errorf("expected at least 65 bytes as input, got %d", len(unslicedInput)) } @@ -1267,7 +1267,7 @@ func fheNegRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byt return nil } -func fheNotRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte) error { +func fheNotRun(sess CoprocessorSession, unslicedInput []byte, _ ExtraData, outputHandle []byte) error { if len(unslicedInput) < 32 { return fmt.Errorf("expected at least 65 bytes as input, got %d", len(unslicedInput)) } @@ -1297,7 +1297,7 @@ func fheNotRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byt return nil } -func fheIfThenElseRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte) error { +func fheIfThenElseRun(sess CoprocessorSession, unslicedInput []byte, _ ExtraData, outputHandle []byte) error { if len(unslicedInput) < 96 { return fmt.Errorf("expected at least 96 bytes as input, got %d", len(unslicedInput)) } @@ -1341,7 +1341,7 @@ func fheIfThenElseRun(sess CoprocessorSession, unslicedInput []byte, outputHandl return nil } -func castRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte) error { +func castRun(sess CoprocessorSession, unslicedInput []byte, _ ExtraData, outputHandle []byte) error { if len(unslicedInput) < 33 { return fmt.Errorf("expected at least 33 bytes as input, got %d", len(unslicedInput)) } @@ -1382,7 +1382,7 @@ func castRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte) return nil } -func fheRandRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte) error { +func fheRandRun(sess CoprocessorSession, unslicedInput []byte, ed ExtraData, outputHandle []byte) error { if len(unslicedInput) < 1 { return fmt.Errorf("expected at least 1 bytes as input, got %d", len(unslicedInput)) } @@ -1397,7 +1397,12 @@ func fheRandRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []by OutputHandle: outputHandle, Operands: []ComputationOperand{ { - Handle: outputHandle, + Handle: ed.RandomCounter[:], + FheUintType: FheUint256, + IsScalar: true, + }, + { + Handle: []byte{resultTypeByte}, FheUintType: FheUint8, IsScalar: true, }, @@ -1410,7 +1415,7 @@ func fheRandRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []by return nil } -func fheRandBoundedRun(sess CoprocessorSession, unslicedInput []byte, outputHandle []byte) error { +func fheRandBoundedRun(sess CoprocessorSession, unslicedInput []byte, ed ExtraData, outputHandle []byte) error { if len(unslicedInput) < 33 { return fmt.Errorf("expected at least 1 bytes as input, got %d", len(unslicedInput)) } @@ -1427,13 +1432,18 @@ func fheRandBoundedRun(sess CoprocessorSession, unslicedInput []byte, outputHand Operation: FheRandBounded, OutputHandle: outputHandle, Operands: []ComputationOperand{ + { + Handle: ed.RandomCounter[:], + FheUintType: FheUint256, + IsScalar: true, + }, { Handle: unslicedInput[0:32], - FheUintType: FheUint32, + FheUintType: FheUint256, IsScalar: true, }, { - Handle: outputHandle, + Handle: []byte{resultTypeByte}, FheUintType: FheUint8, IsScalar: true, }, diff --git a/proto/coprocessor.proto b/proto/coprocessor.proto index 40c84262..34bda54f 100644 --- a/proto/coprocessor.proto +++ b/proto/coprocessor.proto @@ -11,38 +11,21 @@ import "common.proto"; service FhevmCoprocessor { rpc AsyncCompute (AsyncComputeRequest) returns (GenericResponse) {} - rpc UploadCiphertexts (CiphertextUploadBatch) returns (GenericResponse) {} rpc WaitComputations (AsyncComputeRequest) returns (FhevmResponses) {} rpc UploadInputs (InputUploadBatch) returns (InputUploadResponse) {} - // for debugging, should be removed in prod - rpc DebugDecryptCiphertext (DebugDecryptRequest) returns (DebugDecryptResponse) {} - // for debugging, should be removed in prod - rpc DebugEncryptCiphertext (DebugEncryptRequest) returns (GenericResponse) {} + rpc TrivialEncryptCiphertexts (TrivialEncryptBatch) returns (GenericResponse) {} } -message DebugEncryptRequest { - repeated DebugEncryptRequestSingle values = 1; +message TrivialEncryptBatch { + repeated TrivialEncryptRequestSingle values = 1; } -message DebugEncryptRequestSingle { +message TrivialEncryptRequestSingle { bytes handle = 1; bytes be_value = 2; int32 output_type = 3; } -message DebugDecryptRequest { - repeated bytes handles = 1; -} - -message DebugDecryptResponse { - repeated DebugDecryptResponseSingle values = 1; -} - -message DebugDecryptResponseSingle { - int32 output_type = 1; - string value = 2; -} - message AsyncComputation { fhevm.common.FheOperation operation = 1; bytes output_handle = 3; @@ -56,12 +39,6 @@ message AsyncComputationInput { } } -message CiphertextToUpload { - int32 ciphertext_type = 1; - bytes ciphertext_handle = 2; - bytes ciphertext_bytes = 3; -} - message InputToUpload { bytes input_payload = 1; string contract_address = 2; @@ -91,10 +68,6 @@ message AsyncComputeRequest { repeated AsyncComputation computations = 1; } -message CiphertextUploadBatch { - repeated CiphertextToUpload input_ciphertexts = 1; -} - message InputUploadBatch { repeated InputToUpload input_ciphertexts = 1; } From f861e8021225578afc13488bbf41658a37af3bcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20=27birdy=27=20Danjou?= Date: Thu, 12 Sep 2024 10:38:30 +0200 Subject: [PATCH 02/13] chore: add license --- LICENSE | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..95279973 --- /dev/null +++ b/LICENSE @@ -0,0 +1,33 @@ +BSD 3-Clause Clear License + +Copyright © 2024 ZAMA. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or other +materials provided with the distribution. + +3. Neither the name of ZAMA nor the names of its contributors may be used to endorse +or promote products derived from this software without specific prior written permission. + +NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE*. +THIS SOFTWARE IS PROVIDED BY THE ZAMA AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +ZAMA OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*In addition to the rights carried by this license, ZAMA grants to the user a non-exclusive, +free and non-commercial license on all patents filed in its name relating to the open-source +code (the "Patents") for the sole purpose of evaluation, development, research, prototyping +and experimentation. From a89df7ea3ddc793ebf69bf2f770af79087a18cdd Mon Sep 17 00:00:00 2001 From: Petar Ivanov <29689712+dartdart26@users.noreply.github.com> Date: Thu, 12 Sep 2024 17:55:25 +0300 Subject: [PATCH 03/13] feat: tests starting copro with existing local DB Introduce the `COPROCESSOR_TEST_LOCAL_DB` environment variable that uses an existing local DB, but starts coprocessor service instances. --- fhevm-engine/coprocessor/src/tests/utils.rs | 80 +++++++++++++-------- 1 file changed, 52 insertions(+), 28 deletions(-) diff --git a/fhevm-engine/coprocessor/src/tests/utils.rs b/fhevm-engine/coprocessor/src/tests/utils.rs index 84095b63..e1795248 100644 --- a/fhevm-engine/coprocessor/src/tests/utils.rs +++ b/fhevm-engine/coprocessor/src/tests/utils.rs @@ -4,6 +4,7 @@ use rand::RngCore; use std::collections::BTreeMap; use std::sync::atomic::{AtomicU16, Ordering}; use testcontainers::{core::WaitFor, runners::AsyncRunner, GenericImage, ImageExt}; +use tokio::sync::watch::Receiver; pub struct TestInstance { // just to destroy container @@ -44,22 +45,63 @@ pub fn default_tenant_id() -> i32 { pub async fn setup_test_app() -> Result> { if std::env::var("COPROCESSOR_TEST_LOCALHOST").is_ok() { setup_test_app_existing_localhost().await + } else if std::env::var("COPROCESSOR_TEST_LOCAL_DB").is_ok() { + setup_test_app_existing_db().await } else { setup_test_app_custom_docker().await } } -pub async fn setup_test_app_existing_localhost() -> Result> -{ +const LOCAL_DB_URL: &str = "postgresql://postgres:postgres@127.0.0.1:5432/coprocessor"; + +pub async fn setup_test_app_existing_localhost() -> Result> { Ok(TestInstance { _container: None, app_close_channel: None, app_url: "http://127.0.0.1:50051".to_string(), - db_url: "postgresql://postgres:postgres@127.0.0.1:5432/coprocessor".to_string(), + db_url: LOCAL_DB_URL.to_string(), }) } -pub async fn setup_test_app_custom_docker() -> Result> { +pub async fn setup_test_app_existing_db() -> Result> { + let app_port = get_app_port(); + let (app_close_channel, rx) = tokio::sync::watch::channel(false); + start_coprocessor(rx, app_port, &LOCAL_DB_URL).await; + Ok(TestInstance { + _container: None, + app_close_channel: Some(app_close_channel), + app_url: format!("http://127.0.0.1:{app_port}"), + db_url: LOCAL_DB_URL.to_string(), + }) +} + +async fn start_coprocessor(rx: Receiver, app_port: u16, db_url: &str) { + let args: Args = Args { + run_bg_worker: true, + run_server: true, + generate_fhe_keys: false, + server_maximum_ciphertexts_to_schedule: 5000, + work_items_batch_size: 40, + tenant_key_cache_size: 4, + coprocessor_fhe_threads: 4, + maximum_handles_per_input: 255, + tokio_threads: 2, + pg_pool_max_connections: 2, + server_addr: format!("127.0.0.1:{app_port}"), + database_url: Some(db_url.to_string()), + maximimum_compact_inputs_upload: 10, + coprocessor_private_key: "./coprocessor.key".to_string(), + }; + + std::thread::spawn(move || { + crate::start_runtime(args, Some(rx)); + }); + + // wait until app port is opened + tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; +} + +fn get_app_port() -> u16 { static PORT_COUNTER: AtomicU16 = AtomicU16::new(10000); let app_port = PORT_COUNTER.fetch_add(1, Ordering::SeqCst); @@ -67,6 +109,11 @@ pub async fn setup_test_app_custom_docker() -> Result= 50000 { PORT_COUNTER.store(10000, Ordering::SeqCst); } + app_port +} + +async fn setup_test_app_custom_docker() -> Result> { + let app_port = get_app_port(); let container = GenericImage::new("postgres", "15.7") .with_wait_for(WaitFor::message_on_stderr( @@ -102,30 +149,7 @@ pub async fn setup_test_app_custom_docker() -> Result Date: Thu, 12 Sep 2024 19:46:19 +0300 Subject: [PATCH 04/13] fix: random handles in smoke test on existing DB --- fhevm-engine/coprocessor/src/tests/mod.rs | 32 ++++++++++++------- .../coprocessor/src/tests/operators.rs | 10 +++--- fhevm-engine/coprocessor/src/tests/utils.rs | 7 +++- 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/fhevm-engine/coprocessor/src/tests/mod.rs b/fhevm-engine/coprocessor/src/tests/mod.rs index 7dd6f893..ef21a5e0 100644 --- a/fhevm-engine/coprocessor/src/tests/mod.rs +++ b/fhevm-engine/coprocessor/src/tests/mod.rs @@ -8,12 +8,11 @@ use crate::server::coprocessor::{ TrivialEncryptRequestSingle, }; use tonic::metadata::MetadataValue; -use utils::{decrypt_ciphertexts, default_api_key, wait_until_all_ciphertexts_computed}; +use utils::{decrypt_ciphertexts, random_handle, default_api_key, wait_until_all_ciphertexts_computed}; mod errors; mod inputs; mod operators; -mod random; mod utils; #[tokio::test] @@ -29,17 +28,22 @@ async fn test_smoke() -> Result<(), Box> { let api_key_header = format!("bearer {}", default_api_key()); let ct_type = 4; // i32 + let h1 = random_handle().to_be_bytes(); + let h2 = random_handle().to_be_bytes(); + let h3 = random_handle().to_be_bytes(); + let h4 = random_handle().to_be_bytes(); + // encrypt two ciphertexts { let mut encrypt_request = tonic::Request::new(TrivialEncryptBatch { values: vec![ TrivialEncryptRequestSingle { - handle: vec![0x0a, 0xbc], + handle: h1.to_vec(), be_value: vec![123], output_type: ct_type, }, TrivialEncryptRequestSingle { - handle: vec![0x0a, 0xbd], + handle: h2.to_vec(), be_value: vec![124], output_type: ct_type, }, @@ -59,10 +63,10 @@ async fn test_smoke() -> Result<(), Box> { computations: vec![ AsyncComputation { operation: FheOperation::FheAdd.into(), - output_handle: vec![0x0a, 0xbf], + output_handle: h3.to_vec(), inputs: vec![ AsyncComputationInput { - input: Some(Input::InputHandle(vec![0x0a, 0xbe])), + input: Some(Input::InputHandle(h4.to_vec())), }, AsyncComputationInput { input: Some(Input::Scalar(vec![0x00, 0x10])), @@ -71,13 +75,13 @@ async fn test_smoke() -> Result<(), Box> { }, AsyncComputation { operation: FheOperation::FheAdd.into(), - output_handle: vec![0x0a, 0xbe], + output_handle: h4.to_vec(), inputs: vec![ AsyncComputationInput { - input: Some(Input::InputHandle(vec![0x0a, 0xbc])), + input: Some(Input::InputHandle(h1.to_vec())), }, AsyncComputationInput { - input: Some(Input::InputHandle(vec![0x0a, 0xbd])), + input: Some(Input::InputHandle(h2.to_vec())), }, ], }, @@ -96,8 +100,14 @@ async fn test_smoke() -> Result<(), Box> { // decrypt values { - let decrypt_request = vec![vec![0x0a, 0xbe], vec![0x0a, 0xbf]]; - let resp = decrypt_ciphertexts(&pool, 1, decrypt_request).await?; + let mut decrypt_request = tonic::Request::new(DebugDecryptRequest { + handles: vec![h4.to_vec(), h3.to_vec()], + }); + decrypt_request.metadata_mut().append( + "authorization", + MetadataValue::from_str(&api_key_header).unwrap(), + ); + let resp = client.debug_decrypt_ciphertext(decrypt_request).await?; println!("decrypt request: {:?}", resp); assert_eq!(resp.len(), 2); // first value diff --git a/fhevm-engine/coprocessor/src/tests/operators.rs b/fhevm-engine/coprocessor/src/tests/operators.rs index 95923d1e..7655334f 100644 --- a/fhevm-engine/coprocessor/src/tests/operators.rs +++ b/fhevm-engine/coprocessor/src/tests/operators.rs @@ -4,7 +4,7 @@ use crate::server::coprocessor::{ AsyncComputation, AsyncComputeRequest, TrivialEncryptBatch, TrivialEncryptRequestSingle, }; use crate::tests::utils::{ - decrypt_ciphertexts, random_handle_start, wait_until_all_ciphertexts_computed, + decrypt_ciphertexts, random_handle, random_handle_start, wait_until_all_ciphertexts_computed, }; use crate::{ server::coprocessor::{async_computation_input::Input, AsyncComputationInput}, @@ -85,7 +85,7 @@ async fn test_fhe_binary_operands() -> Result<(), Box> { .await?; let mut client = FhevmCoprocessorClient::connect(app.app_url().to_string()).await?; - let mut handle_counter: u64 = random_handle_start(); + let mut handle_counter: u64 = random_handle(); let mut next_handle = || { let out: u64 = handle_counter; handle_counter += 1; @@ -221,7 +221,7 @@ async fn test_fhe_unary_operands() -> Result<(), Box> { .await?; let mut client = FhevmCoprocessorClient::connect(app.app_url().to_string()).await?; - let mut handle_counter: u64 = random_handle_start(); + let mut handle_counter: u64 = random_handle(); let mut next_handle = || { let out: u64 = handle_counter; handle_counter += 1; @@ -332,7 +332,7 @@ async fn test_fhe_casts() -> Result<(), Box> { .await?; let mut client = FhevmCoprocessorClient::connect(app.app_url().to_string()).await?; - let mut handle_counter = random_handle_start(); + let mut handle_counter = random_handle(); let mut next_handle = || { let out: u64 = handle_counter; handle_counter += 1; @@ -465,7 +465,7 @@ async fn test_fhe_if_then_else() -> Result<(), Box> { .await?; let mut client = FhevmCoprocessorClient::connect(app.app_url().to_string()).await?; - let mut handle_counter = random_handle_start(); + let mut handle_counter = random_handle(); let mut next_handle = || { let out: u64 = handle_counter; handle_counter += 1; diff --git a/fhevm-engine/coprocessor/src/tests/utils.rs b/fhevm-engine/coprocessor/src/tests/utils.rs index e1795248..f83b91af 100644 --- a/fhevm-engine/coprocessor/src/tests/utils.rs +++ b/fhevm-engine/coprocessor/src/tests/utils.rs @@ -1,6 +1,7 @@ use crate::cli::Args; use fhevm_engine_common::tfhe_ops::{current_ciphertext_version, deserialize_fhe_ciphertext}; use rand::RngCore; +use rand::Rng; use std::collections::BTreeMap; use std::sync::atomic::{AtomicU16, Ordering}; use testcontainers::{core::WaitFor, runners::AsyncRunner, GenericImage, ImageExt}; @@ -42,6 +43,10 @@ pub fn default_tenant_id() -> i32 { 1 } +pub fn random_handle() -> u64 { + rand::thread_rng().gen() +} + pub async fn setup_test_app() -> Result> { if std::env::var("COPROCESSOR_TEST_LOCALHOST").is_ok() { setup_test_app_existing_localhost().await @@ -63,7 +68,7 @@ pub async fn setup_test_app_existing_localhost() -> Result Result> { +async fn setup_test_app_existing_db() -> Result> { let app_port = get_app_port(); let (app_close_channel, rx) = tokio::sync::watch::channel(false); start_coprocessor(rx, app_port, &LOCAL_DB_URL).await; From 8425252901a452121d21583605cd88aaf9ca1902 Mon Sep 17 00:00:00 2001 From: David Kazlauskas Date: Fri, 13 Sep 2024 09:45:58 +0300 Subject: [PATCH 05/13] Post merge fixes --- fhevm-engine/coprocessor/src/tests/mod.rs | 10 ++-------- fhevm-engine/coprocessor/src/tests/operators.rs | 2 +- fhevm-engine/coprocessor/src/tests/utils.rs | 3 +-- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/fhevm-engine/coprocessor/src/tests/mod.rs b/fhevm-engine/coprocessor/src/tests/mod.rs index ef21a5e0..35e443b5 100644 --- a/fhevm-engine/coprocessor/src/tests/mod.rs +++ b/fhevm-engine/coprocessor/src/tests/mod.rs @@ -100,14 +100,8 @@ async fn test_smoke() -> Result<(), Box> { // decrypt values { - let mut decrypt_request = tonic::Request::new(DebugDecryptRequest { - handles: vec![h4.to_vec(), h3.to_vec()], - }); - decrypt_request.metadata_mut().append( - "authorization", - MetadataValue::from_str(&api_key_header).unwrap(), - ); - let resp = client.debug_decrypt_ciphertext(decrypt_request).await?; + let decrypt_request = vec![h4.to_vec(), h3.to_vec()]; + let resp = decrypt_ciphertexts(&pool, 1, decrypt_request).await?; println!("decrypt request: {:?}", resp); assert_eq!(resp.len(), 2); // first value diff --git a/fhevm-engine/coprocessor/src/tests/operators.rs b/fhevm-engine/coprocessor/src/tests/operators.rs index 7655334f..31d764c4 100644 --- a/fhevm-engine/coprocessor/src/tests/operators.rs +++ b/fhevm-engine/coprocessor/src/tests/operators.rs @@ -4,7 +4,7 @@ use crate::server::coprocessor::{ AsyncComputation, AsyncComputeRequest, TrivialEncryptBatch, TrivialEncryptRequestSingle, }; use crate::tests::utils::{ - decrypt_ciphertexts, random_handle, random_handle_start, wait_until_all_ciphertexts_computed, + decrypt_ciphertexts, random_handle, wait_until_all_ciphertexts_computed, }; use crate::{ server::coprocessor::{async_computation_input::Input, AsyncComputationInput}, diff --git a/fhevm-engine/coprocessor/src/tests/utils.rs b/fhevm-engine/coprocessor/src/tests/utils.rs index f83b91af..7fb94bbb 100644 --- a/fhevm-engine/coprocessor/src/tests/utils.rs +++ b/fhevm-engine/coprocessor/src/tests/utils.rs @@ -1,7 +1,6 @@ use crate::cli::Args; use fhevm_engine_common::tfhe_ops::{current_ciphertext_version, deserialize_fhe_ciphertext}; -use rand::RngCore; -use rand::Rng; +use rand::{Rng, RngCore}; use std::collections::BTreeMap; use std::sync::atomic::{AtomicU16, Ordering}; use testcontainers::{core::WaitFor, runners::AsyncRunner, GenericImage, ImageExt}; From 9835d9164216c7f7c446a9592893802544f4bbd4 Mon Sep 17 00:00:00 2001 From: David Kazlauskas Date: Fri, 13 Sep 2024 11:40:20 +0300 Subject: [PATCH 06/13] Harden trivial encryption --- fhevm-engine/coprocessor/src/server.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/fhevm-engine/coprocessor/src/server.rs b/fhevm-engine/coprocessor/src/server.rs index f0a65fb4..ff6d989b 100644 --- a/fhevm-engine/coprocessor/src/server.rs +++ b/fhevm-engine/coprocessor/src/server.rs @@ -1,4 +1,4 @@ -use std::collections::BTreeMap; +use std::collections::{BTreeMap, BTreeSet}; use std::num::NonZeroUsize; use std::str::FromStr; @@ -17,7 +17,7 @@ use coprocessor::{ }; use fhevm_engine_common::tfhe_ops::{ check_fhe_operand_types, current_ciphertext_version, trivial_encrypt_be_bytes, - try_expand_ciphertext_list, + try_expand_ciphertext_list, validate_fhe_type, }; use fhevm_engine_common::types::{FhevmError, SupportedFheCiphertexts, SupportedFheOperations}; use sha3::{Digest, Keccak256}; @@ -493,7 +493,6 @@ impl coprocessor::fhevm_coprocessor_server::FhevmCoprocessor for CoprocessorServ return Err(tonic::Status::unimplemented("not implemented")); } - // debug functions below, should be removed in production async fn trivial_encrypt_ciphertexts( &self, request: tonic::Request, @@ -501,7 +500,13 @@ impl coprocessor::fhevm_coprocessor_server::FhevmCoprocessor for CoprocessorServ let tenant_id = check_if_api_key_is_valid(&request, &self.pool).await?; let req = request.get_ref(); - // TODO: add check against duplicate input handles in the batch + let mut unique_handles: BTreeSet<&[u8]> = BTreeSet::new(); + for val in &req.values { + validate_fhe_type(val.output_type).map_err(|e| CoprocessorError::FhevmError(e))?; + if !unique_handles.insert(&val.handle) { + return Err(CoprocessorError::DuplicateOutputHandleInBatch(format!("0x{}", hex::encode(&val.handle))).into()); + } + } let mut public_key = sqlx::query!( " From 259b1c91156019f80c0296574644e3b16c4533be Mon Sep 17 00:00:00 2001 From: David Kazlauskas Date: Fri, 13 Sep 2024 17:16:33 +0300 Subject: [PATCH 07/13] Use TFHE randomness implementation --- fhevm-engine/Cargo.toml | 6 + fhevm-engine/coprocessor/src/tests/random.rs | 64 +++++---- fhevm-engine/coprocessor/src/tfhe_worker.rs | 7 +- fhevm-engine/coprocessor/src/types.rs | 19 +-- fhevm-engine/executor/src/server.rs | 2 +- .../fhevm-engine-common/src/tfhe_ops.rs | 135 +++++++++--------- 6 files changed, 116 insertions(+), 117 deletions(-) diff --git a/fhevm-engine/Cargo.toml b/fhevm-engine/Cargo.toml index 4ff0e950..bc03760d 100644 --- a/fhevm-engine/Cargo.toml +++ b/fhevm-engine/Cargo.toml @@ -13,3 +13,9 @@ anyhow = "1.0.86" [profile.dev.package.tfhe] overflow-checks = false + +# for testing in release mode due to too big +# binary inside mac +[profile.release] +opt-level = "z" +lto = true \ No newline at end of file diff --git a/fhevm-engine/coprocessor/src/tests/random.rs b/fhevm-engine/coprocessor/src/tests/random.rs index e6941ce4..eda019c2 100644 --- a/fhevm-engine/coprocessor/src/tests/random.rs +++ b/fhevm-engine/coprocessor/src/tests/random.rs @@ -43,8 +43,10 @@ async fn test_fhe_random_basic() -> Result<(), Box> { let mut repeated_output_handles = Vec::new(); let mut other_seed_output_handles = Vec::new(); + let random_test_types = supported_types(); + let deterministic_seed = 123u8; - for the_type in supported_types() { + for the_type in random_test_types { let output_handle = next_handle(); output_handles.push(output_handle.clone()); @@ -62,7 +64,7 @@ async fn test_fhe_random_basic() -> Result<(), Box> { }); } - for the_type in supported_types() { + for the_type in random_test_types { let output_handle = next_handle(); repeated_output_handles.push(output_handle.clone()); @@ -81,7 +83,7 @@ async fn test_fhe_random_basic() -> Result<(), Box> { } let deterministic_seed = 124u8; - for the_type in supported_types() { + for the_type in random_test_types { let output_handle = next_handle(); other_seed_output_handles.push(output_handle.clone()); @@ -115,19 +117,21 @@ async fn test_fhe_random_basic() -> Result<(), Box> { let resp = decrypt_ciphertexts(&pool, 1, decrypt_request).await?; let expected: Vec = vec![ DecryptionResult { value: "true".to_string(), output_type: 0 }, - DecryptionResult { value: "246".to_string(), output_type: 2 }, - DecryptionResult { value: "63017".to_string(), output_type: 3 }, - DecryptionResult { value: "4129931760".to_string(), output_type: 4 }, - DecryptionResult { value: "17737921846358948632".to_string(), output_type: 5 }, - DecryptionResult { value: "327206904699245123432435293866544047473".to_string(), output_type: 6 }, - DecryptionResult { value: "1405342954708646521029792852792956072442307244669".to_string(), output_type: 7 }, - DecryptionResult { value: "111342740003933073063880775186901842883137555493599211263718834336721313367065".to_string(), output_type: 8 }, - DecryptionResult { value: "12892608486462714192025621397040078423423955713740464502763239020120634468299230123105397307872954410250318097947282133574275938210017581341447804344613993".to_string(), output_type: 9 }, - DecryptionResult { value: "172861618302440003871985169870765936350085874287296918960492143478488761618246504771315475060233667593872334502225689924250171990509708174838253519566820157798395010064387130671854353305667120118078771871328847226561467056702239601377047878285846377269405811576578942463596956091674468702130036387127918822209".to_string(), output_type: 10 }, - DecryptionResult { value: "31075214450348645370562638259461457156660301565210267906013240737336053083827159054203712794280858545976020541340200687848819992364752918396584991833648247977557620662493453593968051971701814968601733859743322792627162168039060394006823897396302176759600031160814967191408374105854249751828206266805375926002417511561943897787495111082912113112803400667069378922271635394928755975139359534582265256642037856166688849734457088884995628268940935666204883226039421296557591795669918923722604457778199088364855244515207994602899710427947199154593190772832070461956037473623162197147067799409973348481724693194438433297577".to_string(), output_type: 11 } + DecryptionResult { value: "191".to_string(), output_type: 2 }, + DecryptionResult { value: "31935".to_string(), output_type: 3 }, + DecryptionResult { value: "50166975".to_string(), output_type: 4 }, + DecryptionResult { value: "1340532071352597695".to_string(), output_type: 5 }, + DecryptionResult { value: "124020002486967631373104553377985821887".to_string(), output_type: 6 }, + DecryptionResult { value: "1460743068773264656276930615123138630088589671615".to_string(), output_type: 7 }, + DecryptionResult { value: "9073722870604321437325343932192605854447491367692274273821339035224667684031".to_string(), output_type: 8 }, + DecryptionResult { value: "305206023014776799985230721993414106811955216557006729499329334660464767143319849635649940645070409986179511078348380864394886287375691494634900011449535".to_string(), output_type: 9 }, + DecryptionResult { value: "47107745454094956047032061683228950257813886136471742519491594819930746822037387709806363956594057633886089295195718013393612776278364590927545759803018868145037996301653648244673256559634442220254150128066779099210193723099178032989466601285160252652521543788771151245736124664154459945466742327296640515263".to_string(), output_type: 10 }, + DecryptionResult { value: "15993832479142254582179552051978985171942987041581078316307196452126031943098588175022265343465618290530692834276986750976786634669778707390871076641509515302233954805902327473616311936122148439292611965911683385306561778692974324402831129553488123365277966756540494010076336201015765082225027076061721793429534417162741021200774038978621182013568300885057697263422825507901057344782449000322153843538116752641484000884044992485272920464597070056681924136192427539146031607003264945583126338515417165811516149183380031917969225191063070817311680151162289712932918856949044454757316587442727824006976546517520450682047".to_string(), output_type: 11 } ]; - assert_eq!(expected, resp,); + println!("results: {:#?}", resp); + + assert_eq!(expected, resp); let resp_repeated = decrypt_ciphertexts(&pool, 1, repeated_output_handles).await?; assert_eq!( @@ -170,24 +174,24 @@ async fn test_fhe_random_bounded() -> Result<(), Box> { let deterministic_seed = 123u8; let bounds = [ - "0", - "200", - "60000", - "4000000000", - "10000000000000000000", - "300000000000000000000000000000000000000", - "100000000000000000000000000000000000000000000000", - "100000000000000000000000000000000000000000000000000000000000000000000000000000", + "8", + "128", + "16384", + "1073741824", + "4611686018427387904", + "85070591730234615865843651857942052864", + "365375409332725729550921208179070754913983135744", + "28948022309329048855892746252171976963317496166410141009864396001978282409984", ]; let results = [ - "false", - "46", - "3017", - "129931760", - "7737921846358948632", - "27206904699245123432435293866544047473", - "5342954708646521029792852792956072442307244669", - "11342740003933073063880775186901842883137555493599211263718834336721313367065", + "true", + "127", + "15551", + "50166975", + "1340532071352597695", + "38949410756733015507260901520043769023", + "364616840775087467624166990585926365346640264383", + "9073722870604321437325343932192605854447491367692274273821339035224667684031", ]; for (idx, the_type) in supported_types().iter().enumerate() { diff --git a/fhevm-engine/coprocessor/src/tfhe_worker.rs b/fhevm-engine/coprocessor/src/tfhe_worker.rs index 7ca97df2..5cc01efc 100644 --- a/fhevm-engine/coprocessor/src/tfhe_worker.rs +++ b/fhevm-engine/coprocessor/src/tfhe_worker.rs @@ -176,7 +176,7 @@ async fn tfhe_worker_cycle( } // set thread tenant key - let tenant_entropy = { + { let mut rk = tenant_key_cache.blocking_write(); let keys = rk .get(&w.tenant_id) @@ -185,8 +185,7 @@ async fn tfhe_worker_cycle( tfhe::set_server_key(keys.sks.clone()); TFHE_TENANT_ID.set(w.tenant_id); } - keys.tenant_entropy() - }; + } let mut deserialized_cts: Vec = Vec::with_capacity(work_ciphertexts.len()); @@ -221,7 +220,7 @@ async fn tfhe_worker_cycle( } let res = - perform_fhe_operation(w.fhe_operation, &deserialized_cts, &tenant_entropy) + perform_fhe_operation(w.fhe_operation, &deserialized_cts) .map_err(|e| { let err: Box = Box::new(e); (err, w.tenant_id, w.output_handle.clone()) diff --git a/fhevm-engine/coprocessor/src/types.rs b/fhevm-engine/coprocessor/src/types.rs index adec7402..0f483998 100644 --- a/fhevm-engine/coprocessor/src/types.rs +++ b/fhevm-engine/coprocessor/src/types.rs @@ -1,7 +1,4 @@ -use std::str::FromStr; - use fhevm_engine_common::types::FhevmError; -use sha3::{Digest, Keccak256}; #[derive(Debug)] pub enum CoprocessorError { @@ -174,18 +171,4 @@ pub struct TfheTenantKeys { // maybe we'll need this #[allow(dead_code)] pub pks: tfhe::CompactPublicKey, -} - -impl TfheTenantKeys { - /// Should be always deterministic random 32 bytes per tenant - pub fn tenant_entropy(&self) -> [u8; 32] { - let chain_id: u64 = self.chain_id as u64; - let mut parsed_address = - alloy::primitives::Address::from_str(&self.verifying_contract_address) - .expect("we should have checked earlier that address parses") - .to_vec(); - let chain_id_bytes = chain_id.to_be_bytes(); - parsed_address.extend_from_slice(&chain_id_bytes); - Keccak256::digest(&parsed_address).into() - } -} +} \ No newline at end of file diff --git a/fhevm-engine/executor/src/server.rs b/fhevm-engine/executor/src/server.rs index 90a7e61a..66b0c8af 100644 --- a/fhevm-engine/executor/src/server.rs +++ b/fhevm-engine/executor/src/server.rs @@ -257,7 +257,7 @@ impl FhevmExecutorService { // Do the computation on the inputs. match inputs { - Ok(inputs) => match perform_fhe_operation(comp.operation as i16, &inputs, &[0; 32]) { + Ok(inputs) => match perform_fhe_operation(comp.operation as i16, &inputs) { Ok(result) => { let compressed = result.clone().compress(); state.ciphertexts.insert( diff --git a/fhevm-engine/fhevm-engine-common/src/tfhe_ops.rs b/fhevm-engine/fhevm-engine-common/src/tfhe_ops.rs index 7687b4aa..c45bd1b1 100644 --- a/fhevm-engine/fhevm-engine-common/src/tfhe_ops.rs +++ b/fhevm-engine/fhevm-engine-common/src/tfhe_ops.rs @@ -1,12 +1,8 @@ use crate::types::{is_ebytes_type, FheOperationType, FhevmError, SupportedFheCiphertexts, SupportedFheOperations}; -use bigdecimal::num_bigint::BigInt; -use rand_chacha::rand_core::SeedableRng; -use rand::Rng; -use sha3::{Digest, Keccak256}; use tfhe::{ integer::{bigint::StaticUnsignedBigInt, U256}, prelude::{ CastInto, FheEq, FheMax, FheMin, FheOrd, FheTryTrivialEncrypt, IfThenElse, RotateLeft, RotateRight - }, FheBool, FheUint1024, FheUint128, FheUint16, FheUint160, FheUint2048, FheUint256, FheUint32, FheUint512, FheUint64, FheUint8 + }, FheBool, FheUint1024, FheUint128, FheUint16, FheUint160, FheUint2048, FheUint256, FheUint32, FheUint512, FheUint64, FheUint8, Seed }; pub fn deserialize_fhe_ciphertext( @@ -749,7 +745,6 @@ pub fn perform_fhe_operation( fhe_operation_int: i16, input_operands: &[SupportedFheCiphertexts], // for deterministc randomness functions - entropy: &[u8; 32], ) -> Result { let fhe_operation: SupportedFheOperations = fhe_operation_int.try_into()?; match fhe_operation { @@ -2764,34 +2759,17 @@ pub fn perform_fhe_operation( } }, SupportedFheOperations::FheRand => { - let mut randomness_array = entropy.to_vec(); - randomness_array.extend_from_slice(&[0; 32]); - assert_eq!(randomness_array.len(), 64); let SupportedFheCiphertexts::Scalar(rand_counter) = &input_operands[0] else { panic!("we should have checked we have only scalar operands here") }; let SupportedFheCiphertexts::Scalar(to_type) = &input_operands[1] else { panic!("we should have checked we have only scalar operands here") }; + let (rand_counter, _) = rand_counter.to_low_high_u128(); let (to_type, _) = to_type.to_low_high_u128(); - rand_counter.copy_to_be_byte_slice(&mut randomness_array[32..64]); - let digest = Keccak256::digest(&randomness_array); - let mut fixed_digest: [u8; 32] = [0; 32]; - fixed_digest.copy_from_slice(digest.as_slice()); - let mut generator = rand_chacha::ChaCha20Rng::from_seed(fixed_digest); - // 256 bytes biggest number we support - let bytes_size = type_bytes_size(to_type as i16); - let mut random_bytes: Vec = Vec::with_capacity(bytes_size); - for _ in 0..bytes_size { - random_bytes.push(generator.gen()); - } - - Ok(trivial_encrypt_be_bytes(to_type as i16, &random_bytes)) + Ok(generate_random_number(to_type as i16, rand_counter, None)) }, SupportedFheOperations::FheRandBounded => { - let mut randomness_array = entropy.to_vec(); - randomness_array.extend_from_slice(&[0; 32]); - assert_eq!(randomness_array.len(), 64); let SupportedFheCiphertexts::Scalar(rand_counter) = &input_operands[0] else { panic!("we should have checked we have only scalar operands here") }; @@ -2801,52 +2779,81 @@ pub fn perform_fhe_operation( let SupportedFheCiphertexts::Scalar(to_type) = &input_operands[2] else { panic!("we should have checked we have only scalar operands here") }; + let (rand_counter, _) = rand_counter.to_low_high_u128(); let (to_type, _) = to_type.to_low_high_u128(); - rand_counter.copy_to_be_byte_slice(&mut randomness_array[32..64]); - let digest = Keccak256::digest(&randomness_array); - let mut fixed_digest: [u8; 32] = [0; 32]; - fixed_digest.copy_from_slice(digest.as_slice()); - let mut generator = rand_chacha::ChaCha20Rng::from_seed(fixed_digest); - // 256 bytes biggest number we support - let bytes_size = type_bytes_size(to_type as i16); - let mut random_bytes: Vec = Vec::with_capacity(bytes_size); - for _ in 0..bytes_size { - random_bytes.push(generator.gen()); - } - - let mut upper_bound_bytes: [u8; 32] = [0; 32]; - upper_bound.copy_to_be_byte_slice(&mut upper_bound_bytes); - - // bound restriction - let big_int = BigInt::from_bytes_be(bigdecimal::num_bigint::Sign::Plus, &random_bytes); - let bound_big_int = BigInt::from_bytes_be(bigdecimal::num_bigint::Sign::Plus, &upper_bound_bytes); - - let final_big_int = - if bound_big_int > BigInt::ZERO { - big_int.clone() % bound_big_int.clone() - } else { - 0.into() - }; - let (_, final_bytes) = final_big_int.to_bytes_be(); - Ok(trivial_encrypt_be_bytes(to_type as i16, &final_bytes)) + Ok(generate_random_number(to_type as i16, rand_counter, Some(*upper_bound))) }, SupportedFheOperations::FheGetInputCiphertext => todo!("Implement FheGetInputCiphertext"), } } -pub fn type_bytes_size(the_type: i16) -> usize { +pub fn generate_random_number(the_type: i16, seed: u128, upper_bound: Option) -> SupportedFheCiphertexts { + let subtract_from = 255; match the_type { - 0 => 1, - 2 => 1, - 3 => 2, - 4 => 4, - 5 => 8, - 6 => 16, - 7 => 20, - 8 => 32, - 9 => 64, - 10 => 128, - 11 => 256, + 0 => { + let num = FheUint8::generate_oblivious_pseudo_random(Seed(seed), 1); + SupportedFheCiphertexts::FheBool(num.gt(0)) + }, + 2 => { + let bit_count = 8; + let random_bits = upper_bound.map(|i| subtract_from - i.leading_zeros()) + .unwrap_or(bit_count).min(bit_count) as u64; + SupportedFheCiphertexts::FheUint8(FheUint8::generate_oblivious_pseudo_random(Seed(seed), random_bits)) + }, + 3 => { + let bit_count = 16; + let random_bits = upper_bound.map(|i| subtract_from - i.leading_zeros()) + .unwrap_or(bit_count).min(bit_count) as u64; + SupportedFheCiphertexts::FheUint16(FheUint16::generate_oblivious_pseudo_random(Seed(seed), random_bits)) + }, + 4 => { + let bit_count = 32; + let random_bits = upper_bound.map(|i| subtract_from - i.leading_zeros()) + .unwrap_or(bit_count).min(bit_count) as u64; + SupportedFheCiphertexts::FheUint32(FheUint32::generate_oblivious_pseudo_random(Seed(seed), random_bits)) + }, + 5 => { + let bit_count = 64; + let random_bits = upper_bound.map(|i| subtract_from - i.leading_zeros()) + .unwrap_or(bit_count).min(bit_count) as u64; + SupportedFheCiphertexts::FheUint64(FheUint64::generate_oblivious_pseudo_random(Seed(seed), random_bits)) + }, + 6 => { + let bit_count = 128; + let random_bits = upper_bound.map(|i| subtract_from - i.leading_zeros()) + .unwrap_or(bit_count).min(bit_count) as u64; + SupportedFheCiphertexts::FheUint128(FheUint128::generate_oblivious_pseudo_random(Seed(seed), random_bits)) + }, + 7 => { + let bit_count = 160; + let random_bits = upper_bound.map(|i| subtract_from - i.leading_zeros()) + .unwrap_or(bit_count).min(bit_count) as u64; + SupportedFheCiphertexts::FheUint160(FheUint160::generate_oblivious_pseudo_random(Seed(seed), random_bits)) + }, + 8 => { + let bit_count = 256; + let random_bits = upper_bound.map(|i| subtract_from - i.leading_zeros()) + .unwrap_or(bit_count).min(bit_count) as u64; + SupportedFheCiphertexts::FheUint256(FheUint256::generate_oblivious_pseudo_random(Seed(seed), random_bits)) + }, + 9 => { + let bit_count = 512; + let random_bits = upper_bound.map(|i| subtract_from - i.leading_zeros()) + .unwrap_or(bit_count).min(bit_count) as u64; + SupportedFheCiphertexts::FheBytes64(FheUint512::generate_oblivious_pseudo_random(Seed(seed), random_bits)) + }, + 10 => { + let bit_count = 1024; + let random_bits = upper_bound.map(|i| subtract_from - i.leading_zeros()) + .unwrap_or(bit_count).min(bit_count) as u64; + SupportedFheCiphertexts::FheBytes128(FheUint1024::generate_oblivious_pseudo_random(Seed(seed), random_bits)) + }, + 11 => { + let bit_count = 2048; + let random_bits = upper_bound.map(|i| subtract_from - i.leading_zeros()) + .unwrap_or(bit_count).min(bit_count) as u64; + SupportedFheCiphertexts::FheBytes256(FheUint2048::generate_oblivious_pseudo_random(Seed(seed), random_bits)) + }, other => { panic!("unknown type to trim to: {other}") } From afe18f15e150108e766539cac17d600c87bb7316 Mon Sep 17 00:00:00 2001 From: David Kazlauskas Date: Mon, 16 Sep 2024 08:38:12 +0300 Subject: [PATCH 08/13] Move out test user setup to code instead of migration --- .../migrations/20240723111257_coprocessor.sql | 3 -- .../coprocessor/migrations/gen-keys.sh | 14 -------- fhevm-engine/coprocessor/src/tests/utils.rs | 32 ++++++++++++++++++- 3 files changed, 31 insertions(+), 18 deletions(-) delete mode 100644 fhevm-engine/coprocessor/migrations/20240723111257_coprocessor.sql delete mode 100755 fhevm-engine/coprocessor/migrations/gen-keys.sh diff --git a/fhevm-engine/coprocessor/migrations/20240723111257_coprocessor.sql b/fhevm-engine/coprocessor/migrations/20240723111257_coprocessor.sql deleted file mode 100644 index 4cdf4ced..00000000 --- a/fhevm-engine/coprocessor/migrations/20240723111257_coprocessor.sql +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:96078adc7be2469c034b922d58b058c96f35a1a3e2d5a346c01c736e8cead869 -size 530459562 diff --git a/fhevm-engine/coprocessor/migrations/gen-keys.sh b/fhevm-engine/coprocessor/migrations/gen-keys.sh deleted file mode 100755 index a7b86723..00000000 --- a/fhevm-engine/coprocessor/migrations/gen-keys.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh - -echo " -INSERT INTO tenants(tenant_api_key, tenant_id, chain_id, verifying_contract_address, pks_key, sks_key, cks_key) -VALUES ( - 'a1503fb6-d79b-4e9e-826d-44cf262f3e05', - 1, - 12345, - '0x6819e3aDc437fAf9D533490eD3a7552493fCE3B1', - decode('$(cat ../../fhevm-keys/pks | xxd -p | tr -d '\n')','hex'), - decode('$(cat ../../fhevm-keys/sks | xxd -p | tr -d '\n')','hex'), - decode('$(cat ../../fhevm-keys/cks | xxd -p | tr -d '\n')','hex') -) -" > 20240723111257_coprocessor.sql diff --git a/fhevm-engine/coprocessor/src/tests/utils.rs b/fhevm-engine/coprocessor/src/tests/utils.rs index 7fb94bbb..282147bb 100644 --- a/fhevm-engine/coprocessor/src/tests/utils.rs +++ b/fhevm-engine/coprocessor/src/tests/utils.rs @@ -149,7 +149,8 @@ async fn setup_test_app_custom_docker() -> Result Result<(), Box> { + let sks = tokio::fs::read("../fhevm-keys/sks").await.expect("can't read sks key"); + let pks = tokio::fs::read("../fhevm-keys/pks").await.expect("can't read pks key"); + let cks = tokio::fs::read("../fhevm-keys/cks").await.expect("can't read cks key"); + sqlx::query!( + " + INSERT INTO tenants(tenant_api_key, tenant_id, chain_id, verifying_contract_address, pks_key, sks_key, cks_key) + VALUES ( + 'a1503fb6-d79b-4e9e-826d-44cf262f3e05', + 1, + 12345, + '0x6819e3aDc437fAf9D533490eD3a7552493fCE3B1', + $1, + $2, + $3 + ) + ", + &pks, + &sks, + &cks, + ) + .execute(pool) + .await?; + + Ok(()) +} + pub async fn decrypt_ciphertexts( pool: &sqlx::PgPool, tenant_id: i32, From 9e5241fd53749539c285718fd7acd03570332c0c Mon Sep 17 00:00:00 2001 From: David Kazlauskas Date: Mon, 16 Sep 2024 08:58:06 +0300 Subject: [PATCH 09/13] sqlx updates --- ...3f6ae667cb0b284ec64ddfbd68da697573085.json | 22 +++++++++ ...3e5361d4454a700260d10b3c7eed6f5cd6653.json | 46 +++++++++++++++++++ ...22087f047078cdb7adb1320c5ec276dc99c77.json | 18 ++++++++ ...3064fd0ea38f3b1e908344002701ab6ce5f31.json | 46 +++++++++++++++++++ ...3f6ae667cb0b284ec64ddfbd68da697573085.json | 22 +++++++++ ...3e5361d4454a700260d10b3c7eed6f5cd6653.json | 46 +++++++++++++++++++ ...22087f047078cdb7adb1320c5ec276dc99c77.json | 18 ++++++++ ...3064fd0ea38f3b1e908344002701ab6ce5f31.json | 46 +++++++++++++++++++ 8 files changed, 264 insertions(+) create mode 100644 fhevm-engine/.sqlx/query-4b48296e5ecf0a6e6a50aefdaa83f6ae667cb0b284ec64ddfbd68da697573085.json create mode 100644 fhevm-engine/.sqlx/query-b3c087db688b8b51227d7ed02f53e5361d4454a700260d10b3c7eed6f5cd6653.json create mode 100644 fhevm-engine/.sqlx/query-db0e921f5b61687985de3df888422087f047078cdb7adb1320c5ec276dc99c77.json create mode 100644 fhevm-engine/.sqlx/query-f4c7f18af089735e942488b1c643064fd0ea38f3b1e908344002701ab6ce5f31.json create mode 100644 fhevm-engine/coprocessor/.sqlx/query-4b48296e5ecf0a6e6a50aefdaa83f6ae667cb0b284ec64ddfbd68da697573085.json create mode 100644 fhevm-engine/coprocessor/.sqlx/query-b3c087db688b8b51227d7ed02f53e5361d4454a700260d10b3c7eed6f5cd6653.json create mode 100644 fhevm-engine/coprocessor/.sqlx/query-db0e921f5b61687985de3df888422087f047078cdb7adb1320c5ec276dc99c77.json create mode 100644 fhevm-engine/coprocessor/.sqlx/query-f4c7f18af089735e942488b1c643064fd0ea38f3b1e908344002701ab6ce5f31.json diff --git a/fhevm-engine/.sqlx/query-4b48296e5ecf0a6e6a50aefdaa83f6ae667cb0b284ec64ddfbd68da697573085.json b/fhevm-engine/.sqlx/query-4b48296e5ecf0a6e6a50aefdaa83f6ae667cb0b284ec64ddfbd68da697573085.json new file mode 100644 index 00000000..d62bdaf0 --- /dev/null +++ b/fhevm-engine/.sqlx/query-4b48296e5ecf0a6e6a50aefdaa83f6ae667cb0b284ec64ddfbd68da697573085.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT sks_key\n FROM tenants\n WHERE tenant_id = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "sks_key", + "type_info": "Bytea" + } + ], + "parameters": { + "Left": [ + "Int4" + ] + }, + "nullable": [ + false + ] + }, + "hash": "4b48296e5ecf0a6e6a50aefdaa83f6ae667cb0b284ec64ddfbd68da697573085" +} diff --git a/fhevm-engine/.sqlx/query-b3c087db688b8b51227d7ed02f53e5361d4454a700260d10b3c7eed6f5cd6653.json b/fhevm-engine/.sqlx/query-b3c087db688b8b51227d7ed02f53e5361d4454a700260d10b3c7eed6f5cd6653.json new file mode 100644 index 00000000..46c65d99 --- /dev/null +++ b/fhevm-engine/.sqlx/query-b3c087db688b8b51227d7ed02f53e5361d4454a700260d10b3c7eed6f5cd6653.json @@ -0,0 +1,46 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT tenant_id, chain_id, verifying_contract_address, pks_key, sks_key\n FROM tenants\n WHERE tenant_id = ANY($1::INT[])\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "tenant_id", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "chain_id", + "type_info": "Int4" + }, + { + "ordinal": 2, + "name": "verifying_contract_address", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "pks_key", + "type_info": "Bytea" + }, + { + "ordinal": 4, + "name": "sks_key", + "type_info": "Bytea" + } + ], + "parameters": { + "Left": [ + "Int4Array" + ] + }, + "nullable": [ + false, + false, + false, + false, + false + ] + }, + "hash": "b3c087db688b8b51227d7ed02f53e5361d4454a700260d10b3c7eed6f5cd6653" +} diff --git a/fhevm-engine/.sqlx/query-db0e921f5b61687985de3df888422087f047078cdb7adb1320c5ec276dc99c77.json b/fhevm-engine/.sqlx/query-db0e921f5b61687985de3df888422087f047078cdb7adb1320c5ec276dc99c77.json new file mode 100644 index 00000000..71964737 --- /dev/null +++ b/fhevm-engine/.sqlx/query-db0e921f5b61687985de3df888422087f047078cdb7adb1320c5ec276dc99c77.json @@ -0,0 +1,18 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO ciphertexts(tenant_id, handle, ciphertext, ciphertext_version, ciphertext_type)\n VALUES ($1, $2, $3, $4, $5)\n ON CONFLICT (tenant_id, handle, ciphertext_version) DO NOTHING\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int4", + "Bytea", + "Bytea", + "Int2", + "Int2" + ] + }, + "nullable": [] + }, + "hash": "db0e921f5b61687985de3df888422087f047078cdb7adb1320c5ec276dc99c77" +} diff --git a/fhevm-engine/.sqlx/query-f4c7f18af089735e942488b1c643064fd0ea38f3b1e908344002701ab6ce5f31.json b/fhevm-engine/.sqlx/query-f4c7f18af089735e942488b1c643064fd0ea38f3b1e908344002701ab6ce5f31.json new file mode 100644 index 00000000..c550bd0c --- /dev/null +++ b/fhevm-engine/.sqlx/query-f4c7f18af089735e942488b1c643064fd0ea38f3b1e908344002701ab6ce5f31.json @@ -0,0 +1,46 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT tenant_id, output_handle, dependencies, fhe_operation, is_scalar\n FROM computations c\n WHERE is_completed = false\n AND is_error = false\n AND NOT EXISTS (\n SELECT 1\n FROM unnest(c.dependencies) WITH ORDINALITY AS elems(v, dep_index)\n WHERE (c.tenant_id, elems.v) NOT IN ( SELECT tenant_id, handle FROM ciphertexts )\n -- don't select scalar operands\n AND (\n NOT c.is_scalar\n OR c.is_scalar AND NOT elems.dep_index = 2\n )\n -- ignore fhe random operations, all inputs are scalars\n AND NOT c.fhe_operation = ANY(ARRAY[26, 27])\n )\n LIMIT $1\n FOR UPDATE SKIP LOCKED\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "tenant_id", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "output_handle", + "type_info": "Bytea" + }, + { + "ordinal": 2, + "name": "dependencies", + "type_info": "ByteaArray" + }, + { + "ordinal": 3, + "name": "fhe_operation", + "type_info": "Int2" + }, + { + "ordinal": 4, + "name": "is_scalar", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false, + false + ] + }, + "hash": "f4c7f18af089735e942488b1c643064fd0ea38f3b1e908344002701ab6ce5f31" +} diff --git a/fhevm-engine/coprocessor/.sqlx/query-4b48296e5ecf0a6e6a50aefdaa83f6ae667cb0b284ec64ddfbd68da697573085.json b/fhevm-engine/coprocessor/.sqlx/query-4b48296e5ecf0a6e6a50aefdaa83f6ae667cb0b284ec64ddfbd68da697573085.json new file mode 100644 index 00000000..d62bdaf0 --- /dev/null +++ b/fhevm-engine/coprocessor/.sqlx/query-4b48296e5ecf0a6e6a50aefdaa83f6ae667cb0b284ec64ddfbd68da697573085.json @@ -0,0 +1,22 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT sks_key\n FROM tenants\n WHERE tenant_id = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "sks_key", + "type_info": "Bytea" + } + ], + "parameters": { + "Left": [ + "Int4" + ] + }, + "nullable": [ + false + ] + }, + "hash": "4b48296e5ecf0a6e6a50aefdaa83f6ae667cb0b284ec64ddfbd68da697573085" +} diff --git a/fhevm-engine/coprocessor/.sqlx/query-b3c087db688b8b51227d7ed02f53e5361d4454a700260d10b3c7eed6f5cd6653.json b/fhevm-engine/coprocessor/.sqlx/query-b3c087db688b8b51227d7ed02f53e5361d4454a700260d10b3c7eed6f5cd6653.json new file mode 100644 index 00000000..46c65d99 --- /dev/null +++ b/fhevm-engine/coprocessor/.sqlx/query-b3c087db688b8b51227d7ed02f53e5361d4454a700260d10b3c7eed6f5cd6653.json @@ -0,0 +1,46 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT tenant_id, chain_id, verifying_contract_address, pks_key, sks_key\n FROM tenants\n WHERE tenant_id = ANY($1::INT[])\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "tenant_id", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "chain_id", + "type_info": "Int4" + }, + { + "ordinal": 2, + "name": "verifying_contract_address", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "pks_key", + "type_info": "Bytea" + }, + { + "ordinal": 4, + "name": "sks_key", + "type_info": "Bytea" + } + ], + "parameters": { + "Left": [ + "Int4Array" + ] + }, + "nullable": [ + false, + false, + false, + false, + false + ] + }, + "hash": "b3c087db688b8b51227d7ed02f53e5361d4454a700260d10b3c7eed6f5cd6653" +} diff --git a/fhevm-engine/coprocessor/.sqlx/query-db0e921f5b61687985de3df888422087f047078cdb7adb1320c5ec276dc99c77.json b/fhevm-engine/coprocessor/.sqlx/query-db0e921f5b61687985de3df888422087f047078cdb7adb1320c5ec276dc99c77.json new file mode 100644 index 00000000..71964737 --- /dev/null +++ b/fhevm-engine/coprocessor/.sqlx/query-db0e921f5b61687985de3df888422087f047078cdb7adb1320c5ec276dc99c77.json @@ -0,0 +1,18 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO ciphertexts(tenant_id, handle, ciphertext, ciphertext_version, ciphertext_type)\n VALUES ($1, $2, $3, $4, $5)\n ON CONFLICT (tenant_id, handle, ciphertext_version) DO NOTHING\n ", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int4", + "Bytea", + "Bytea", + "Int2", + "Int2" + ] + }, + "nullable": [] + }, + "hash": "db0e921f5b61687985de3df888422087f047078cdb7adb1320c5ec276dc99c77" +} diff --git a/fhevm-engine/coprocessor/.sqlx/query-f4c7f18af089735e942488b1c643064fd0ea38f3b1e908344002701ab6ce5f31.json b/fhevm-engine/coprocessor/.sqlx/query-f4c7f18af089735e942488b1c643064fd0ea38f3b1e908344002701ab6ce5f31.json new file mode 100644 index 00000000..c550bd0c --- /dev/null +++ b/fhevm-engine/coprocessor/.sqlx/query-f4c7f18af089735e942488b1c643064fd0ea38f3b1e908344002701ab6ce5f31.json @@ -0,0 +1,46 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT tenant_id, output_handle, dependencies, fhe_operation, is_scalar\n FROM computations c\n WHERE is_completed = false\n AND is_error = false\n AND NOT EXISTS (\n SELECT 1\n FROM unnest(c.dependencies) WITH ORDINALITY AS elems(v, dep_index)\n WHERE (c.tenant_id, elems.v) NOT IN ( SELECT tenant_id, handle FROM ciphertexts )\n -- don't select scalar operands\n AND (\n NOT c.is_scalar\n OR c.is_scalar AND NOT elems.dep_index = 2\n )\n -- ignore fhe random operations, all inputs are scalars\n AND NOT c.fhe_operation = ANY(ARRAY[26, 27])\n )\n LIMIT $1\n FOR UPDATE SKIP LOCKED\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "tenant_id", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "output_handle", + "type_info": "Bytea" + }, + { + "ordinal": 2, + "name": "dependencies", + "type_info": "ByteaArray" + }, + { + "ordinal": 3, + "name": "fhe_operation", + "type_info": "Int2" + }, + { + "ordinal": 4, + "name": "is_scalar", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false, + false + ] + }, + "hash": "f4c7f18af089735e942488b1c643064fd0ea38f3b1e908344002701ab6ce5f31" +} From 758d915de738ee8b9653b0856e78b023572e5cc9 Mon Sep 17 00:00:00 2001 From: David Kazlauskas Date: Mon, 16 Sep 2024 09:35:00 +0300 Subject: [PATCH 10/13] Run devnode --- fhevm-engine/coprocessor/docker-compose.yml | 10 ++++++++++ fhevm-engine/coprocessor/src/tests/mod.rs | 16 +++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/fhevm-engine/coprocessor/docker-compose.yml b/fhevm-engine/coprocessor/docker-compose.yml index 78ebb2b2..c80c9453 100644 --- a/fhevm-engine/coprocessor/docker-compose.yml +++ b/fhevm-engine/coprocessor/docker-compose.yml @@ -16,10 +16,20 @@ services: - DATABASE_URL=postgresql://postgres:postgres@db:5432/coprocessor ports: - '50051:50051' + volumes: + - ${PWD}/coprocessor.key:/usr/share/coprocessor.key command: - --run-bg-worker - --run-server - --server-addr=0.0.0.0:50051 + - --coprocessor-key=/usr/share/coprocessor.key + geth: + image: custom-devnet:v1 + environment: + - FHEVM_COPROCESSOR_API_KEY=a1503fb6-d79b-4e9e-826d-44cf262f3e05 + - FHEVM_COPROCESSOR_URL=coproc:50051 + ports: + - '8545:8545' volumes: db: driver: local diff --git a/fhevm-engine/coprocessor/src/tests/mod.rs b/fhevm-engine/coprocessor/src/tests/mod.rs index 35e443b5..f9baadbb 100644 --- a/fhevm-engine/coprocessor/src/tests/mod.rs +++ b/fhevm-engine/coprocessor/src/tests/mod.rs @@ -140,4 +140,18 @@ async fn test_custom_function() -> Result<(), Box> { println!("{:#?}", res); Ok(()) -} \ No newline at end of file +} + +#[tokio::test] +#[ignore] +/// setup test data with keys +async fn setup_test_user() -> Result<(), Box> { + let pool = sqlx::postgres::PgPoolOptions::new() + .max_connections(2) + .connect(std::env::var("DATABASE_URL").expect("expected to get db url")) + .await?; + + utils::setup_test_user(&pool).await?; + + Ok(()) +} From 05169488e34a36006f2ef11106a9954e72bdb4c9 Mon Sep 17 00:00:00 2001 From: David Kazlauskas Date: Mon, 16 Sep 2024 09:36:53 +0300 Subject: [PATCH 11/13] Docker compose setup --- fhevm-engine/coprocessor/Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fhevm-engine/coprocessor/Makefile b/fhevm-engine/coprocessor/Makefile index e9511433..c045204b 100644 --- a/fhevm-engine/coprocessor/Makefile +++ b/fhevm-engine/coprocessor/Makefile @@ -19,6 +19,11 @@ recreate_db: $(MAKE) cleanup $(MAKE) init_db +.PHONY: docker_compose_setup +docker_compose_setup: + $(MAKE) init_db + cargo test setup_test_user -- --nocapture --ignored + .PHONY: clean_run clean_run: $(MAKE) recreate_db From 630b90e0f1432af8fcb50ea4ee0d117569aab173 Mon Sep 17 00:00:00 2001 From: David Kazlauskas Date: Mon, 16 Sep 2024 09:38:09 +0300 Subject: [PATCH 12/13] Database url --- fhevm-engine/coprocessor/Makefile | 2 +- fhevm-engine/coprocessor/src/tests/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fhevm-engine/coprocessor/Makefile b/fhevm-engine/coprocessor/Makefile index c045204b..09d1ab1d 100644 --- a/fhevm-engine/coprocessor/Makefile +++ b/fhevm-engine/coprocessor/Makefile @@ -22,7 +22,7 @@ recreate_db: .PHONY: docker_compose_setup docker_compose_setup: $(MAKE) init_db - cargo test setup_test_user -- --nocapture --ignored + DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/coprocessor cargo test setup_test_user -- --nocapture --ignored .PHONY: clean_run clean_run: diff --git a/fhevm-engine/coprocessor/src/tests/mod.rs b/fhevm-engine/coprocessor/src/tests/mod.rs index f9baadbb..9feff16c 100644 --- a/fhevm-engine/coprocessor/src/tests/mod.rs +++ b/fhevm-engine/coprocessor/src/tests/mod.rs @@ -148,7 +148,7 @@ async fn test_custom_function() -> Result<(), Box> { async fn setup_test_user() -> Result<(), Box> { let pool = sqlx::postgres::PgPoolOptions::new() .max_connections(2) - .connect(std::env::var("DATABASE_URL").expect("expected to get db url")) + .connect(&std::env::var("DATABASE_URL").expect("expected to get db url")) .await?; utils::setup_test_user(&pool).await?; From 2014e413c326b7f387d5f635dd7605d733826170 Mon Sep 17 00:00:00 2001 From: David Kazlauskas Date: Mon, 16 Sep 2024 09:57:12 +0300 Subject: [PATCH 13/13] Random remove unused function --- fhevm-engine/coprocessor/src/tests/utils.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/fhevm-engine/coprocessor/src/tests/utils.rs b/fhevm-engine/coprocessor/src/tests/utils.rs index 282147bb..9ad7b671 100644 --- a/fhevm-engine/coprocessor/src/tests/utils.rs +++ b/fhevm-engine/coprocessor/src/tests/utils.rs @@ -296,8 +296,4 @@ pub async fn decrypt_ciphertexts( let values = values.into_iter().map(|i| i.1).collect::>(); Ok(values) -} - -pub fn random_handle_start() -> u64 { - rand::thread_rng().next_u64() -} +} \ No newline at end of file