Skip to content

Commit 5e05c31

Browse files
committed
wip
1 parent 1ef4231 commit 5e05c31

File tree

4 files changed

+107
-20
lines changed

4 files changed

+107
-20
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
use jsonrpsee::types::ErrorObjectOwned;
2+
use katana_pool_api::PoolError;
3+
use katana_provider_api::ProviderError;
4+
5+
/// Error codes for Cartridge API (starting at 200 to avoid conflicts).
6+
const CONTROLLER_DEPLOYMENT_FAILED: i32 = 200;
7+
const VRF_MISSING_FOLLOW_UP_CALL: i32 = 201;
8+
const VRF_INVALID_TARGET: i32 = 202;
9+
const VRF_EXECUTION_FAILED: i32 = 203;
10+
const PAYMASTER_EXECUTION_FAILED: i32 = 204;
11+
const POOL_ERROR: i32 = 205;
12+
const PROVIDER_ERROR: i32 = 206;
13+
const INTERNAL_ERROR: i32 = 299;
14+
15+
#[derive(Debug, thiserror::Error, Clone)]
16+
pub enum CartridgeApiError {
17+
/// Failed to deploy a Cartridge controller account.
18+
#[error("Controller deployment failed: {reason}")]
19+
ControllerDeployment { reason: String },
20+
21+
/// The `request_random` call is not followed by another call in the outside execution.
22+
#[error("request_random call must be followed by another call")]
23+
VrfMissingFollowUpCall,
24+
25+
/// The `request_random` call does not target the expected VRF account.
26+
#[error("request_random call must target the VRF account")]
27+
VrfInvalidTarget,
28+
29+
/// The VRF outside execution request failed.
30+
///
31+
/// Error returns by the VRF server.
32+
#[error("VRF execution failed: {reason}")]
33+
VrfExecutionFailed { reason: String },
34+
35+
/// The paymaster failed to execute the transaction.
36+
///
37+
/// Error returns by the Paymaster server.
38+
#[error("Paymaster execution failed: {reason}")]
39+
PaymasterExecutionFailed { reason: String },
40+
41+
/// Failed to submit transaction to the pool.
42+
#[error("Transaction pool error: {reason}")]
43+
PoolError { reason: String },
44+
45+
/// Storage provider error.
46+
#[error("Provider error: {reason}")]
47+
ProviderError { reason: String },
48+
49+
/// Internal error (e.g., task execution failure).
50+
#[error("Internal error: {reason}")]
51+
InternalError { reason: String },
52+
}
53+
54+
impl From<CartridgeApiError> for ErrorObjectOwned {
55+
fn from(err: CartridgeApiError) -> Self {
56+
let code = match &err {
57+
CartridgeApiError::ControllerDeployment { .. } => CONTROLLER_DEPLOYMENT_FAILED,
58+
CartridgeApiError::VrfMissingFollowUpCall => VRF_MISSING_FOLLOW_UP_CALL,
59+
CartridgeApiError::VrfInvalidTarget => VRF_INVALID_TARGET,
60+
CartridgeApiError::VrfExecutionFailed { .. } => VRF_EXECUTION_FAILED,
61+
CartridgeApiError::PaymasterExecutionFailed { .. } => PAYMASTER_EXECUTION_FAILED,
62+
CartridgeApiError::PoolError { .. } => POOL_ERROR,
63+
CartridgeApiError::ProviderError { .. } => PROVIDER_ERROR,
64+
CartridgeApiError::InternalError { .. } => INTERNAL_ERROR,
65+
};
66+
67+
ErrorObjectOwned::owned(code, err.to_string(), None::<()>)
68+
}
69+
}
70+
71+
impl From<ProviderError> for CartridgeApiError {
72+
fn from(value: ProviderError) -> Self {
73+
CartridgeApiError::ProviderError { reason: value.to_string() }
74+
}
75+
}
76+
77+
impl From<anyhow::Error> for CartridgeApiError {
78+
fn from(value: anyhow::Error) -> Self {
79+
CartridgeApiError::ControllerDeployment { reason: value.to_string() }
80+
}
81+
}
82+
83+
impl From<PoolError> for CartridgeApiError {
84+
fn from(error: PoolError) -> Self {
85+
CartridgeApiError::PoolError { reason: error.to_string() }
86+
}
87+
}

crates/rpc/rpc-api/src/error/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
pub mod cartridge;
12
pub mod dev;
23
pub mod katana;
34
pub mod starknet;

crates/rpc/rpc-server/src/cartridge/mod.rs

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ use katana_primitives::{ContractAddress, Felt};
4949
use katana_provider::api::state::{StateFactoryProvider, StateProvider};
5050
use katana_provider::{ProviderFactory, ProviderRO, ProviderRW};
5151
use katana_rpc_api::cartridge::CartridgeApiServer;
52-
use katana_rpc_api::error::starknet::StarknetApiError;
52+
use katana_rpc_api::error::cartridge::CartridgeApiError;
5353
use katana_rpc_api::paymaster::PaymasterApiClient;
5454
use katana_rpc_types::broadcasted::AddInvokeTransactionResponse;
5555
use katana_rpc_types::cartridge::FeeSource;
@@ -163,14 +163,14 @@ where
163163
})
164164
}
165165

