Skip to content

Commit 64a4e81

Browse files
committed
wip
1 parent b05cdc1 commit 64a4e81

File tree

6 files changed

+405
-300
lines changed

6 files changed

+405
-300
lines changed

crates/cartridge/src/lib.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ pub mod vrf;
55

66
pub use client::Client;
77
pub use vrf::{
8-
bootstrap_vrf, derive_vrf_accounts, resolve_executable, start_vrf_sidecar,
9-
vrf_account_class_hash, vrf_consumer_class_hash, vrf_secret_key_from_account_key,
10-
wait_for_http_ok, InfoResponse, RequestContext, SignedOutsideExecution, VrfBootstrap,
11-
VrfBootstrapConfig, VrfBootstrapResult, VrfClient, VrfClientError, VrfDerivedAccounts,
12-
VrfOutsideExecution, VrfSidecarConfig, VrfSidecarInfo, BOOTSTRAP_TIMEOUT, SIDECAR_TIMEOUT,
13-
VRF_ACCOUNT_SALT, VRF_CONSUMER_SALT, VRF_SERVER_PORT,
8+
bootstrap_vrf, derive_vrf_accounts, resolve_executable, vrf_account_class_hash,
9+
vrf_consumer_class_hash, vrf_secret_key_from_account_key, wait_for_http_ok, InfoResponse,
10+
RequestContext, SignedOutsideExecution, VrfBootstrap, VrfBootstrapConfig, VrfBootstrapResult,
11+
VrfClient, VrfClientError, VrfDerivedAccounts, VrfOutsideExecution, VrfService,
12+
VrfServiceConfig, VrfServiceProcess, VrfSidecarError, VrfSidecarResult, BOOTSTRAP_TIMEOUT,
13+
SIDECAR_TIMEOUT, VRF_ACCOUNT_SALT, VRF_CONSUMER_SALT, VRF_SERVER_PORT,
1414
};
1515

1616
#[rustfmt::skip]

crates/cartridge/src/vrf/bootstrap.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,6 @@ pub struct VrfBootstrapConfig {
5555
pub source_address: ContractAddress,
5656
/// Source account private key.
5757
pub source_private_key: Felt,
58-
59-
/// Whether to fund the VRF account (when fees are enabled).
60-
pub fund_account: bool,
6158
}
6259

6360
/// Result of VRF bootstrap operations.
@@ -172,8 +169,8 @@ pub async fn bootstrap_vrf(config: &VrfBootstrapConfig) -> Result<VrfBootstrapRe
172169
wait_for_contract(&provider, vrf_account_address, BOOTSTRAP_TIMEOUT).await?;
173170
}
174171

