Skip to content

Commit

Permalink
feat: input proofs DB table and communication
Browse files Browse the repository at this point in the history
Define the communication over the DB between the gw-listener,
the zkproof-worker and the transaction-sender services.

Add more verify proof tests in transaction-sender.

Fix compiler warnings in coprocessor due to deprecated rand functions.
  • Loading branch information
dartdart26 committed Feb 19, 2025
1 parent b826fbf commit d50c8e5
Show file tree
Hide file tree
Showing 15 changed files with 331 additions and 57 deletions.
8 changes: 8 additions & 0 deletions fhevm-engine/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

77 changes: 54 additions & 23 deletions fhevm-engine/coprocessor/src/bin/cli.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
use std::str::FromStr;

use clap::Parser;
use coprocessor::server::{common::FheOperation, coprocessor::{fhevm_coprocessor_client::FhevmCoprocessorClient, AsyncComputation, AsyncComputationInput, AsyncComputeRequest, GetCiphertextBatch, TrivialEncryptBatch, TrivialEncryptRequestSingle}};
use coprocessor::server::{
common::FheOperation,
coprocessor::{
fhevm_coprocessor_client::FhevmCoprocessorClient, AsyncComputation, AsyncComputationInput,
AsyncComputeRequest, GetCiphertextBatch, TrivialEncryptBatch, TrivialEncryptRequestSingle,
},
};
use rand::Rng;
use sqlx::types::Uuid;
use tonic::metadata::MetadataValue;
Expand Down Expand Up @@ -47,12 +53,31 @@ pub enum Args {
fn main() {
let args = Args::parse();
match args {
Args::InsertTenant { pks_file, sks_file, public_params_file, tenant_api_key, acl_contract_address, verifying_contract_address, chain_id } => {
insert_tenant(pks_file, sks_file, public_params_file, tenant_api_key, acl_contract_address, verifying_contract_address, chain_id);
},
Args::SmokeTest { tenant_api_key, coprocessor_url } => {
Args::InsertTenant {
pks_file,
sks_file,
public_params_file,
tenant_api_key,
acl_contract_address,
verifying_contract_address,
chain_id,
} => {
insert_tenant(
pks_file,
sks_file,
public_params_file,
tenant_api_key,
acl_contract_address,
verifying_contract_address,
chain_id,
);
}
Args::SmokeTest {
tenant_api_key,
coprocessor_url,
} => {
smoke_test(tenant_api_key, coprocessor_url);
},
}
}
}

Expand All @@ -66,11 +91,11 @@ fn smoke_test(tenant_api_key: String, coprocessor_url: String) {
.await.expect("Can't connect to coprocessor server");

let api_key_header = format!("bearer {}", tenant_api_key);
let handle_a = rand::thread_rng().gen::<u64>().to_be_bytes().to_vec();
let handle_b = rand::thread_rng().gen::<u64>().to_be_bytes().to_vec();
let output_handle = rand::thread_rng().gen::<u64>().to_be_bytes().to_vec();
let num_a = rand::thread_rng().gen::<u32>().to_be_bytes().to_vec();
let num_b = rand::thread_rng().gen::<u32>().to_be_bytes().to_vec();
let handle_a = rand::rng().random::<u64>().to_be_bytes().to_vec();
let handle_b = rand::rng().random::<u64>().to_be_bytes().to_vec();
let output_handle = rand::rng().random::<u64>().to_be_bytes().to_vec();
let num_a = rand::rng().random::<u32>().to_be_bytes().to_vec();
let num_b = rand::rng().random::<u32>().to_be_bytes().to_vec();

println!(
"Trivially encrypting numbers 0x{} and 0x{} with handles 0x{} and 0x{}",
Expand Down Expand Up @@ -171,15 +196,21 @@ fn smoke_test(tenant_api_key: String, coprocessor_url: String) {
});
}

fn insert_tenant(pks_file: String, sks_file: String, public_params_file: String, tenant_api_key: String, acl_contract_address: String, verifying_contract_address: String, chain_id: u32) {
let db_url = std::env::var("DATABASE_URL")
.expect("DATABASE_URL environment variable is undefined");
let pks_file = std::fs::read(&pks_file)
.expect("Can't read pks file");
let sks_file = std::fs::read(&sks_file)
.expect("Can't read pks file");
let public_params_file = std::fs::read(&public_params_file)
.expect("Can't read public params file");
fn insert_tenant(
pks_file: String,
sks_file: String,
public_params_file: String,
tenant_api_key: String,
acl_contract_address: String,
verifying_contract_address: String,
chain_id: u32,
) {
let db_url =
std::env::var("DATABASE_URL").expect("DATABASE_URL environment variable is undefined");
let pks_file = std::fs::read(&pks_file).expect("Can't read pks file");
let sks_file = std::fs::read(&sks_file).expect("Can't read pks file");
let public_params_file =
std::fs::read(&public_params_file).expect("Can't read public params file");
let _ = alloy::primitives::Address::from_str(&acl_contract_address)
.expect("Can't parse acl contract adddress");
let _ = alloy::primitives::Address::from_str(&verifying_contract_address)
Expand All @@ -194,8 +225,8 @@ fn insert_tenant(pks_file: String, sks_file: String, public_params_file: String,
let pool = sqlx::postgres::PgPoolOptions::new()
.max_connections(1)
.connect(&db_url)
.await.expect("Can't connect to postgres instance");

.await
.expect("Can't connect to postgres instance");

sqlx::query!(
"
Expand Down Expand Up @@ -230,4 +261,4 @@ fn insert_tenant(pks_file: String, sks_file: String, public_params_file: String,
.await
.expect("Can't insert new tenant");
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@ CREATE TABLE IF NOT EXISTS verify_proofs (
contract_address TEXT NOT NULL,
user_address TEXT NOT NULL,
input BYTEA,
handles BYTEA NOT NULL,
handles BYTEA,
retry_count INTEGER NOT NULL DEFAULT 0,
verified BOOLEAN NOT NULL DEFAULT false
verified BOOLEAN NOT NULL DEFAULT false,
last_error TEXT,
verified_at TIMESTAMPTZ,
last_retry_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_verify_proofs_verified_retry ON verify_proofs(verified, retry_count, zk_proof_id);
2 changes: 2 additions & 0 deletions fhevm-engine/gw-listener/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
artifacts
cache
12 changes: 12 additions & 0 deletions fhevm-engine/gw-listener/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,16 @@ license.workspace = true
[dependencies]
# workspace dependencies
alloy = { workspace = true }
anyhow = { workspace = true }
clap = { workspace = true }
sqlx = { workspace = true }
tokio = { workspace = true }
tokio-util = { workspace = true }
tracing = { workspace = true }
tracing-subscriber = { workspace = true }

[build-dependencies]
foundry-compilers = { version = "0.13.0", features = ["svm-solc"] }

[dev-dependencies]
serial_test = "3.2.0"
23 changes: 23 additions & 0 deletions fhevm-engine/gw-listener/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Gateway Listener

The **gw-listener** service listens for input proof verification events from the ZKPoKManager contract and inserts them into the DB into the `verify_proofs` table: [verify_proofs table](../fhevm-db/migrations/20250207092623_verify_proofs.sql)

The following fields are insertion in by gw-listner:

* zk_proof_id
* chain_id
* contract_address
* user_address
* input (the ciphertext + proof as a tfhe-rs serialized data structure)
* created_at

At the time of insertion, the `verified` and `verified_at` fields are false and NULL, respectively.

The gw-listener will notify **zkproof-worker** services that work is available over the `verify_proof_requests` DB channel (configurable, but this is the default one).

Once a ZK proof request is verified, a zkproof-worker should set:
* `verified = true`
* `verified_at = NOW()`
* `handles = concatenated 32-byte handles` (s.t. the length of the handles field in bytes is a multiple of 32)

Then, zkproof-worker should notify the **transaction-sender** on the **verify_proof_responses** DB channel (configurable, but this is the default one).
16 changes: 16 additions & 0 deletions fhevm-engine/gw-listener/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use std::path::Path;

use foundry_compilers::{Project, ProjectPathsConfig};

fn main() {
let paths = ProjectPathsConfig::hardhat(Path::new(env!("CARGO_MANIFEST_DIR"))).unwrap();
let project = Project::builder()
.paths(paths)
.build(Default::default())
.unwrap();

let output = project.compile().unwrap();
assert!(!output.has_compiler_errors());

project.rerun_if_sources_changed();
}
31 changes: 31 additions & 0 deletions fhevm-engine/gw-listener/contracts/ZKPoKManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: BSD-3-Clause-Clear
pragma solidity ^0.8.28;

contract ZKPoKManager {
event VerifyProofRequest(
uint256 indexed zkProofId,
uint256 indexed contractChainId,
address contractAddress,
address userAddress,
bytes ciphertextWithZKProof
);

uint256 zkProofIdCounter = 0;

function verifyProofRequest(
uint256 contractChainId,
address contractAddress,
address userAddress,
bytes calldata ciphertextWithZKProof
) public {
uint256 zkProofId = zkProofIdCounter;
zkProofIdCounter += 1;
emit VerifyProofRequest(
zkProofId,
contractChainId,
contractAddress,
userAddress,
ciphertextWithZKProof
);
}
}
4 changes: 2 additions & 2 deletions fhevm-engine/gw-listener/src/bin/gw_listener.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use alloy::primitives::Address;
use alloy::{primitives::Address, transports::http::reqwest::Url};
use clap::Parser;

#[derive(Parser, Debug, Clone)]
Expand All @@ -11,7 +11,7 @@ struct Conf {
database_pool_size: u32,

#[arg(long)]
gw_url: String,
gw_url: Url,

#[arg(short, long)]
zkpok_manager_address: Address,
Expand Down
13 changes: 0 additions & 13 deletions fhevm-engine/gw-listener/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1 @@
pub fn add(left: u64, right: u64) -> u64 {
left + right
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
2 changes: 0 additions & 2 deletions fhevm-engine/transaction-sender/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ tracing-subscriber = { workspace = true }
async-trait = "0.1.86"

[build-dependencies]
# crates.io dependencies
foundry-compilers = { version = "0.13.0", features = ["svm-solc"] }

[dev-dependencies]
# crates.io dependencies
serial_test = "3.2.0"
4 changes: 4 additions & 0 deletions fhevm-engine/transaction-sender/src/bin/transaction_sender.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ struct Conf {
#[arg(long, default_value = "15")]
verify_proof_resp_max_retries: u32,

#[arg(long, default_value = "true")]
verify_proof_remove_after_max_retries: bool,

#[arg(long, default_value = "1")]
error_sleep_initial_secs: u16,

Expand Down Expand Up @@ -96,6 +99,7 @@ async fn main() -> anyhow::Result<()> {

verify_proof_resp_batch_limit: conf.verify_proof_resp_batch_limit,
verify_proof_resp_max_retries: conf.verify_proof_resp_max_retries,
verify_proof_remove_after_max_retries: conf.verify_proof_remove_after_max_retries,

db_polling_interval_secs: conf.database_polling_interval_secs,

Expand Down
2 changes: 2 additions & 0 deletions fhevm-engine/transaction-sender/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub struct ConfigSettings {

pub verify_proof_resp_batch_limit: u32,
pub verify_proof_resp_max_retries: u32,
pub verify_proof_remove_after_max_retries: bool,

pub db_polling_interval_secs: u16,

Expand All @@ -27,6 +28,7 @@ impl Default for ConfigSettings {
add_ciphertexts_db_channel: "add_ciphertexts".to_owned(),
verify_proof_resp_batch_limit: 128,
verify_proof_resp_max_retries: 15,
verify_proof_remove_after_max_retries: true,
db_polling_interval_secs: 5,
error_sleep_initial_secs: 1,
error_sleep_max_secs: 16,
Expand Down
Loading

0 comments on commit d50c8e5

Please sign in to comment.