1use crate::Status;
4use serde::{Deserialize, Serialize, Serializer};
5use serde_with::{base64::Base64, serde_as};
6use std::borrow::Cow;
7use thiserror::Error;
8use warg_crypto::hash::AnyHash;
9use warg_protocol::registry::{LogId, RegistryIndex, RegistryLen};
10
11#[derive(Serialize, Deserialize)]
13#[serde(rename_all = "camelCase")]
14pub struct ConsistencyRequest {
15 pub from: RegistryLen,
17 pub to: RegistryLen,
19}
20
21#[serde_as]
23#[derive(Serialize, Deserialize)]
24#[serde(rename_all = "camelCase")]
25pub struct ConsistencyResponse {
26 #[serde_as(as = "Base64")]
28 pub proof: Vec<u8>,
29}
30
31#[derive(Serialize, Deserialize)]
33#[serde(rename_all = "camelCase")]
34pub struct InclusionRequest {
35 pub log_length: RegistryLen,
37 pub leafs: Vec<RegistryIndex>,
39}
40
41#[serde_as]
43#[derive(Serialize, Deserialize, Debug)]
44#[serde(rename_all = "camelCase")]
45pub struct InclusionResponse {
46 #[serde_as(as = "Base64")]
48 pub log: Vec<u8>,
49 #[serde_as(as = "Base64")]
51 pub map: Vec<u8>,
52}
53
54#[non_exhaustive]
56#[derive(Debug, Error)]
57pub enum ProofError {
58 #[error("checkpoint not found for log length {0}")]
60 CheckpointNotFound(RegistryLen),
61 #[error("log leaf `{0}` not found")]
63 LeafNotFound(RegistryIndex),
64 #[error("failed to prove inclusion of package log `{0}`")]
66 PackageLogNotIncluded(LogId),
67 #[error("failed to prove inclusion: found root `{found}` but was given root `{root}`")]
69 IncorrectProof {
70 root: AnyHash,
72 found: AnyHash,
74 },
75 #[error("failed to bundle proofs: {0}")]
77 BundleFailure(String),
78 #[error("{message}")]
80 Message {
81 status: u16,
83 message: String,
85 },
86}
87
88impl ProofError {
89 pub fn status(&self) -> u16 {
91 match self {
92 Self::CheckpointNotFound(_) | Self::LeafNotFound(_) => 404,
93 Self::BundleFailure(_)
94 | Self::PackageLogNotIncluded(_)
95 | Self::IncorrectProof { .. } => 422,
96 Self::Message { status, .. } => *status,
97 }
98 }
99}
100
101#[derive(Serialize, Deserialize)]
102#[serde(rename_all = "camelCase")]
103enum EntityType {
104 LogLength,
105 Leaf,
106}
107
108#[derive(Serialize, Deserialize)]
109#[serde(tag = "reason", rename_all = "camelCase")]
110enum BundleError<'a> {
111 PackageNotIncluded {
112 log_id: Cow<'a, LogId>,
113 },
114 IncorrectProof {
115 root: Cow<'a, AnyHash>,
116 found: Cow<'a, AnyHash>,
117 },
118 Failure {
119 message: Cow<'a, str>,
120 },
121}
122
123#[derive(Serialize, Deserialize)]
124#[serde(untagged, rename_all = "camelCase")]
125enum RawError<'a> {
126 NotFound {
127 status: Status<404>,
128 #[serde(rename = "type")]
129 ty: EntityType,
130 id: RegistryIndex,
131 },
132 BundleError {
133 status: Status<422>,
134 #[serde(flatten)]
135 error: BundleError<'a>,
136 },
137 Message {
138 status: u16,
139 message: Cow<'a, str>,
140 },
141}
142
143impl Serialize for ProofError {
144 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
145 match self {
146 Self::CheckpointNotFound(log_length) => RawError::NotFound {
147 status: Status::<404>,
148 ty: EntityType::LogLength,
149 id: *log_length,
150 }
151 .serialize(serializer),
152 Self::LeafNotFound(leaf_index) => RawError::NotFound {
153 status: Status::<404>,
154 ty: EntityType::Leaf,
155 id: *leaf_index,
156 }
157 .serialize(serializer),
158 Self::PackageLogNotIncluded(log_id) => RawError::BundleError {
159 status: Status::<422>,
160 error: BundleError::PackageNotIncluded {
161 log_id: Cow::Borrowed(log_id),
162 },
163 }
164 .serialize(serializer),
165 Self::IncorrectProof { root, found } => RawError::BundleError {
166 status: Status::<422>,
167 error: BundleError::IncorrectProof {
168 root: Cow::Borrowed(root),
169 found: Cow::Borrowed(found),
170 },
171 }
172 .serialize(serializer),
173 Self::BundleFailure(message) => RawError::BundleError {
174 status: Status::<422>,
175 error: BundleError::Failure {
176 message: Cow::Borrowed(message),
177 },
178 }
179 .serialize(serializer),
180 Self::Message { status, message } => RawError::Message {
181 status: *status,
182 message: Cow::Borrowed(message),
183 }
184 .serialize(serializer),
185 }
186 }
187}
188
189impl<'de> Deserialize<'de> for ProofError {
190 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
191 where
192 D: serde::Deserializer<'de>,
193 {
194 match RawError::deserialize(deserializer)? {
195 RawError::NotFound { status: _, ty, id } => match ty {
196 EntityType::LogLength => Ok(Self::CheckpointNotFound(id)),
197 EntityType::Leaf => Ok(Self::LeafNotFound(id)),
198 },
199 RawError::BundleError { status: _, error } => match error {
200 BundleError::PackageNotIncluded { log_id } => {
201 Ok(Self::PackageLogNotIncluded(log_id.into_owned()))
202 }
203 BundleError::IncorrectProof { root, found } => Ok(Self::IncorrectProof {
204 root: root.into_owned(),
205 found: found.into_owned(),
206 }),
207 BundleError::Failure { message } => Ok(Self::BundleFailure(message.into_owned())),
208 },
209 RawError::Message { status, message } => Ok(Self::Message {
210 status,
211 message: message.into_owned(),
212 }),
213 }
214 }
215}