use std::{io, time::Duration};
use hyphae_query::{ExecutionLimits, Query, QueryError, QueryResult, Record};
use hyphae_storage::{SnapshotError, SnapshotInfo, SnapshotReadLimits};
use thiserror::Error;
use crate::DocumentError;
pub const RESULT_PROOF_FORMAT_VERSION: u16 = 1;
pub const MAX_RESULT_PROOF_BYTES: u64 = 64 * 1024 * 1024;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ProofAnchor {
pub checkpoint_sequence: u64,
pub checkpoint_digest: Option<[u8; 32]>,
pub snapshot_digest: [u8; 32],
}
impl ProofAnchor {
pub fn from_snapshot(snapshot: &SnapshotInfo) -> Self {
Self {
checkpoint_sequence: snapshot.checkpoint_sequence,
checkpoint_digest: snapshot.checkpoint_digest,
snapshot_digest: snapshot.snapshot_digest,
}
}
pub fn digest(&self) -> [u8; 32] {
let mut hasher = blake3::Hasher::new();
hasher.update(b"hyphae-proof-anchor-v1");
hasher.update(&self.checkpoint_sequence.to_le_bytes());
hasher.update(&self.checkpoint_digest.unwrap_or([0; 32]));
hasher.update(&self.snapshot_digest);
*hasher.finalize().as_bytes()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ProvenOperation {
Get {
key: Vec<u8>,
},
Query(Query),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ProvenResult {
Get(Option<Record>),
Query(QueryResult),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ResultProof {
pub(crate) anchor: ProofAnchor,
pub(crate) operation: ProvenOperation,
pub(crate) result: ProvenResult,
pub(crate) proof_digest: [u8; 32],
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ResultProofArtifact {
pub proof: ResultProof,
pub snapshot: SnapshotInfo,
}
impl ResultProof {
pub fn anchor(&self) -> &ProofAnchor {
&self.anchor
}
pub fn anchor_digest(&self) -> [u8; 32] {
self.anchor.digest()
}
pub fn operation(&self) -> &ProvenOperation {
&self.operation
}
pub fn result(&self) -> &ProvenResult {
&self.result
}
pub fn proof_digest(&self) -> [u8; 32] {
self.proof_digest
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct VerificationLimits {
pub proof_bytes: u64,
pub snapshot: SnapshotReadLimits,
pub query: ExecutionLimits,
pub timeout: Duration,
}
impl Default for VerificationLimits {
fn default() -> Self {
Self {
proof_bytes: MAX_RESULT_PROOF_BYTES,
snapshot: SnapshotReadLimits::default(),
query: ExecutionLimits::default(),
timeout: Duration::from_secs(60),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct VerificationReport {
pub anchor: ProofAnchor,
pub anchor_digest: [u8; 32],
pub proof_digest: [u8; 32],
pub result: ProvenResult,
}
#[derive(Debug, Error)]
pub enum ProofError {
#[error(transparent)]
Io(#[from] io::Error),
#[error(transparent)]
Document(#[from] DocumentError),
#[error("snapshot witness failed: {source}")]
Snapshot {
#[source]
source: Box<SnapshotError>,
},
#[error("query reexecution failed: {source}")]
Query {
#[source]
source: Box<QueryError>,
},
#[error("invalid result proof: {reason}")]
Invalid {
reason: &'static str,
},
#[error("unsupported result-proof format {found}; supported format is {supported}")]
UnsupportedVersion {
found: u16,
supported: u16,
},
#[error("result proof is {actual} bytes; verification limit is {maximum}")]
ProofLimitExceeded {
actual: u64,
maximum: u64,
},
#[error("result-proof length overflow")]
LengthOverflow,
#[error("result-proof CRC32C mismatch")]
ChecksumMismatch,
#[error("result-proof BLAKE3 mismatch")]
DigestMismatch,
#[error("result-proof anchor does not match the trusted anchor digest")]
AnchorMismatch,
#[error("snapshot witness does not match the result-proof anchor")]
SnapshotAnchorMismatch,
#[error("result-proof operation and result variants do not match")]
OperationResultMismatch,
#[error("offline reexecution does not match the result proof")]
ReexecutionMismatch,
#[error("result-proof verification timed out")]
TimedOut,
}
impl From<SnapshotError> for ProofError {
fn from(source: SnapshotError) -> Self {
Self::Snapshot {
source: Box::new(source),
}
}
}
impl From<QueryError> for ProofError {
fn from(source: QueryError) -> Self {
Self::Query {
source: Box::new(source),
}
}
}