166-
fn nonce(&self, address: ContractAddress) -> Result<Option<Nonce>, StarknetApiError> {
166+
fn nonce(&self, address: ContractAddress) -> Result<Option<Nonce>, CartridgeApiError> {
167167
match self.pool.get_nonce(address) {
168168
pending_nonce @ Some(..) => Ok(pending_nonce),
169169
None => Ok(self.state()?.nonce(address)?),
170170
}
171171
}
172172

173-
fn state(&self) -> Result<Box<dyn StateProvider>, StarknetApiError> {
173+
fn state(&self) -> Result<Box<dyn StateProvider>, CartridgeApiError> {
174174
match &*self.block_producer.producer.read() {
175175
BlockProducerMode::Instant(_) => Ok(self.backend.storage.provider().latest()?),
176176
BlockProducerMode::Interval(producer) => Ok(producer.executor().read().state()),
@@ -183,7 +183,7 @@ where
183183
outside_execution: OutsideExecution,
184184
signature: Vec<Felt>,
185185
fee_source: Option<FeeSource>,
186-
) -> Result<AddInvokeTransactionResponse, StarknetApiError> {
186+
) -> Result<AddInvokeTransactionResponse, CartridgeApiError> {
187187
debug!(%contract_address, ?outside_execution, "Adding execute outside transaction.");
188188
self.on_cpu_blocking_task(move |this| async move {
189189
let pm_address = this.controller_deployer_address;
@@ -228,15 +228,11 @@ where
228228
get_request_random_call(&outside_execution)
229229
{
230230
if position + 1 >= outside_execution.len() {
231-
return Err(StarknetApiError::unexpected(
232-
"request_random call must be followed by another call",
233-
));
231+
return Err(CartridgeApiError::VrfMissingFollowUpCall);
234232
}
235233

236234
if request_random_call.to != vrf_service.account_address() {
237-
return Err(StarknetApiError::unexpected(
238-
"request_random call must target the vrf account",
239-
));
235+
return Err(CartridgeApiError::VrfInvalidTarget);
240236
}
241237

242238
// Delegate VRF computation to the VRF server
@@ -284,7 +280,9 @@ where
284280
.paymaster_client
285281
.execute_raw_transaction(request)
286282
.await
287-
.map_err(StarknetApiError::unexpected)?;
283+
.map_err(|e| CartridgeApiError::PaymasterExecutionFailed {
284+
reason: e.to_string(),
285+
})?;
288286

289287
Ok(AddInvokeTransactionResponse { transaction_hash: response.transaction_hash })
290288
})
@@ -293,7 +291,7 @@ where
293291

294292
/// Spawns an async function that is mostly CPU-bound blocking task onto the manager's blocking
295293
/// pool.
296-
async fn on_cpu_blocking_task<T, F>(&self, func: T) -> Result<F::Output, StarknetApiError>
294+
async fn on_cpu_blocking_task<T, F>(&self, func: T) -> Result<F::Output, CartridgeApiError>
297295
where
298296
T: FnOnce(Self) -> F,
299297
F: Future + Send + 'static,
@@ -315,9 +313,9 @@ where
315313

316314
match self.task_spawner.cpu_bound().spawn(task).await {
317315
TaskResult::Ok(result) => Ok(result),
318-
TaskResult::Err(err) => {
319-
Err(StarknetApiError::unexpected(format!("internal task execution failed: {err}")))
320-
}
316+
TaskResult::Err(err) => Err(CartridgeApiError::InternalError {
317+
reason: format!("task execution failed: {err}"),
318+
}),
321319
}
322320
}
323321
}

crates/rpc/rpc-server/src/cartridge/vrf.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use cartridge::vrf::{RequestContext, SignedOutsideExecution, VrfOutsideExecution
55
use cartridge::VrfClient;
66
use katana_primitives::chain::ChainId;
77
use katana_primitives::{ContractAddress, Felt};
8-
use katana_rpc_api::error::starknet::StarknetApiError;
8+
use katana_rpc_api::error::cartridge::CartridgeApiError;
99
use katana_rpc_types::outside_execution::OutsideExecution;
1010
use starknet::macros::selector;
1111
use url::Url;
@@ -46,7 +46,7 @@ impl VrfService {
4646
outside_execution: &OutsideExecution,
4747
signature: &[Felt],
4848
chain_id: ChainId,
49-
) -> Result<SignedOutsideExecution, StarknetApiError> {
49+
) -> Result<SignedOutsideExecution, CartridgeApiError> {
5050
let vrf_outside_execution = match outside_execution {
5151
OutsideExecution::V2(v2) => VrfOutsideExecution::V2(v2.clone()),
5252
OutsideExecution::V3(v3) => VrfOutsideExecution::V3(v3.clone()),
@@ -63,9 +63,10 @@ impl VrfService {
6363
rpc_url: Some(self.rpc_url.clone()),
6464
};
6565

66-
self.client.outside_execution(request, context).await.map_err(|err| {
67-
StarknetApiError::unexpected(format!("vrf outside_execution failed: {err}"))
68-
})
66+
self.client
67+
.outside_execution(request, context)
68+
.await
69+
.map_err(|err| CartridgeApiError::VrfExecutionFailed { reason: err.to_string() })
6970
}
7071
}
7172

0 commit comments

Comments
 (0)