Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/nip 90 #4

Merged
merged 3 commits into from
Jul 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 6 additions & 10 deletions crates/cli/src/user_cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// ******************************************************
// ****************** PREPARE JOB ***********************
// ******************************************************
let job_id = GenerateZKPJobRequest::new_job_id();

let job_request = GenerateZKPJobRequest {
job_id,
request: FibonnacciProvingRequest {
log_size: 5,
claim: 443693538,
Expand All @@ -37,21 +34,20 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// ******************************************************
// ****************** 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);

Expand Down
4 changes: 2 additions & 2 deletions crates/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
84 changes: 68 additions & 16 deletions crates/core/src/dvm/customer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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
Expand Down Expand Up @@ -50,6 +53,7 @@ impl Customer {

Ok(Self {
settings,
user_keys,
nostr_client: client,
verifier_service: Default::default(),
})
Expand All @@ -69,15 +73,28 @@ 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)?;
pub async fn submit_job(&self, job: GenerateZKPJobRequest) -> Result<String, CustomerError> {
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(())
Ok(event_id.to_string())
}

/// Waits for a job result from the Nostr network
Expand All @@ -92,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));

Expand Down Expand Up @@ -138,7 +155,6 @@ impl Customer {
serde_json::from_str::<GenerateZKPJobResult>(&event.content)
{
if result.job_id == job_id {
info!("Job result found for job_id: {}", job_id);
return Ok(true);
}
}
Expand All @@ -150,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)?;

Expand All @@ -184,3 +199,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::<u32>().ok())
.unwrap();
let claim = params
.get("claim")
.and_then(|s| s.parse::<u32>().ok())
.unwrap();

assert_eq!(log_size, 5);
assert_eq!(claim, 443693538);

// Print extracted parameters for debugging
println!("Extracted parameters: {:?}", params);
}
}
15 changes: 3 additions & 12 deletions crates/core/src/dvm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,22 @@ 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 {
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 }
}
}

Expand Down
116 changes: 66 additions & 50 deletions crates/core/src/dvm/service_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -18,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
Expand Down Expand Up @@ -65,6 +69,7 @@ impl ServiceProvider {

Ok(Self {
settings,
prover_agent_keys,
proving_service: Default::default(),
nostr_client: client,
db,
Expand All @@ -90,7 +95,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
Expand Down Expand Up @@ -133,59 +140,68 @@ impl ServiceProvider {

/// Handles a single proving request event
async fn handle_event(&self, event: Box<Event>) -> Result<(), ServiceProviderError> {
debug!("Event received [{}]", event.id);
if let Ok(job_request) = serde_json::from_str::<GenerateZKPJobRequest>(&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::<u32>().ok())
.unwrap();
let claim = params
.get("claim")
.and_then(|s| s.parse::<u32>().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 job_result_event: Event =
EventBuilder::job_result(*event, Some(response_json), 0, None)
.unwrap()
.to_event(&self.prover_agent_keys)
.unwrap();

let event_id = self.nostr_client.send_event(job_result_event).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(())
Expand Down
1 change: 1 addition & 0 deletions crates/core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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;
Loading