use crate::Status;
use serde::{Deserialize, Serialize, Serializer};
use serde_with::{base64::Base64, serde_as};
use std::borrow::Cow;
use thiserror::Error;
use warg_crypto::hash::AnyHash;
use warg_protocol::registry::{LogId, RegistryIndex, RegistryLen};
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ConsistencyRequest {
pub from: RegistryLen,
pub to: RegistryLen,
}
#[serde_as]
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ConsistencyResponse {
#[serde_as(as = "Base64")]
pub proof: Vec<u8>,
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct InclusionRequest {
pub log_length: RegistryLen,
pub leafs: Vec<RegistryIndex>,
}
#[serde_as]
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct InclusionResponse {
#[serde_as(as = "Base64")]
pub log: Vec<u8>,
#[serde_as(as = "Base64")]
pub map: Vec<u8>,
}
#[non_exhaustive]
#[derive(Debug, Error)]
pub enum ProofError {
#[error("checkpoint not found for log length {0}")]
CheckpointNotFound(RegistryLen),
#[error("log leaf `{0}` not found")]
LeafNotFound(RegistryIndex),
#[error("failed to prove inclusion of package log `{0}`")]
PackageLogNotIncluded(LogId),
#[error("failed to prove inclusion: found root `{found}` but was given root `{root}`")]
IncorrectProof {
root: AnyHash,
found: AnyHash,
},
#[error("failed to bundle proofs: {0}")]
BundleFailure(String),
#[error("{message}")]
Message {
status: u16,
message: String,
},
}
impl ProofError {
pub fn status(&self) -> u16 {
match self {
Self::CheckpointNotFound(_) | Self::LeafNotFound(_) => 404,
Self::BundleFailure(_)
| Self::PackageLogNotIncluded(_)
| Self::IncorrectProof { .. } => 422,
Self::Message { status, .. } => *status,
}
}
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
enum EntityType {
LogLength,
Leaf,
}
#[derive(Serialize, Deserialize)]
#[serde(tag = "reason", rename_all = "camelCase")]
enum BundleError<'a> {
PackageNotIncluded {
log_id: Cow<'a, LogId>,
},
IncorrectProof {
root: Cow<'a, AnyHash>,
found: Cow<'a, AnyHash>,
},
Failure {
message: Cow<'a, str>,
},
}
#[derive(Serialize, Deserialize)]
#[serde(untagged, rename_all = "camelCase")]
enum RawError<'a> {
NotFound {
status: Status<404>,
#[serde(rename = "type")]
ty: EntityType,
id: RegistryIndex,
},
BundleError {
status: Status<422>,
#[serde(flatten)]
error: BundleError<'a>,
},
Message {
status: u16,
message: Cow<'a, str>,
},
}
impl Serialize for ProofError {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
Self::CheckpointNotFound(log_length) => RawError::NotFound {
status: Status::<404>,
ty: EntityType::LogLength,
id: *log_length,
}
.serialize(serializer),
Self::LeafNotFound(leaf_index) => RawError::NotFound {
status: Status::<404>,
ty: EntityType::Leaf,
id: *leaf_index,
}
.serialize(serializer),
Self::PackageLogNotIncluded(log_id) => RawError::BundleError {
status: Status::<422>,
error: BundleError::PackageNotIncluded {
log_id: Cow::Borrowed(log_id),
},
}
.serialize(serializer),
Self::IncorrectProof { root, found } => RawError::BundleError {
status: Status::<422>,
error: BundleError::IncorrectProof {
root: Cow::Borrowed(root),
found: Cow::Borrowed(found),
},
}
.serialize(serializer),
Self::BundleFailure(message) => RawError::BundleError {
status: Status::<422>,
error: BundleError::Failure {
message: Cow::Borrowed(message),
},
}
.serialize(serializer),
Self::Message { status, message } => RawError::Message {
status: *status,
message: Cow::Borrowed(message),
}
.serialize(serializer),
}
}
}
impl<'de> Deserialize<'de> for ProofError {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
match RawError::deserialize(deserializer)? {
RawError::NotFound { status: _, ty, id } => match ty {
EntityType::LogLength => Ok(Self::CheckpointNotFound(id)),
EntityType::Leaf => Ok(Self::LeafNotFound(id)),
},
RawError::BundleError { status: _, error } => match error {
BundleError::PackageNotIncluded { log_id } => {
Ok(Self::PackageLogNotIncluded(log_id.into_owned()))
}
BundleError::IncorrectProof { root, found } => Ok(Self::IncorrectProof {
root: root.into_owned(),
found: found.into_owned(),
}),
BundleError::Failure { message } => Ok(Self::BundleFailure(message.into_owned())),
},
RawError::Message { status, message } => Ok(Self::Message {
status,
message: message.into_owned(),
}),
}
}
}