|
5 | 5 | //! - Bootstrap logic for deploying VRF contracts |
6 | 6 | //! - Sidecar process management |
7 | 7 |
|
| 8 | +mod client; |
| 9 | + |
8 | 10 | pub mod bootstrap; |
9 | 11 | pub mod sidecar; |
10 | 12 |
|
| 13 | +pub use client::*; |
| 14 | + |
11 | 15 | pub use bootstrap::{ |
12 | 16 | bootstrap_vrf, derive_vrf_accounts, vrf_account_class_hash, vrf_consumer_class_hash, |
13 | 17 | vrf_secret_key_from_account_key, VrfBootstrap, VrfBootstrapConfig, VrfBootstrapResult, |
14 | 18 | VrfDerivedAccounts, BOOTSTRAP_TIMEOUT, VRF_ACCOUNT_SALT, VRF_CONSUMER_SALT, |
15 | 19 | }; |
16 | | -use katana_primitives::Felt; |
17 | | -use katana_rpc_types::outside_execution::{OutsideExecutionV2, OutsideExecutionV3}; |
18 | | -use serde::{Deserialize, Serialize}; |
19 | 20 | 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, |
22 | 23 | }; |
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