From a2b63c6f589de83d22dab3fe37b12caa0221eae6 Mon Sep 17 00:00:00 2001 From: "Abdel @ StarkWare" Date: Fri, 26 Jul 2024 18:41:02 +0200 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=9A=A7=20nip-90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/core/src/dvm/customer.rs | 64 ++++++++++++-- crates/core/src/dvm/mod.rs | 4 +- crates/core/src/dvm/service_provider.rs | 109 +++++++++++++----------- crates/core/src/lib.rs | 1 + crates/core/src/nostr_utils.rs | 29 +++++++ 5 files changed, 150 insertions(+), 57 deletions(-) create mode 100644 crates/core/src/nostr_utils.rs diff --git a/crates/core/src/dvm/customer.rs b/crates/core/src/dvm/customer.rs index 7692cea..dc400af 100644 --- a/crates/core/src/dvm/customer.rs +++ b/crates/core/src/dvm/customer.rs @@ -6,6 +6,7 @@ use thiserror::Error; use tokio::time::timeout; use crate::config::Settings; +use crate::dvm::constants::*; use crate::dvm::types::{GenerateZKPJobRequest, GenerateZKPJobResult}; use crate::verifier_service::VerifierService; @@ -16,6 +17,8 @@ use crate::verifier_service::VerifierService; pub struct Customer { /// Application settings settings: Settings, + /// User keys + user_keys: Keys, /// Nostr client for network communication nostr_client: Client, /// Service for verifying proofs @@ -50,6 +53,7 @@ impl Customer { Ok(Self { settings, + user_keys, nostr_client: client, verifier_service: Default::default(), }) @@ -70,12 +74,25 @@ impl Customer { /// Submits a job request to the Nostr network pub async fn submit_job(&self, job: GenerateZKPJobRequest) -> Result<(), CustomerError> { - let request_json = serde_json::to_string(&job)?; debug!("Publishing proving request..."); - let event_id = self - .nostr_client - .publish_text_note(request_json, []) - .await?; + + let tags = vec![ + Tag::parse(&[ + "param", + "log_size", + job.request.log_size.to_string().as_str(), + ]) + .unwrap(), + Tag::parse(&["param", "claim", job.request.claim.to_string().as_str()]).unwrap(), + Tag::parse(&["output", "text/json"]).unwrap(), + ]; + let event: Event = EventBuilder::job_request(Kind::Custom(JOB_REQUEST_KIND), tags) + .unwrap() + .to_event(&self.user_keys) + .unwrap(); + + let event_id = self.nostr_client.send_event(event).await?; + info!("Proving request published [{}]", event_id.to_string()); Ok(()) } @@ -184,3 +201,40 @@ impl Customer { .map_err(|e| CustomerError::VerificationError(e.to_string())) } } +#[cfg(test)] +mod tests { + + use nostr_sdk::prelude::*; + + use crate::nostr_utils::extract_params_from_tags; + + #[test] + fn test_submit_job() { + let tags = vec![ + Tag::parse(&["param", "log_size", "5"]).unwrap(), + Tag::parse(&["param", "claim", "443693538"]).unwrap(), + Tag::parse(&["output", "text/json"]).unwrap(), + ]; + let params = extract_params_from_tags(&tags); + + assert_eq!(params.get("log_size"), Some(&"5".to_string())); + assert_eq!(params.get("claim"), Some(&"443693538".to_string())); + assert_eq!(params.get("output"), Some(&"text/json".to_string())); + + // Convert and check numeric parameters + let log_size = params + .get("log_size") + .and_then(|s| s.parse::().ok()) + .unwrap(); + let claim = params + .get("claim") + .and_then(|s| s.parse::().ok()) + .unwrap(); + + assert_eq!(log_size, 5); + assert_eq!(claim, 443693538); + + // Print extracted parameters for debugging + println!("Extracted parameters: {:?}", params); + } +} diff --git a/crates/core/src/dvm/mod.rs b/crates/core/src/dvm/mod.rs index 97280f7..f67b068 100644 --- a/crates/core/src/dvm/mod.rs +++ b/crates/core/src/dvm/mod.rs @@ -7,8 +7,8 @@ pub mod constants { pub const DVM_DESCRIPTION: &str = "Censorship-resistant global proving network."; pub const SERVICE_NAME: &str = "generate-zk-proof"; pub const VERSION: &str = "0.1.0"; - pub const JOB_REQUEST_KIND: u64 = 5600; - pub const JOB_RESULT_KIND: u64 = 6600; + pub const JOB_REQUEST_KIND: u16 = 5600; + pub const JOB_RESULT_KIND: u16 = 6600; } pub mod types { diff --git a/crates/core/src/dvm/service_provider.rs b/crates/core/src/dvm/service_provider.rs index 03dc444..a42613e 100644 --- a/crates/core/src/dvm/service_provider.rs +++ b/crates/core/src/dvm/service_provider.rs @@ -6,7 +6,9 @@ use thiserror::Error; use crate::config::Settings; use crate::db::{Database, RequestStatus}; -use crate::dvm::types::{GenerateZKPJobRequest, GenerateZKPJobResult}; +use crate::dvm::constants::JOB_REQUEST_KIND; +use crate::dvm::types::{FibonnacciProvingRequest, GenerateZKPJobResult}; +use crate::nostr_utils::extract_params_from_tags; use crate::prover_service::ProverService; /// ServiceProvider is the main component of the Askeladd prover agent. @@ -90,7 +92,9 @@ impl ServiceProvider { /// This method subscribes to Nostr events and handles incoming proving requests pub async fn run(&self) -> Result<(), ServiceProviderError> { let proving_req_sub_id = SubscriptionId::new(&self.settings.proving_req_sub_id); - let filter = Filter::new().kind(Kind::TextNote).since(Timestamp::now()); + let filter = Filter::new() + .kind(Kind::Custom(JOB_REQUEST_KIND)) + .since(Timestamp::now()); // Subscribe to Nostr events self.nostr_client @@ -133,59 +137,64 @@ impl ServiceProvider { /// Handles a single proving request event async fn handle_event(&self, event: Box) -> Result<(), ServiceProviderError> { - debug!("Event received [{}]", event.id); - if let Ok(job_request) = serde_json::from_str::(&event.content) { - info!("Proving request received [{}]", event.id); - let request = job_request.request; - - if let Some(status) = self.db.get_request_status(&job_request.job_id)? { - match status { - RequestStatus::Completed => { - info!("Request {} already processed, skipping", job_request.job_id); - return Ok(()); - } - RequestStatus::Failed => { - info!("Request {} failed before, retrying", job_request.job_id); - } - RequestStatus::Pending => { - info!( - "Request {} is already pending, skipping", - job_request.job_id - ); - return Ok(()); - } + info!("Proving request received [{}]", event.id); + + let job_id = event.id.to_string(); + let tags = &event.tags; + let params = extract_params_from_tags(tags); + let log_size = params + .get("log_size") + .and_then(|s| s.parse::().ok()) + .unwrap(); + let claim = params + .get("claim") + .and_then(|s| s.parse::().ok()) + .unwrap(); + + let request = FibonnacciProvingRequest { log_size, claim }; + + if let Some(status) = self.db.get_request_status(&job_id)? { + match status { + RequestStatus::Completed => { + info!("Request {} already processed, skipping", &job_id); + return Ok(()); } - } else { - self.db.insert_request(&job_request.job_id, &request)?; - } - - match self.proving_service.generate_proof(request) { - Ok(response) => { - let job_result = GenerateZKPJobResult { - job_id: job_request.job_id.clone(), - response, - }; - let response_json = serde_json::to_string(&job_result)?; - let event_id = self - .nostr_client - .publish_text_note(response_json, vec![]) - .await?; - info!("Proving response published [{}]", event_id.to_string()); - - self.db.update_request( - &job_request.job_id, - Some(&job_result.response), - RequestStatus::Completed, - )?; + RequestStatus::Failed => { + info!("Request {} failed before, retrying", &job_id); } - Err(e) => { - error!("Proof generation failed: {}", e); - self.db - .update_request(&job_request.job_id, None, RequestStatus::Failed)?; + RequestStatus::Pending => { + info!("Request {} is already pending, skipping", &job_id); + return Ok(()); } } } else { - debug!("Received non-request event, ignoring"); + self.db.insert_request(&job_id, &request)?; + } + + match self.proving_service.generate_proof(request) { + Ok(response) => { + let job_result = GenerateZKPJobResult { + job_id: job_id.clone(), + response, + }; + let response_json = serde_json::to_string(&job_result)?; + let event_id = self + .nostr_client + .publish_text_note(response_json, vec![]) + .await?; + info!("Proving response published [{}]", event_id.to_string()); + + self.db.update_request( + &job_id, + Some(&job_result.response), + RequestStatus::Completed, + )?; + } + Err(e) => { + error!("Proof generation failed: {}", e); + self.db + .update_request(&job_id, None, RequestStatus::Failed)?; + } } Ok(()) diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index c11aedd..f93da0d 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -1,5 +1,6 @@ pub mod config; pub mod db; pub mod dvm; +pub mod nostr_utils; pub mod prover_service; pub mod verifier_service; diff --git a/crates/core/src/nostr_utils.rs b/crates/core/src/nostr_utils.rs new file mode 100644 index 0000000..8ec9f8b --- /dev/null +++ b/crates/core/src/nostr_utils.rs @@ -0,0 +1,29 @@ +use std::collections::HashMap; + +use nostr_sdk::prelude::*; + +/// Extracts parameters and output format from a vector of Tags. +/// +/// # Arguments +/// +/// * `tags` - A vector of Tag structs +/// +/// # Returns +/// +/// A HashMap where: +/// - Keys are parameter names or "output" for the output format +/// - Values are the corresponding parameter values or output format +pub fn extract_params_from_tags(tags: &[Tag]) -> HashMap { + let mut params = HashMap::new(); + + for tag in tags { + let tag_vec = tag.as_vec(); + if tag_vec.len() >= 3 && tag_vec[0] == "param" { + params.insert(tag_vec[1].to_string(), tag_vec[2].to_string()); + } else if tag_vec.len() >= 2 && tag_vec[0] == "output" { + params.insert("output".to_string(), tag_vec[1].to_string()); + } + } + + params +} From e113a16a83a0b5314409d64b3aad29f946037d78 Mon Sep 17 00:00:00 2001 From: "Abdel @ StarkWare" Date: Fri, 26 Jul 2024 19:02:55 +0200 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=9A=A7=20nip-90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/core/src/dvm/service_provider.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/crates/core/src/dvm/service_provider.rs b/crates/core/src/dvm/service_provider.rs index a42613e..87e7544 100644 --- a/crates/core/src/dvm/service_provider.rs +++ b/crates/core/src/dvm/service_provider.rs @@ -20,6 +20,8 @@ use crate::prover_service::ProverService; pub struct ServiceProvider { /// Application settings settings: Settings, + /// Prover Agent Nostr keys + prover_agent_keys: Keys, /// Service for generating proofs proving_service: ProverService, /// Nostr client for communication @@ -67,6 +69,7 @@ impl ServiceProvider { Ok(Self { settings, + prover_agent_keys, proving_service: Default::default(), nostr_client: client, db, @@ -177,11 +180,15 @@ impl ServiceProvider { job_id: job_id.clone(), response, }; - let response_json = serde_json::to_string(&job_result)?; - let event_id = self - .nostr_client - .publish_text_note(response_json, vec![]) - .await?; + let _response_json = serde_json::to_string(&job_result)?; + + let job_result_event: Event = EventBuilder::job_result(*event, 0, None) + .unwrap() + .to_event(&self.prover_agent_keys) + .unwrap(); + + println!("Job result event: {:?}", job_result_event); + let event_id = self.nostr_client.send_event(job_result_event).await?; info!("Proving response published [{}]", event_id.to_string()); self.db.update_request( From b81bbbd989dc1483e22224cbe2489d0c1ddb4664 Mon Sep 17 00:00:00 2001 From: "Abdel @ StarkWare" Date: Mon, 29 Jul 2024 13:25:13 +0200 Subject: [PATCH 3/3] =?UTF-8?q?=E2=9C=A8=20nip-90=20compliant?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/cli/src/user_cli.rs | 16 ++++++---------- crates/core/Cargo.toml | 4 ++-- crates/core/src/dvm/customer.rs | 20 +++++++++----------- crates/core/src/dvm/mod.rs | 11 +---------- crates/core/src/dvm/service_provider.rs | 12 ++++++------ 5 files changed, 24 insertions(+), 39 deletions(-) diff --git a/crates/cli/src/user_cli.rs b/crates/cli/src/user_cli.rs index a18aa6a..88dc5ce 100644 --- a/crates/cli/src/user_cli.rs +++ b/crates/cli/src/user_cli.rs @@ -24,10 +24,7 @@ async fn main() -> Result<(), Box> { // ****************************************************** // ****************** PREPARE JOB *********************** // ****************************************************** - let job_id = GenerateZKPJobRequest::new_job_id(); - let job_request = GenerateZKPJobRequest { - job_id, request: FibonnacciProvingRequest { log_size: 5, claim: 443693538, @@ -37,21 +34,20 @@ async fn main() -> Result<(), Box> { // ****************************************************** // ****************** SUBMIT JOB ************************ // ****************************************************** - info!("Submitting job with id: {}", job_request.job_id); - customer.submit_job(job_request.clone()).await?; + info!("Submitting job"); + let job_id = customer.submit_job(job_request.clone()).await?; + info!("Job submitted with id: {}", job_id); // ****************************************************** // ****************** WAIT FOR JOB RESULT *************** // ****************************************************** - info!("Waiting for job result with id: {}", job_request.job_id); - let job_result = customer - .wait_for_job_result(&job_request.job_id, 60) - .await?; + info!("Waiting for job result with id: {}", job_id); + let job_result = customer.wait_for_job_result(&job_id, 60).await?; // ****************************************************** // ****************** VERIFY PROOF ********************** // ****************************************************** - info!("Verifying proof with id: {}", job_request.job_id); + info!("Verifying proof with id: {}", job_id); let is_valid = customer.verify_proof(&job_result)?; info!("Proof is valid: {}", is_valid); diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 16d69db..6dc1181 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -11,7 +11,7 @@ thiserror = "1.0.62" config = "0.14.0" dotenv = "0.15" rusqlite = { version = "0.32.0", features = ["bundled"] } -nostr-sdk = "0.33.0" +#nostr-sdk = "0.33.0" +nostr-sdk = { git = "https://github.com/AbdelStark/nostr", rev = "95b5f63" } log = "0.4.22" tokio = { version = "1", default-features = false } -uuid = { version = "1.3", features = ["v4"] } diff --git a/crates/core/src/dvm/customer.rs b/crates/core/src/dvm/customer.rs index dc400af..714948f 100644 --- a/crates/core/src/dvm/customer.rs +++ b/crates/core/src/dvm/customer.rs @@ -73,7 +73,7 @@ impl Customer { } /// Submits a job request to the Nostr network - pub async fn submit_job(&self, job: GenerateZKPJobRequest) -> Result<(), CustomerError> { + pub async fn submit_job(&self, job: GenerateZKPJobRequest) -> Result { debug!("Publishing proving request..."); let tags = vec![ @@ -94,7 +94,7 @@ impl Customer { let event_id = self.nostr_client.send_event(event).await?; info!("Proving request published [{}]", event_id.to_string()); - Ok(()) + Ok(event_id.to_string()) } /// Waits for a job result from the Nostr network @@ -109,7 +109,7 @@ impl Customer { // Set up a filter for the job result events let filter = Filter::new() - .kind(Kind::TextNote) + .kind(Kind::Custom(JOB_RESULT_KIND)) .author(prover_agent_public_key) .since(Timestamp::now() - Duration::from_secs(60)); @@ -155,7 +155,6 @@ impl Customer { serde_json::from_str::(&event.content) { if result.job_id == job_id { - info!("Job result found for job_id: {}", job_id); return Ok(true); } } @@ -167,16 +166,15 @@ impl Customer { .await .map_err(CustomerError::NostrClientError)?; + let filter = Filter::new() + .kind(Kind::Custom(JOB_RESULT_KIND)) + .author(PublicKey::from_bech32(&self.settings.prover_agent_pk).unwrap()) + .since(Timestamp::now() - Duration::from_secs(60)); + // Fetch recent events to find the job result let events = self .nostr_client - .get_events_of( - vec![Filter::new() - .kind(Kind::TextNote) - .author(PublicKey::from_bech32(&self.settings.prover_agent_pk).unwrap()) - .since(Timestamp::now() - Duration::from_secs(60))], - None, - ) + .get_events_of(vec![filter], None) .await .map_err(CustomerError::NostrClientError)?; diff --git a/crates/core/src/dvm/mod.rs b/crates/core/src/dvm/mod.rs index f67b068..beaa500 100644 --- a/crates/core/src/dvm/mod.rs +++ b/crates/core/src/dvm/mod.rs @@ -14,24 +14,15 @@ pub mod constants { pub mod types { use serde::{Deserialize, Serialize}; use stwo_prover::core::prover::StarkProof; - use uuid::Uuid; #[derive(Debug, Serialize, Deserialize, Clone)] pub struct GenerateZKPJobRequest { - pub job_id: String, pub request: FibonnacciProvingRequest, } impl GenerateZKPJobRequest { pub fn new(request: FibonnacciProvingRequest) -> Self { - Self { - job_id: Self::new_job_id(), - request, - } - } - - pub fn new_job_id() -> String { - Uuid::new_v4().to_string() + Self { request } } } diff --git a/crates/core/src/dvm/service_provider.rs b/crates/core/src/dvm/service_provider.rs index 87e7544..9d8f94e 100644 --- a/crates/core/src/dvm/service_provider.rs +++ b/crates/core/src/dvm/service_provider.rs @@ -180,14 +180,14 @@ impl ServiceProvider { job_id: job_id.clone(), response, }; - let _response_json = serde_json::to_string(&job_result)?; + let response_json = serde_json::to_string(&job_result)?; - let job_result_event: Event = EventBuilder::job_result(*event, 0, None) - .unwrap() - .to_event(&self.prover_agent_keys) - .unwrap(); + let job_result_event: Event = + EventBuilder::job_result(*event, Some(response_json), 0, None) + .unwrap() + .to_event(&self.prover_agent_keys) + .unwrap(); - println!("Job result event: {:?}", job_result_event); let event_id = self.nostr_client.send_event(job_result_event).await?; info!("Proving response published [{}]", event_id.to_string());