use std::pin::Pin;
use sp1_core_machine::io::SP1Stdin;
use sp1_prover::{
worker::{SP1LightNode, SP1NodeCore},
Groth16Bn254Proof, PlonkBn254Proof, SP1VerifyingKey,
};
use crate::{
proof::verify_mock_public_inputs,
prover::{BaseProveRequest, ProveRequest},
Prover, SP1Proof, SP1ProofWithPublicValues, SP1ProvingKey, SP1VerificationError, StatusCode,
};
use sp1_core_executor::SP1CoreOpts;
use std::future::{Future, IntoFuture};
#[derive(Clone)]
pub struct MockProver {
inner: SP1LightNode,
}
impl MockProver {
#[must_use]
pub async fn new() -> Self {
Self { inner: SP1LightNode::new().await }
}
#[must_use]
pub async fn new_with_opts(opts: SP1CoreOpts) -> Self {
Self { inner: SP1LightNode::with_opts(opts).await }
}
}
impl Prover for MockProver {
type ProvingKey = SP1ProvingKey;
type Error = anyhow::Error;
type ProveRequest<'a> = MockProveRequest<'a>;
fn inner(&self) -> &SP1NodeCore {
self.inner.inner()
}
fn prove<'a>(&'a self, pk: &'a Self::ProvingKey, stdin: SP1Stdin) -> Self::ProveRequest<'a> {
MockProveRequest { base: BaseProveRequest::new(self, pk, stdin) }
}
fn setup(
&self,
elf: sp1_build::Elf,
) -> impl crate::prover::SendFutureResult<Self::ProvingKey, Self::Error> {
async move {
let vk = self.inner.setup(&elf).await?;
let pk = SP1ProvingKey { vk, elf };
Ok(pk)
}
}
fn verify(
&self,
proof: &SP1ProofWithPublicValues,
vkey: &SP1VerifyingKey,
_status_code: Option<StatusCode>,
) -> Result<(), SP1VerificationError> {
match &proof.proof {
SP1Proof::Plonk(PlonkBn254Proof { public_inputs, .. }) => {
verify_mock_public_inputs(vkey, &proof.public_values, public_inputs)
.map_err(SP1VerificationError::Plonk)
}
SP1Proof::Groth16(Groth16Bn254Proof { public_inputs, .. }) => {
verify_mock_public_inputs(vkey, &proof.public_values, public_inputs)
.map_err(SP1VerificationError::Groth16)
}
_ => Ok(()),
}
}
}
pub struct MockProveRequest<'a> {
pub(crate) base: BaseProveRequest<'a, MockProver>,
}
impl<'a> ProveRequest<'a, MockProver> for MockProveRequest<'a> {
fn base(&mut self) -> &mut BaseProveRequest<'a, MockProver> {
&mut self.base
}
}
impl<'a> IntoFuture for MockProveRequest<'a> {
type Output = Result<SP1ProofWithPublicValues, anyhow::Error>;
type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send + 'a>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(async move {
let BaseProveRequest { prover, pk, mode, stdin, context_builder } = self.base;
let mut req = prover.execute(pk.elf.clone(), stdin);
req.context_builder = context_builder;
let (public_values, _) = req.await?;
Ok(SP1ProofWithPublicValues::create_mock_proof(
&pk.vk,
public_values,
mode,
prover.version(),
))
})
}
}
#[cfg(test)]
mod tests {
use crate::{prover::ProveRequest, utils::setup_logger, MockProver, Prover, SP1Stdin};
#[tokio::test]
async fn test_mock_proof_all_types() {
setup_logger();
let prover = MockProver::new().await;
let pk =
prover.setup(test_artifacts::FIBONACCI_ELF).await.expect("failed to setup proving key");
let mut stdin = SP1Stdin::new();
stdin.write(&10usize);
let core_proof =
prover.prove(&pk, stdin).core().await.expect("failed to create mock Core proof");
prover.verify(&core_proof, &pk.vk, None).expect("failed to verify mock Core proof");
let mut stdin = SP1Stdin::new();
stdin.write(&10usize);
let compressed_proof = prover
.prove(&pk, stdin)
.compressed()
.await
.expect("failed to create mock Compressed proof");
prover
.verify(&compressed_proof, &pk.vk, None)
.expect("failed to verify mock Compressed proof");
let mut stdin = SP1Stdin::new();
stdin.write(&10usize);
let plonk_proof =
prover.prove(&pk, stdin).plonk().await.expect("failed to create mock Plonk proof");
prover.verify(&plonk_proof, &pk.vk, None).expect("failed to verify mock Plonk proof");
let mut stdin = SP1Stdin::new();
stdin.write(&10usize);
let groth16_proof =
prover.prove(&pk, stdin).groth16().await.expect("failed to create mock Groth16 proof");
prover.verify(&groth16_proof, &pk.vk, None).expect("failed to verify mock Groth16 proof");
}
#[tokio::test]
async fn test_mock_proof_public_values() {
setup_logger();
let prover = MockProver::new().await;
let pk =
prover.setup(test_artifacts::FIBONACCI_ELF).await.expect("failed to setup proving key");
let mut stdin = SP1Stdin::new();
stdin.write(&10usize);
let (expected_pv, _) =
prover.execute(pk.elf.clone(), stdin.clone()).await.expect("failed to execute program");
let proof =
prover.prove(&pk, stdin).core().await.expect("failed to create mock Core proof");
assert_eq!(proof.public_values.as_slice(), expected_pv.as_slice());
}
#[tokio::test]
async fn test_mock_plonk_proof_wrong_vkey_fails() {
setup_logger();
let prover = MockProver::new().await;
let pk1 = prover
.setup(test_artifacts::FIBONACCI_ELF)
.await
.expect("failed to setup proving key 1");
let pk2 = prover
.setup(test_artifacts::HELLO_WORLD_ELF)
.await
.expect("failed to setup proving key 2");
let mut stdin = SP1Stdin::new();
stdin.write(&10usize);
let proof =
prover.prove(&pk1, stdin).plonk().await.expect("failed to create mock Plonk proof");
let result = prover.verify(&proof, &pk2.vk, None);
assert!(result.is_err(), "Verification should fail with wrong vkey");
}
#[tokio::test]
async fn test_mock_groth16_proof_wrong_vkey_fails() {
setup_logger();
let prover = MockProver::new().await;
let pk1 = prover
.setup(test_artifacts::FIBONACCI_ELF)
.await
.expect("failed to setup proving key 1");
let pk2 = prover
.setup(test_artifacts::HELLO_WORLD_ELF)
.await
.expect("failed to setup proving key 2");
let mut stdin = SP1Stdin::new();
stdin.write(&10usize);
let proof =
prover.prove(&pk1, stdin).groth16().await.expect("failed to create mock Groth16 proof");
let result = prover.verify(&proof, &pk2.vk, None);
assert!(result.is_err(), "Verification should fail with wrong vkey");
}
#[tokio::test]
async fn test_mock_plonk_proof_tampered_public_values_fails() {
setup_logger();
let prover = MockProver::new().await;
let pk =
prover.setup(test_artifacts::FIBONACCI_ELF).await.expect("failed to setup proving key");
let mut stdin = SP1Stdin::new();
stdin.write(&10usize);
let mut proof =
prover.prove(&pk, stdin).plonk().await.expect("failed to create mock Plonk proof");
proof.public_values = sp1_primitives::io::SP1PublicValues::from(&[0xDE, 0xAD, 0xBE, 0xEF]);
let result = prover.verify(&proof, &pk.vk, None);
assert!(result.is_err(), "Verification should fail with tampered public values");
}
#[tokio::test]
async fn test_mock_groth16_proof_tampered_public_values_fails() {
setup_logger();
let prover = MockProver::new().await;
let pk =
prover.setup(test_artifacts::FIBONACCI_ELF).await.expect("failed to setup proving key");
let mut stdin = SP1Stdin::new();
stdin.write(&10usize);
let mut proof =
prover.prove(&pk, stdin).groth16().await.expect("failed to create mock Groth16 proof");
proof.public_values = sp1_primitives::io::SP1PublicValues::from(&[0xDE, 0xAD, 0xBE, 0xEF]);
let result = prover.verify(&proof, &pk.vk, None);
assert!(result.is_err(), "Verification should fail with tampered public values");
}
}