diff --git a/client/README.md b/client/README.md index 444871ed..8a10e316 100644 --- a/client/README.md +++ b/client/README.md @@ -36,10 +36,9 @@ Open a new terminal to start interacting with the client through the CLI. Let's cargo build --release ``` -Once the project is built, we need to compile and deploy the contracts that the protocol is going to use to submit and verify the scores: +Once the project is built, we need to deploy the contracts that the protocol is going to use to submit and verify the scores: ```bash -./target/release/eigen-trust-client compile ./target/release/eigen-trust-client deploy ``` @@ -73,7 +72,7 @@ The command-line interface was built using [clap.rs](http://clap.rs/). There is - `--action (add | remove)`: Defines the action to perform. You can choose to `add` a new member to a group or `remove` an existing member from it. - `--ic`: Provides the identity commitment of the participant you intend to add or remove from the group. - `--addr`: Specifies the participant's Ethereum address. -- `compile`: Compiles all the `.sol` and `.yul` contracts available in the `assets` folder. For `.sol` contracts, it generates an ABI JSON file and a Rust binding file. For `.yul` smart contracts, it compiles Yul code into binary. +- `compile`: Compiles the AttestationStation and stores the generated bindings. - `deploy`: Deploys all the contracts. - `proof`: Calculates the global scores, generates the zk proof and stores it in `et-proof.json` at the `assets` folder. - `scores`: Calculates the global scores and stores them in the `scores.csv` file within the `assets` folder. diff --git a/client/src/error.rs b/client/src/error.rs index 4f707ea4..a038d3d1 100644 --- a/client/src/error.rs +++ b/client/src/error.rs @@ -30,6 +30,8 @@ pub enum EigenError { TransactionError, /// Contract compilation error ContractCompilationError, + /// Path error + PathError, /// Unknown error. Unknown, } @@ -48,6 +50,7 @@ impl From for u8 { EigenError::ParseError => 8, EigenError::TransactionError => 9, EigenError::ContractCompilationError => 10, + EigenError::PathError => 11, EigenError::Unknown => 255, } } @@ -67,6 +70,7 @@ impl From for EigenError { 8 => EigenError::ParseError, 9 => EigenError::TransactionError, 10 => EigenError::ContractCompilationError, + 11 => EigenError::PathError, _ => EigenError::Unknown, } } diff --git a/client/src/eth.rs b/client/src/eth.rs index 3ddb0d93..a073e232 100644 --- a/client/src/eth.rs +++ b/client/src/eth.rs @@ -18,17 +18,18 @@ use ethers::{ prelude::{k256::ecdsa::SigningKey, Abigen, ContractFactory}, providers::Middleware, signers::coins_bip39::{English, Mnemonic}, - solc::{artifacts::ContractBytecode, Artifact, Solc}, + solc::{Artifact, CompilerOutput, Solc}, types::TransactionRequest, utils::keccak256, }; -use log::info; use secp256k1::SecretKey; -use std::{fs::write, sync::Arc}; +use std::sync::Arc; + +/// Compiles the AttestationStation contract. +pub fn compile_as() -> Result { + let path = + get_assets_path().map_err(|_| EigenError::ParseError)?.join("AttestationStation.sol"); -/// Deploys the AttestationStation contract. -pub async fn deploy_as(signer: Arc) -> Result { - let path = get_assets_path().unwrap().join("AttestationStation.sol"); let compiler_output = Solc::default().compile_source(&path).map_err(|_| EigenError::ContractCompilationError)?; @@ -36,55 +37,17 @@ pub async fn deploy_as(signer: Arc) -> Result return Err(EigenError::ContractCompilationError); } - let mut address: Option
= None; - for (_, contract) in compiler_output.contracts_iter() { - let (abi, bytecode, _) = contract.clone().into_parts(); - let abi = abi.ok_or(EigenError::ParseError)?; - let bytecode = bytecode.ok_or(EigenError::ParseError)?; - - let factory = ContractFactory::new(abi, bytecode, signer.clone()); - - match factory.deploy(()).unwrap().send().await { - Ok(contract) => { - address = Some(contract.address()); - break; - }, - Err(_) => continue, - } - } - - address.ok_or(EigenError::ParseError) + Ok(compiler_output) } -/// Calls the EtVerifier contract. -pub async fn call_verifier( - signer: Arc, verifier_address: Address, proof: NativeProof, -) { - let calldata = encode_calldata::(&[proof.pub_ins], &proof.proof); - - let tx = TransactionRequest::default().data(calldata).to(verifier_address); - let res = signer.send_transaction(tx, None).await.unwrap().await.unwrap(); - info!("{:#?}", res); -} +/// Generates the bindings for the AttestationStation contract and save them into a file. +pub fn gen_as_bindings() -> Result<(), EigenError> { + let contracts = compile_as()?; -/// Compiles the AttestationStation contract. -pub fn compile_att_station() -> Result<(), EigenError> { - let path = - get_assets_path().map_err(|_| EigenError::ParseError)?.join("AttestationStation.sol"); - - // compile it - let contracts = - Solc::default().compile_source(&path).map_err(|_| EigenError::ContractCompilationError)?; - - if !contracts.errors.is_empty() { - return Err(EigenError::ContractCompilationError); - } - - for (name, contr) in contracts.contracts_iter() { - let contract: ContractBytecode = contr.clone().into(); - let abi = contract.clone().abi.ok_or(EigenError::ParseError)?; + for (name, contract) in contracts.contracts_iter() { + let abi = contract.clone().abi.ok_or_else(|| EigenError::ParseError)?; let abi_json = serde_json::to_string(&abi).map_err(|_| EigenError::ParseError)?; - let contract_json = serde_json::to_string(&contract).map_err(|_| EigenError::ParseError)?; + let bindings = Abigen::new(name, abi_json) .map_err(|_| EigenError::ParseError)? .generate() @@ -93,24 +56,40 @@ pub fn compile_att_station() -> Result<(), EigenError> { bindings .write_to_file(get_file_path("attestation_station", FileType::Rs).unwrap()) .map_err(|_| EigenError::ParseError)?; - write( - get_file_path("attestation_station", FileType::Json).unwrap(), - contract_json, - ) - .map_err(|_| EigenError::ParseError)?; } + Ok(()) } +/// Deploys the AttestationStation contract. +pub async fn deploy_as(signer: Arc) -> Result { + let contracts = compile_as()?; + let mut address: Option
= None; + + for (_, contract) in contracts.contracts_iter() { + let (abi, bytecode, _) = contract.clone().into_parts(); + let abi = abi.ok_or_else(|| EigenError::ParseError)?; + let bytecode = bytecode.ok_or_else(|| EigenError::ParseError)?; + + let factory = ContractFactory::new(abi, bytecode, signer.clone()); + + match factory.deploy(()).unwrap().send().await { + Ok(contract) => { + address = Some(contract.address()); + break; + }, + Err(_) => continue, + } + } + + address.ok_or_else(|| EigenError::ParseError) +} + /// Deploys the EtVerifier contract. pub async fn deploy_verifier(signer: Arc) -> Result { - // Compile the et_verifier.yul contract - let path = get_assets_path().map_err(|_| EigenError::ParseError)?.join("et_verifier.yul"); - let path_str = path.to_str().ok_or(EigenError::ParseError)?; - let compiled_contract = - compile_yul(&read_yul(path_str).map_err(|_| EigenError::ContractCompilationError)?); + let yul_result = read_yul("et_verifier").map_err(|_| EigenError::ContractCompilationError)?; + let compiled_contract = compile_yul(&yul_result); - // Deploy the contract let tx = TransactionRequest::default().data(compiled_contract); let pen_tx = signer.send_transaction(tx, None).await.map_err(|_| EigenError::TransactionError)?; @@ -121,6 +100,23 @@ pub async fn deploy_verifier(signer: Arc) -> Result, verifier_address: Address, proof: NativeProof, +) -> Result<(), EigenError> { + let calldata = encode_calldata::(&[proof.pub_ins], &proof.proof); + let tx = TransactionRequest::default().data(calldata).to(verifier_address); + + // Send transaction + let tx_result = + signer.send_transaction(tx, None).await.map_err(|_| EigenError::TransactionError)?; + + // Await for transaction confirmation + tx_result.await.map_err(|_| EigenError::TransactionError)?; + + Ok(()) +} + /// Returns a vector of ECDSA private keys derived from the given mnemonic phrase. pub fn ecdsa_secret_from_mnemonic( mnemonic: &str, count: u32, @@ -256,7 +252,7 @@ mod tests { // Read proof data and call verifier let proof_raw: ProofRaw = read_json("et_proof").unwrap(); let proof = Proof::from(proof_raw); - call_verifier(client.get_signer(), addr, proof).await; + call_verifier(client.get_signer(), addr, proof).await.unwrap(); drop(anvil); } diff --git a/client/src/fs.rs b/client/src/fs.rs index f2f650db..0e2086ba 100644 --- a/client/src/fs.rs +++ b/client/src/fs.rs @@ -6,7 +6,7 @@ use serde::de::DeserializeOwned; use serde_json::from_reader; use std::{ env::current_dir, - fs::{read, read_to_string, write, File}, + fs::{read_to_string, File}, io::{BufReader, Result}, path::PathBuf, }; @@ -50,18 +50,6 @@ pub fn get_file_path(file_name: &str, file_type: FileType) -> Result { Ok(assets_path.join(format!("{}.{}", file_name, file_type.as_str()))) } -/// Reads a binary file from the `assets` directory and returns its contents as bytes. -pub fn read_binary(file_name: &str) -> Result> { - let bin_path = get_file_path(file_name, FileType::Bin)?; - read(bin_path) -} - -/// Writes bytes to a binary file in the `assets` directory. -pub fn write_binary(bytes: Vec, file_name: &str) -> Result<()> { - let bin_path = get_file_path(file_name, FileType::Bin)?; - write(bin_path, bytes) -} - /// Reads a JSON file from the `assets` directory and returns its deserialized contents. pub fn read_json(file_name: &str) -> Result { let json_path = get_file_path(file_name, FileType::Json)?; @@ -70,7 +58,7 @@ pub fn read_json(file_name: &str) -> Result { from_reader(reader).map_err(Into::into) } -/// Reads a file from the `assets` directory and returns its contents as a string. +/// Reads a `.yul` file from the `assets` directory and returns its contents as a string. pub fn read_yul(file_name: &str) -> Result { let yul_path = get_file_path(file_name, FileType::Yul)?; read_to_string(yul_path) @@ -88,38 +76,6 @@ mod tests { field: String, } - #[test] - fn test_read_binary() { - let file_name = "test_read_binary"; - - // Write test file - let mut file = File::create(get_file_path(file_name, FileType::Bin).unwrap()).unwrap(); - file.write_all(b"binary data").unwrap(); - - // Test reading - let data = read_binary(file_name).unwrap(); - assert_eq!(data, b"binary data"); - - // Cleanup - fs::remove_file(get_file_path(file_name, FileType::Bin).unwrap()).unwrap(); - } - - #[test] - fn test_write_binary() { - let file_name = "test_write_binary"; - let binary_data: Vec = vec![0xff, 0x61, 0x4a, 0x6d, 0x59, 0x56, 0x2a, 0x42, 0x37, 0x72]; - - // Write binary data - write_binary(binary_data.clone(), file_name).unwrap(); - - // Test if the file was written correctly - let data = fs::read(get_file_path(file_name, FileType::Bin).unwrap()).unwrap(); - assert_eq!(data, binary_data); - - // Cleanup - fs::remove_file(get_file_path(file_name, FileType::Bin).unwrap()).unwrap(); - } - #[test] fn test_read_json() { let file_name = "test_read_json"; diff --git a/client/src/main.rs b/client/src/main.rs index 604e5cbe..119fdad9 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -5,7 +5,7 @@ use clap::Parser; use cli::*; use dotenv::dotenv; use eigen_trust_client::{ - eth::{compile_att_station, deploy_as, deploy_verifier}, + eth::{deploy_as, deploy_verifier, gen_as_bindings}, fs::read_json, Client, ClientConfig, }; @@ -53,10 +53,10 @@ async fn main() { Err(e) => error!("Failed to execute bandada command: {:?}", e), }, Mode::Compile => { - info!("Compiling contracts..."); - match compile_att_station() { - Ok(_) => info!("AttestationStation Compilation successful"), - Err(e) => error!("Error during AttestationStation compilation: {}", e), + info!("Compiling AttestationStation..."); + match gen_as_bindings() { + Ok(_) => info!("Compilation successful"), + Err(e) => error!("Error during compilation: {}", e), } info!("Done!"); }, diff --git a/client/src/storage.rs b/client/src/storage.rs index fa63f081..2c03d543 100644 --- a/client/src/storage.rs +++ b/client/src/storage.rs @@ -256,10 +256,10 @@ impl AttestationRecord { #[cfg(test)] mod tests { + use crate::fs::get_assets_path; use crate::storage::*; use serde::{Deserialize, Serialize}; use std::fs; - use std::path::PathBuf; // Define the test struct #[derive(Debug, Deserialize, PartialEq, Clone, Serialize)] @@ -272,7 +272,7 @@ mod tests { fn test_csv_file_storage() { // Create the CSV file let filename = "test.csv"; - let filepath = PathBuf::from("../data").join(filename); + let filepath = get_assets_path().unwrap().join(filename); let mut csv_storage = CSVFileStorage::::new(filepath.clone()); let content = vec![Record {