#![cfg(feature = "risc0")]
use crate::imp::core::{Bytes32, ChiaBlockRef, ExecutionProof};
use crate::imp::crypto::bls;
use crate::imp::prover::build_public_input;
use crate::imp::prover::risc0_backend::{Risc0Prover, Risc0Verifier};
use crate::imp::prover::{MockChainSource, Prover, ProverError, ServingInputs, Verifier};
struct Fixture {
program_hash: Bytes32,
root: Bytes32,
public_input: Vec<u8>,
block: ChiaBlockRef,
serving: ServingInputs,
prover: Risc0Prover,
}
impl Fixture {
fn new() -> Self {
let sk = bls::SecretKey::from_seed(&[7u8; 32]);
let pk = sk.public_key();
let block = ChiaBlockRef {
header_hash: Bytes32([0x55u8; 32]),
height: 42,
timestamp: 1_000_000,
};
let program_hash = Bytes32([0xAAu8; 32]);
let root = Bytes32([0xBBu8; 32]);
let public_input = build_public_input(&[0x33u8; 32], &block);
let serving = ServingInputs {
retrieval_key: Bytes32([1u8; 32]),
roothash: root,
chunk_ciphertext: vec![vec![0xDE, 0xAD], vec![0xBE, 0xEF]],
};
let prover = Risc0Prover::new(sk, pk, block.clone());
Self {
program_hash,
root,
public_input,
block,
serving,
prover,
}
}
fn prove(&self) -> ExecutionProof {
self.prover
.prove(self.program_hash, &self.public_input, &self.serving)
.expect("risc0 proving must succeed")
}
fn chain(&self) -> MockChainSource {
MockChainSource::new(vec![self.block.clone()], self.block.timestamp + 100)
}
}
#[test]
fn risc0_prove_verify_smoke() {
let fx = Fixture::new();
let proof = fx.prove();
assert_eq!(proof.public_output, fx.serving.compute_public_output());
Risc0Verifier::default()
.verify(&proof, fx.program_hash, &[fx.root], &fx.chain())
.expect("risc0 proof must verify");
}
#[test]
fn risc0_tampered_proof_rejected() {
let fx = Fixture::new();
let mut proof = fx.prove();
proof.proof.truncate(proof.proof.len() / 2);
let err = Risc0Verifier::default()
.verify(&proof, fx.program_hash, &[fx.root], &fx.chain())
.unwrap_err();
assert!(
matches!(err, ProverError::ZkProofInvalid(_)),
"expected ZkProofInvalid, got {err:?}"
);
}
#[test]
fn risc0_wrong_program_hash_rejected() {
let fx = Fixture::new();
let proof = fx.prove();
let wrong_program_hash = Bytes32([0xCCu8; 32]);
let err = Risc0Verifier::default()
.verify(&proof, wrong_program_hash, &[fx.root], &fx.chain())
.unwrap_err();
assert!(
matches!(err, ProverError::ProgramHashMismatch { .. }),
"expected ProgramHashMismatch, got {err:?}"
);
}
#[test]
fn risc0_tampered_output_rejected() {
let fx = Fixture::new();
let mut proof = fx.prove();
proof.public_output = Bytes32([0xEEu8; 32]);
let err = Risc0Verifier::default()
.verify(&proof, fx.program_hash, &[fx.root], &fx.chain())
.unwrap_err();
assert!(
matches!(err, ProverError::PublicOutputMismatch),
"expected PublicOutputMismatch, got {err:?}"
);
}
#[test]
fn risc0_tampered_public_input_rejected() {
let fx = Fixture::new();
let mut proof = fx.prove();
proof.public_input = build_public_input(&[0x99u8; 32], &fx.block);
let err = Risc0Verifier::default()
.verify(&proof, fx.program_hash, &[fx.root], &fx.chain())
.unwrap_err();
assert!(
matches!(err, ProverError::PublicInputMismatch),
"expected PublicInputMismatch, got {err:?}"
);
}