Skip to content

Commit

Permalink
feat(sdk): add support program registration (#1842)
Browse files Browse the repository at this point in the history
  • Loading branch information
mattstam authored Dec 5, 2024
1 parent 013c24e commit 6c09a31
Show file tree
Hide file tree
Showing 3 changed files with 3,439 additions and 905 deletions.
144 changes: 93 additions & 51 deletions crates/sdk/src/network-v2/client.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
use std::time::Duration;
use std::result::Result::Ok as StdOk;
use std::str::FromStr;
use std::time::{Duration, SystemTime, UNIX_EPOCH};

use alloy_signer::SignerSync;
use alloy_signer_local::PrivateKeySigner;
use anyhow::{Context, Ok, Result};
use reqwest_middleware::ClientWithMiddleware as HttpClientWithMiddleware;
use serde::de::DeserializeOwned;
use serde::Serialize;
use serde::{de::DeserializeOwned, Serialize};
use tonic::{
transport::{channel::ClientTlsConfig, Channel},
Code,
};

use sp1_core_machine::io::SP1Stdin;
use sp1_prover::SP1VerifyingKey;
use std::str::FromStr;
use std::time::{SystemTime, UNIX_EPOCH};
use tokio::try_join;
use tonic::transport::channel::ClientTlsConfig;
use tonic::transport::Channel;
use sp1_prover::{HashableKey, SP1VerifyingKey};

use crate::network_v2::proto::artifact::{
artifact_store_client::ArtifactStoreClient, CreateArtifactRequest,
};
use crate::network_v2::proto::network::{
prover_network_client::ProverNetworkClient, GetFilteredProofRequestsRequest,
GetFilteredProofRequestsResponse, GetNonceRequest, GetProofRequestStatusRequest,
GetProofRequestStatusResponse, ProofMode, ProofStatus, ProofStrategy, RequestProofRequest,
RequestProofRequestBody, RequestProofResponse,
prover_network_client::ProverNetworkClient, CreateProgramRequest, CreateProgramRequestBody,
CreateProgramResponse, FulfillmentStatus, FulfillmentStrategy, GetNonceRequest,
GetProgramRequest, GetProgramResponse, GetProofRequestStatusRequest,
GetProofRequestStatusResponse, ProofMode, RequestProofRequest, RequestProofRequestBody,
RequestProofResponse,
};
use crate::network_v2::Signable;

Expand Down Expand Up @@ -95,6 +97,76 @@ impl NetworkClient {
Ok(res.into_inner().nonce)
}

/// Get the verifying key hash from a verifying key. The verifying key hash is used to identify
/// a program.
pub fn get_vk_hash(vk: &SP1VerifyingKey) -> Result<Vec<u8>> {
let vk_hash_str = vk.bytes32();
let vk_hash = hex::decode(vk_hash_str.strip_prefix("0x").unwrap_or(&vk_hash_str))?;
Ok(vk_hash)
}

/// Registers a program if it is not already registered.
pub async fn register_program(&self, vk: &SP1VerifyingKey, elf: &[u8]) -> Result<Vec<u8>> {
let vk_hash = Self::get_vk_hash(vk)?;

// Try to get the existing program.
match self.get_program(&vk_hash).await? {
Some(_) => {
// The program already exists.
Ok(vk_hash)
}
None => {
// The program doesn't exist, create it.
self.create_program(&vk_hash, vk, elf).await?;
log::info!("Registered program 0x{}", hex::encode(vk_hash.clone()));
Ok(vk_hash)
}
}
}

/// Attempts to get program info, returns None if program doesn't exist.
async fn get_program(&self, vk_hash: &[u8]) -> Result<Option<GetProgramResponse>> {
let mut rpc = self.get_rpc().await?;
match rpc.get_program(GetProgramRequest { vk_hash: vk_hash.to_vec() }).await {
StdOk(response) => Ok(Some(response.into_inner())),
Err(status) if status.code() == Code::NotFound => Ok(None),
Err(e) => Err(e.into()),
}
}

/// Creates a new program.
async fn create_program(
&self,
vk_hash: &[u8],
vk: &SP1VerifyingKey,
elf: &[u8],
) -> Result<CreateProgramResponse> {
// Create the program artifact.
let mut store = self.get_store().await?;
let program_uri = self.create_artifact_with_content(&mut store, &elf).await?;

// Serialize the verifying key.
let vk_encoded = bincode::serialize(&vk)?;

// Send the request.
let mut rpc = self.get_rpc().await?;
let nonce = self.get_nonce().await?;
let request_body = CreateProgramRequestBody {
nonce,
vk_hash: vk_hash.to_vec(),
vk: vk_encoded,
program_uri,
};

Ok(rpc
.create_program(CreateProgramRequest {
signature: request_body.sign(&self.signer).into(),
body: Some(request_body),
})
.await?
.into_inner())
}

/// Get the status of a given proof. If the status is Fulfilled, the proof is also returned.
pub async fn get_proof_request_status<P: DeserializeOwned>(
&self,
Expand All @@ -108,9 +180,9 @@ impl NetworkClient {
.await?
.into_inner();

let status = ProofStatus::try_from(res.proof_status)?;
let status = FulfillmentStatus::try_from(res.fulfillment_status)?;
let proof = match status {
ProofStatus::Fulfilled => {
FulfillmentStatus::Fulfilled => {
let proof_uri = res
.proof_uri
.as_ref()
Expand All @@ -124,38 +196,15 @@ impl NetworkClient {
Ok((res, proof))
}

/// Get all the proof requests for a given status. Also filter by version if provided.
pub async fn get_filtered_proof_requests(
&self,
version: Option<String>,
proof_status: Option<i32>,
execution_status: Option<i32>,
limit: Option<u32>,
) -> Result<GetFilteredProofRequestsResponse> {
let mut rpc = self.get_rpc().await?;
let res = rpc
.get_filtered_proof_requests(GetFilteredProofRequestsRequest {
version,
proof_status,
execution_status,
limit,
})
.await?
.into_inner();

Ok(res)
}

/// Creates a proof request with the given ELF and stdin.
/// Creates a proof request with the given verifying key hash and stdin.
#[allow(clippy::too_many_arguments)]
pub async fn request_proof(
&self,
elf: &[u8],
vk_hash: &[u8],
stdin: &SP1Stdin,
vk: &SP1VerifyingKey,
mode: ProofMode,
version: &str,
strategy: ProofStrategy,
strategy: FulfillmentStrategy,
timeout_secs: u64,
cycle_limit: u64,
) -> Result<RequestProofResponse> {
Expand All @@ -164,26 +213,19 @@ impl NetworkClient {
let since_the_epoch = start.duration_since(UNIX_EPOCH).expect("Invalid start time");
let deadline = since_the_epoch.as_secs() + timeout_secs;

// Create the program and stdin artifacts.
// Create the stdin artifact.
let mut store = self.get_store().await?;
let mut store_clone = store.clone();
let program_promise = self.create_artifact_with_content(&mut store, &elf);
let stdin_promise = self.create_artifact_with_content(&mut store_clone, &stdin);
let (program_uri, stdin_uri) = try_join!(program_promise, stdin_promise)?;

// Serialize the vkey.
let vkey = bincode::serialize(&vk)?;
let stdin_uri = self.create_artifact_with_content(&mut store, &stdin).await?;

// Send the request.
let mut rpc = self.get_rpc().await?;
let nonce = self.get_nonce().await?;
let request_body = RequestProofRequestBody {
nonce,
version: format!("sp1-{}", version),
vkey,
vk_hash: vk_hash.to_vec(),
mode: mode.into(),
strategy: strategy.into(),
program_uri,
stdin_uri,
deadline,
cycle_limit,
Expand Down
Loading

0 comments on commit 6c09a31

Please sign in to comment.