Playtesting the zkSVM

Interacting with the Prover

Please reach out to request an API key and start sending requests to the server.

The high-level flow is: 1) start the session, 2) send requests containing SVM transactions, 3) end the session to trigger proof generation, and 4) poll the endpoint to retrieve summary statistics.

Start Session

Request

Kick off a session and initialize the genesis accounts that should exist before transaction processing. The request will be rejected if it doesn't contain a valid access key.

POST /session
{
  "api_key": "3ca8a552-f347-4692-a391-f2affa1f01c3",
  "genesis_accounts": {
    "AVxHjnUapzK8C3hQuiyCz7R22bag2CWGNor9v6YZQuJh": {
      "lamports": 1000000000000,
      "data": [],
      "owner": "11111111111111111111111111111111",
      "executable": false,
      "rentEpoch": 124
    },
    "C7zRFydqurVQArmveAKnB11j6WHkFERyH4FYMzSQDRMh": {
      "lamports": 1000000000000,
      "data": [],
      "owner": "11111111111111111111111111111111",
      "executable": false,
      "rentEpoch": 124
    }
  }
}

Response

{  
  "session_id": "f8f694eb-377f-48f6-a14b-3f4c56ad3edc"
}

Send Transaction

Request

Send SVM transactions that the prover should include in the ZK proof. Each element should be serialized via bincode.

POST /session/<session_id>/transactions
{  
    "transaction": [...]
}

Response

The server will return how many transactions are remaining for the current session. There's a max cap of 50 due to memory constraints of the underlying zkVM.

{
  "remaining_transactions_in_session": 25
}

End Session

Request

End the session to trigger proof generation and select the SP1 proof type. Supported types are core, compressed, and groth16. The first is recommended for testing and the last for on-chain verification.

PUT /session/<session_id>
{
    "proof_type": "groth16"
}

Response

Since proving is async, the response only contains a successful acknowledgement of the request and not the proof details.

{}

Poll Session

Request

GET /session/<session_id>

Response

{
  "session": {
    "session_id": "f8f694eb-377f-48f6-a14b-3f4c56ad3edc",
    "api_key": "3ca8a552-f347-4692-a391-f2affa1f01c3",
    "proof": {
      "proving_time": 123,
      "cycle_count": 456,
      ...
    },
    ...
  },
  "transactions": [...]
}

Sample Code

Feel free to use the following Rust client as a guide for interacting with the prover.

async fn main() -> Result<(), Box<dyn Error>> {
    let client = HttpClient::new(base_url);

    // Start session
    println!("starting session...");
    let res = client
        .post(
            "/session",
            Some(json!({ "api_key": api_key.to_string(), "genesis_accounts": genesis_accounts })),
        )
        .await
        .unwrap();

    let response: StartSessionResponse = serde_json::from_value(res).unwrap();
    let session_id = match response {
        StartSessionResponse::Success { session_id } => session_id,
        StartSessionResponse::Error { error } => {
            panic!("Error response: {}", error);
        }
    };
    println!("session id: {session_id:?}");

    // Send transactions
    println!("sending transactions...");
    let tx = prepare_spl_tx(&payer, &alice, &bob, &minter, &mint_auth);
    let transaction: Vec<u8> = bincode::serialize(&tx).unwrap();
    let res = client
        .post(
            &format!("/session/{}/transactions", session_id.to_string()),
            Some(json!({ "transaction": transaction })),
        )
        .await
        .unwrap();
    println!("{res:#?}");

    // Poll session
    println!("polling session...");
    let res = client
        .get(&format!("/session/{}", session_id.to_string()))
        .await
        .unwrap();
    println!("{res:#?}");

    // End session
    println!("ending session...");
    let res = client
        .put(
            &format!("/session/{}", session_id.to_string()),
            Some(json!({ "proof_type": "groth16" })),
        )
        .await
        .unwrap();
    println!("{res:#?}");

    // Poll session again
    println!("polling session again...");
    let res = client
        .get(&format!("/session/{}", session_id.to_string()))
        .await
        .unwrap();
    println!("{res:#?}");

    Ok(())
}

System Constraints

There are a few limitations to keep in mind. The system currently places a hard cap of 50 transactions per session due to memory constraints of the underlying zkVM, which is around 24B cycles. The server will reject requests that send additional transactions beyond 50. Not that if you start a new session, old data from previous sessions are not persisted.

The zkSVM contains several pre-installed Solana Program Library (SPL) programs, e.g. the token and token-2022 programs, but any custom programs need to be deployed before they can be interacted with.

Last updated