175-
// Fund VRF account if fees are enabled
176-
if config.fund_account {
172+
// Fund VRF account
173+
{
177174
let amount = Felt::from(1_000_000_000_000_000_000u128);
178175
let transfer_call = Call {
179176
to: DEFAULT_STRK_FEE_TOKEN_ADDRESS.into(),

crates/cartridge/src/vrf/client.rs

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
use katana_primitives::Felt;
2+
use katana_rpc_types::outside_execution::{OutsideExecutionV2, OutsideExecutionV3};
3+
use serde::{Deserialize, Serialize};
4+
use url::Url;
5+
6+
#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)]
7+
pub struct StarkVrfProof {
8+
pub gamma_x: Felt,
9+
pub gamma_y: Felt,
10+
pub c: Felt,
11+
pub s: Felt,
12+
pub sqrt_ratio: Felt,
13+
pub rnd: Felt,
14+
}
15+
16+
/// OutsideExecution enum with tagged serialization for VRF server compatibility.
17+
///
18+
/// Different from `katana_rpc_types::OutsideExecution` which uses untagged serialization.
19+
#[derive(Debug, Clone, Serialize, Deserialize)]
20+
pub enum VrfOutsideExecution {
21+
V2(OutsideExecutionV2),
22+
V3(OutsideExecutionV3),
23+
}
24+
25+
/// A signed outside execution request.
26+
#[derive(Debug, Clone, Serialize, Deserialize)]
27+
pub struct SignedOutsideExecution {
28+
pub address: Felt,
29+
pub outside_execution: VrfOutsideExecution,
30+
pub signature: Vec<Felt>,
31+
}
32+
33+
/// Response from GET /info endpoint.
34+
#[derive(Debug, Clone, Serialize, Deserialize)]
35+
pub struct InfoResponse {
36+
pub public_key_x: String,
37+
pub public_key_y: String,
38+
}
39+
40+
/// Request context for outside execution.
41+
#[derive(Debug, Clone, Serialize, Deserialize)]
42+
pub struct RequestContext {
43+
pub chain_id: String,
44+
pub rpc_url: Option<Url>,
45+
}
46+
47+
/// Error type for VRF client operations.
48+
#[derive(thiserror::Error, Debug)]
49+
pub enum VrfClientError {
50+
#[error("URL parsing error: {0}")]
51+
UrlParse(#[from] url::ParseError),
52+
53+
#[error("HTTP request error: {0}")]
54+
Request(#[from] reqwest::Error),
55+
56+
#[error("JSON parsing error: {0}")]
57+
Json(#[from] serde_json::Error),
58+
59+
#[error("server error: {0}")]
60+
Server(String),
61+
}
62+
63+
/// HTTP client for interacting with the VRF server.
64+
#[derive(Debug, Clone)]
65+
pub struct VrfClient {
66+
url: Url,
67+
client: reqwest::Client,
68+
}
69+
70+
impl VrfClient {
71+
/// Creates a new [`VrfClient`] with the given base URL.
72+
pub fn new(url: Url) -> Self {
73+
Self { url, client: reqwest::Client::new() }
74+
}
75+
76+
/// Health check - GET /
77+
///
78+
/// Returns `Ok(())` if server responds with "OK".
79+
#[tracing::instrument(level = "trace", skip_all)]
80+
pub async fn health_check(&self) -> Result<(), VrfClientError> {
81+
let response = self.client.get(self.url.clone()).send().await?;
82+
let text = response.text().await?;
83+
84+
if text.trim() == "OK" {
85+
Ok(())
86+
} else {
87+
Err(VrfClientError::Server(format!("unexpected response: {text}")))
88+
}
89+
}
90+
91+
/// Get VRF public key info - GET /info
92+
#[tracing::instrument(level = "trace", skip_all)]
93+
pub async fn info(&self) -> Result<InfoResponse, VrfClientError> {
94+
let url = self.url.join("/info")?;
95+
let response = self.client.get(url).send().await?;
96+
let info: InfoResponse = response.json().await?;
97+
Ok(info)
98+
}
99+
100+
/// Generate VRF proof - POST /proof
101+
#[tracing::instrument(level = "trace", skip_all)]
102+
pub async fn proof(&self, seed: Vec<String>) -> Result<StarkVrfProof, VrfClientError> {
103+
#[derive(Debug, Serialize)]
104+
struct ProofRequest {
105+
seed: Vec<String>,
106+
}
107+
108+
let url = self.url.join("/proof")?;
109+
let response = self
110+
.client
111+
.post(url)
112+
.header("Content-Type", "application/json")
113+
.json(&ProofRequest { seed })
114+
.send()
115+
.await?;
116+
117+
#[derive(Debug, Deserialize)]
118+
struct ProofResponse {
119+
result: StarkVrfProof,
120+
}
121+
122+
Ok(response.json::<ProofResponse>().await?.result)
123+
}
124+
125+
/// Process outside execution with VRF - POST /outside_execution
126+
#[tracing::instrument(level = "trace", skip_all)]
127+
pub async fn outside_execution(
128+
&self,
129+
request: SignedOutsideExecution,
130+
context: RequestContext,
131+
) -> Result<SignedOutsideExecution, VrfClientError> {
132+
#[derive(Debug, Serialize)]
133+
struct OutsideExecutionRequest {
134+
request: SignedOutsideExecution,
135+
context: RequestContext,
136+
}
137+
138+
let url = self.url.join("/outside_execution")?;
139+
let response = self
140+
.client
141+
.post(url)
142+
.header("Content-Type", "application/json")
143+
.json(&OutsideExecutionRequest { request, context })
144+
.send()
145+
.await?;
146+
147+
// Check for error responses (server returns 404 with JSON error for failures)
148+
if !response.status().is_success() {
149+
let error_text = response.text().await?;
150+
return Err(VrfClientError::Server(error_text));
151+
}
152+
153+
#[derive(Debug, Deserialize)]
154+
struct OutsideExecutionResponse {
155+
result: SignedOutsideExecution,
156+
}
157+
158+
Ok(response.json::<OutsideExecutionResponse>().await?.result)
159+
}
160+
}

crates/cartridge/src/vrf/mod.rs

Lines changed: 6 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -5,175 +5,19 @@
55
//! - Bootstrap logic for deploying VRF contracts
66
//! - Sidecar process management
77
8+
mod client;
9+
810
pub mod bootstrap;
911
pub mod sidecar;
1012

13+
pub use client::*;
14+
1115
pub use bootstrap::{
1216
bootstrap_vrf, derive_vrf_accounts, vrf_account_class_hash, vrf_consumer_class_hash,
1317
vrf_secret_key_from_account_key, VrfBootstrap, VrfBootstrapConfig, VrfBootstrapResult,
1418
VrfDerivedAccounts, BOOTSTRAP_TIMEOUT, VRF_ACCOUNT_SALT, VRF_CONSUMER_SALT,
1519
};
16-
use katana_primitives::Felt;
17-
use katana_rpc_types::outside_execution::{OutsideExecutionV2, OutsideExecutionV3};
18-
use serde::{Deserialize, Serialize};
1920
pub use sidecar::{
20-
resolve_executable, start_vrf_sidecar, wait_for_http_ok, VrfSidecarConfig, VrfSidecarInfo,
21-
SIDECAR_TIMEOUT, VRF_SERVER_PORT,
21+
resolve_executable, wait_for_http_ok, Error as VrfSidecarError, Result as VrfSidecarResult,
22+
VrfService, VrfServiceConfig, VrfServiceProcess, SIDECAR_TIMEOUT, VRF_SERVER_PORT,
2223
};
23-
use url::Url;
24-
25-
#[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)]
26-
pub struct StarkVrfProof {
27-
pub gamma_x: Felt,
28-
pub gamma_y: Felt,
29-
pub c: Felt,
30-
pub s: Felt,
31-
pub sqrt_ratio: Felt,
32-
pub rnd: Felt,
33-
}
34-
35-
/// OutsideExecution enum with tagged serialization for VRF server compatibility.
36-
///
37-
/// Different from `katana_rpc_types::OutsideExecution` which uses untagged serialization.
38-
#[derive(Debug, Clone, Serialize, Deserialize)]
39-
pub enum VrfOutsideExecution {
40-
V2(OutsideExecutionV2),
41-
V3(OutsideExecutionV3),
42-
}
43-
44-
/// A signed outside execution request.
45-
#[derive(Debug, Clone, Serialize, Deserialize)]
46-
pub struct SignedOutsideExecution {
47-
pub address: Felt,
48-
pub outside_execution: VrfOutsideExecution,
49-
pub signature: Vec<Felt>,
50-
}
51-
52-
/// Response from GET /info endpoint.
53-
#[derive(Debug, Clone, Serialize, Deserialize)]
54-
pub struct InfoResponse {
55-
pub public_key_x: String,
56-
pub public_key_y: String,
57-
}
58-
59-
/// Request context for outside execution.
60-
#[derive(Debug, Clone, Serialize, Deserialize)]
61-
pub struct RequestContext {
62-
pub chain_id: String,
63-
pub rpc_url: Option<Url>,
64-
}
65-
66-
/// Error type for VRF client operations.
67-
#[derive(thiserror::Error, Debug)]
68-
pub enum VrfClientError {
69-
#[error("URL parsing error: {0}")]
70-
UrlParse(#[from] url::ParseError),
71-
72-
#[error("HTTP request error: {0}")]
73-
Request(#[from] reqwest::Error),
74-
75-
#[error("JSON parsing error: {0}")]
76-
Json(#[from] serde_json::Error),
77-
78-
#[error("server error: {0}")]
79-
Server(String),
80-
}
81-
82-
/// HTTP client for interacting with the VRF server.
83-
#[derive(Debug, Clone)]
84-
pub struct VrfClient {
85-
url: Url,
86-
client: reqwest::Client,
87-
}
88-
89-
impl VrfClient {
90-
/// Creates a new [`VrfClient`] with the given base URL.
91-
pub fn new(url: Url) -> Self {
92-
Self { url, client: reqwest::Client::new() }
93-
}
94-
95-
/// Health check - GET /
96-
///
97-
/// Returns `Ok(())` if server responds with "OK".
98-
#[tracing::instrument(level = "trace", skip_all)]
99-
pub async fn health_check(&self) -> Result<(), VrfClientError> {
100-
let response = self.client.get(self.url.clone()).send().await?;
101-
let text = response.text().await?;
102-
103-
if text.trim() == "OK" {
104-
Ok(())
105-
} else {
106-
Err(VrfClientError::Server(format!("unexpected response: {text}")))
107-
}
108-
}
109-
110-
/// Get VRF public key info - GET /info
111-
#[tracing::instrument(level = "trace", skip_all)]
112-
pub async fn info(&self) -> Result<InfoResponse, VrfClientError> {
113-
let url = self.url.join("/info")?;
114-
let response = self.client.get(url).send().await?;
115-
let info: InfoResponse = response.json().await?;
116-
Ok(info)
117-
}
118-
119-
/// Generate VRF proof - POST /proof
120-
#[tracing::instrument(level = "trace", skip_all)]
121-
pub async fn proof(&self, seed: Vec<String>) -> Result<StarkVrfProof, VrfClientError> {
122-
#[derive(Debug, Serialize)]
123-
struct ProofRequest {
124-
seed: Vec<String>,
125-
}
126-
127-
let url = self.url.join("/proof")?;
128-
let response = self
129-
.client
130-
.post(url)
131-
.header("Content-Type", "application/json")
132-
.json(&ProofRequest { seed })
133-
.send()
134-
.await?;
135-
136-
#[derive(Debug, Deserialize)]
137-
struct ProofResponse {
138-
result: StarkVrfProof,
139-
}
140-
141-
Ok(response.json::<ProofResponse>().await?.result)
142-
}
143-
144-
/// Process outside execution with VRF - POST /outside_execution
145-
#[tracing::instrument(level = "trace", skip_all)]
146-
pub async fn outside_execution(
147-
&self,
148-
request: SignedOutsideExecution,
149-
context: RequestContext,
150-
) -> Result<SignedOutsideExecution, VrfClientError> {
151-
#[derive(Debug, Serialize)]
152-
struct OutsideExecutionRequest {
153-
request: SignedOutsideExecution,
154-
context: RequestContext,
155-
}
156-
157-
let url = self.url.join("/outside_execution")?;
158-
let response = self
159-
.client
160-
.post(url)
161-
.header("Content-Type", "application/json")
162-
.json(&OutsideExecutionRequest { request, context })
163-
.send()
164-
.await?;
165-
166-
// Check for error responses (server returns 404 with JSON error for failures)
167-
if !response.status().is_success() {
168-
let error_text = response.text().await?;
169-
return Err(VrfClientError::Server(error_text));
170-
}
171-
172-
#[derive(Debug, Deserialize)]
173-
struct OutsideExecutionResponse {
174-
result: SignedOutsideExecution,
175-
}
176-
177-
Ok(response.json::<OutsideExecutionResponse>().await?.result)
178-
}
179-
}

0 commit comments

Comments
 (0)