use qssm_gadget::TruthWitness;
use qssm_le::{PublicInstance, Witness, BETA, N};
use qssm_templates::QssmTemplate;
use qssm_utils::hashing::{
blake3_hash, hash_domain, DOMAIN_SDK_LE_MASK, DOMAIN_SDK_LE_WITNESS, DOMAIN_SDK_MS_SEED,
};
use crate::context::{Proof, ProofContext};
use crate::error::ZkError;
use zeroize::Zeroize;
const DOMAIN_EXTERNAL_ENTROPY: &str = "QSSM-SDK-EXTERNAL-ENTROPY-v1";
pub fn prove(
ctx: &ProofContext,
template: &QssmTemplate,
claim: &serde_json::Value,
value: u64,
target: u64,
binding_ctx: [u8; 32],
mut entropy_seed: [u8; 32],
) -> Result<Proof, ZkError> {
template.verify_public_claim(claim)?;
let mut ms_seed = hash_domain(
DOMAIN_SDK_MS_SEED,
&[entropy_seed.as_slice(), binding_ctx.as_slice()],
);
let binding_entropy = blake3_hash(&binding_ctx);
let (root, salts) = qssm_ms::commit(ms_seed, binding_entropy).map_err(ZkError::MsCommit)?;
ms_seed.zeroize();
let context = crate::MS_CONTEXT_TAG.to_vec();
let ms_proof = qssm_ms::prove(
value,
target,
&salts,
binding_entropy,
&context,
&binding_ctx,
)
.map_err(|e| ZkError::MsProve {
source: e,
value,
target,
})?;
let external_entropy = hash_domain(
DOMAIN_EXTERNAL_ENTROPY,
&[entropy_seed.as_slice(), binding_ctx.as_slice()],
);
let external_entropy_included = false;
let tw = TruthWitness::bind(
*root.as_bytes(),
binding_ctx,
ms_proof.n(),
ms_proof.k(),
ms_proof.bit_at_k(),
*ms_proof.challenge(),
external_entropy,
external_entropy_included,
);
tw.validate().map_err(|_| ZkError::TruthWitnessInvalid)?;
let public = PublicInstance::digest_coeffs(tw.digest_coeff_vector)
.map_err(|_| ZkError::TruthWitnessInvalid)?;
let witness = derive_le_witness(&entropy_seed, &binding_ctx);
let mut le_mask_seed = hash_domain(
DOMAIN_SDK_LE_MASK,
&[entropy_seed.as_slice(), binding_ctx.as_slice()],
);
let (le_commitment, le_proof) =
qssm_le::prove_arithmetic(ctx.vk(), &public, &witness, &binding_ctx, le_mask_seed)
.map_err(ZkError::LeProve)?;
le_mask_seed.zeroize();
entropy_seed.zeroize();
Ok(Proof::new(
*root.as_bytes(),
ms_proof,
le_commitment,
le_proof,
external_entropy,
external_entropy_included,
value,
target,
binding_entropy,
))
}
fn derive_le_witness(entropy_seed: &[u8; 32], binding_ctx: &[u8; 32]) -> Witness {
let beta_i32 = BETA as i32;
let modulus = 2 * BETA + 1; let mut r = [0i32; N];
for chunk_idx in 0u32..32 {
let idx_bytes = chunk_idx.to_le_bytes();
let h = hash_domain(
DOMAIN_SDK_LE_WITNESS,
&[entropy_seed.as_slice(), binding_ctx.as_slice(), &idx_bytes],
);
for j in 0..8 {
let offset = j * 4;
let raw = u32::from_le_bytes([h[offset], h[offset + 1], h[offset + 2], h[offset + 3]]);
r[chunk_idx as usize * 8 + j] = (raw % modulus) as i32 - beta_i32;
}
}
let witness = Witness::new(r);
r.zeroize();
witness
}