warg_server/api/v1/
proof.rs

1use super::{Json, RegistryHeader};
2use crate::services::{CoreService, CoreServiceError};
3use axum::{
4    debug_handler, extract::State, http::StatusCode, response::IntoResponse, routing::post, Router,
5};
6use warg_api::v1::proof::{
7    ConsistencyRequest, ConsistencyResponse, InclusionRequest, InclusionResponse, ProofError,
8};
9use warg_protocol::registry::{RegistryIndex, RegistryLen};
10
11#[derive(Clone)]
12pub struct Config {
13    core: CoreService,
14}
15
16impl Config {
17    pub fn new(core: CoreService) -> Self {
18        Self { core }
19    }
20
21    pub fn into_router(self) -> Router {
22        Router::new()
23            .route("/consistency", post(prove_consistency))
24            .route("/inclusion", post(prove_inclusion))
25            .with_state(self)
26    }
27}
28
29struct ProofApiError(ProofError);
30
31impl From<CoreServiceError> for ProofApiError {
32    fn from(value: CoreServiceError) -> Self {
33        Self(match value {
34            CoreServiceError::CheckpointNotFound(log_length) => {
35                ProofError::CheckpointNotFound(log_length)
36            }
37            CoreServiceError::LeafNotFound(leaf) => ProofError::LeafNotFound(leaf),
38            CoreServiceError::BundleFailure(e) => ProofError::BundleFailure(e.to_string()),
39            CoreServiceError::PackageNotIncluded(id) => ProofError::PackageLogNotIncluded(id),
40            CoreServiceError::IncorrectProof { root, found } => {
41                ProofError::IncorrectProof { root, found }
42            }
43            other => {
44                tracing::error!("Unhandled CoreServiceError: {other:?}");
45                ProofError::Message {
46                    status: 500,
47                    message: "Internal service error".to_string(),
48                }
49            }
50        })
51    }
52}
53
54impl IntoResponse for ProofApiError {
55    fn into_response(self) -> axum::response::Response {
56        (StatusCode::from_u16(self.0.status()).unwrap(), Json(self.0)).into_response()
57    }
58}
59
60#[debug_handler]
61async fn prove_consistency(
62    State(config): State<Config>,
63    RegistryHeader(_registry_header): RegistryHeader,
64    Json(body): Json<ConsistencyRequest>,
65) -> Result<Json<ConsistencyResponse>, ProofApiError> {
66    let bundle = config
67        .core
68        .log_consistency_proof(body.from as RegistryLen, body.to as RegistryLen)
69        .await?;
70
71    Ok(Json(ConsistencyResponse {
72        proof: bundle.encode(),
73    }))
74}
75
76#[debug_handler]
77async fn prove_inclusion(
78    State(config): State<Config>,
79    RegistryHeader(_registry_header): RegistryHeader,
80    Json(body): Json<InclusionRequest>,
81) -> Result<Json<InclusionResponse>, ProofApiError> {
82    let log_length = body.log_length as RegistryLen;
83    let leafs = body
84        .leafs
85        .into_iter()
86        .map(|index| index as RegistryIndex)
87        .collect::<Vec<RegistryIndex>>();
88
89    let log_bundle = config.core.log_inclusion_proofs(log_length, &leafs).await?;
90    let map_bundle = config.core.map_inclusion_proofs(log_length, &leafs).await?;
91
92    Ok(Json(InclusionResponse {
93        log: log_bundle.encode(),
94        map: map_bundle.encode(),
95    }))
96}