use crate::imp::core::{Bytes32, ProofPrelude};
use crate::imp::guest::content::GateConfig;
use crate::imp::guest::datasection::{DataSection, SectionId};
use crate::imp::guest::host::DigHost;
use crate::imp::guest::oblivious::build_access_plan;
use crate::imp::guest::request::ProofRequest;
use alloc::vec::Vec;
use sha2::{Digest, Sha256};
pub enum ProofOutcome {
Real(ProofPrelude),
Decoy(ProofPrelude),
}
fn read_chunk(ds: &DataSection, index: u32) -> Option<Vec<u8>> {
let pool = ds.section(SectionId::ChunkPool)?;
if pool.len() < 4 {
return None;
}
let count = u32::from_be_bytes([pool[0], pool[1], pool[2], pool[3]]);
if index >= count {
return None;
}
let mut p = 4usize;
for i in 0..count {
if p + 4 > pool.len() {
return None;
}
let len = u32::from_be_bytes([pool[p], pool[p + 1], pool[p + 2], pool[p + 3]]) as usize;
p += 4;
if p + len > pool.len() {
return None;
}
if i == index {
return Some(pool[p..p + len].to_vec());
}
p += len;
}
None
}
fn decoy_prelude(rk: &Bytes32, root: &Bytes32) -> ProofPrelude {
let blob = crate::imp::guest::decoy::decoy_proof_blob(rk);
let mut oc = Sha256::new();
oc.update(b"digstore-decoy-output-v1");
oc.update(rk.0);
let mut output_commitment = [0u8; 32];
output_commitment.copy_from_slice(&oc.finalize());
let mut sd = Sha256::new();
sd.update(b"digstore-decoy-serving-v1");
sd.update(&blob);
let mut serving_digest = [0u8; 32];
serving_digest.copy_from_slice(&sd.finalize());
ProofPrelude {
roothash: *root,
output_commitment: Bytes32(output_commitment),
serving_digest: Bytes32(serving_digest),
}
}
pub fn serve_proof<H: DigHost + ?Sized>(
host: &H,
ds: &DataSection,
req: &ProofRequest,
_cfg: &GateConfig,
) -> ProofOutcome {
let root = req.root_hash.unwrap_or_else(|| ds.current_root());
let entry = match ds.lookup_key(&req.retrieval_key) {
Some(e) => e,
None => return ProofOutcome::Decoy(decoy_prelude(&req.retrieval_key, &root)),
};
let pool = ds.section(SectionId::ChunkPool).unwrap_or(&[]);
let pool_size = if pool.len() >= 4 {
u32::from_be_bytes([pool[0], pool[1], pool[2], pool[3]])
} else {
0
};
let plan = build_access_plan(&entry.chunk_indices, pool_size, |c| {
host.random_bytes(c)
.unwrap_or_else(|_| alloc::vec![0u8; c as usize])
});
let mut gathered: Vec<Vec<u8>> = Vec::with_capacity(plan.order.len());
for idx in &plan.order {
gathered.push(read_chunk(ds, *idx).unwrap_or_default());
}
let output_commitment = {
let mut h = Sha256::new();
for pos in &plan.real_positions {
h.update(gathered[*pos].as_slice());
}
let mut o = [0u8; 32];
o.copy_from_slice(&h.finalize());
Bytes32(o)
};
let mut sd = Sha256::new();
sd.update(req.retrieval_key.0);
for idx in &entry.chunk_indices {
sd.update(idx.to_be_bytes());
}
sd.update(req.client_nonce);
let mut serving_digest = [0u8; 32];
serving_digest.copy_from_slice(&sd.finalize());
ProofOutcome::Real(ProofPrelude {
roothash: root,
output_commitment,
serving_digest: Bytes32(serving_digest),
})
}