pub(crate) fn verify_proof<C: SP1ProverComponents>(
    prover: &SP1Prover<C>,
    version: &str,
    bundle: &SP1ProofWithPublicValues,
    vkey: &SP1VerifyingKey,
) -> Result<(), SP1VerificationError> {
    // Check that the SP1 version matches the version of the currentcircuit.
    if bundle.sp1_version != version {
        return Err(SP1VerificationError::VersionMismatch(bundle.sp1_version.clone()));
    }

    match &bundle.proof {
        SP1Proof::Core(proof) => {
            let public_values: &PublicValues<Word<_>, _> =
                proof.last().unwrap().public_values.as_slice().borrow();

            // Get the committed value digest bytes.
            let committed_value_digest_bytes = public_values
                .committed_value_digest
                .iter()
                .flat_map(|w| w.0.iter().map(|x| x.as_canonical_u32() as u8))
                .collect_vec();

            // Make sure the committed value digest matches the public values hash.
            // It is computationally infeasible to find two distinct inputs, one processed with
            // SHA256 and the other with Blake3, that yield the same hash value.
            if committed_value_digest_bytes != bundle.public_values.hash() &&
                committed_value_digest_bytes != bundle.public_values.blake3_hash()
            {
                return Err(SP1VerificationError::InvalidPublicValues);
            }

            // Verify the core proof.
            prover
                .verify(&SP1CoreProofData(proof.clone()), vkey)
                .map_err(SP1VerificationError::Core)
        }
        SP1Proof::Compressed(proof) => {
            let public_values: &PublicValues<Word<_>, _> =
                proof.proof.public_values.as_slice().borrow();

            // Get the committed value digest bytes.
            let committed_value_digest_bytes = public_values
                .committed_value_digest
                .iter()
                .flat_map(|w| w.0.iter().map(|x| x.as_canonical_u32() as u8))
                .collect_vec();

            // Make sure the committed value digest matches the public values hash.
            // It is computationally infeasible to find two distinct inputs, one processed with
            // SHA256 and the other with Blake3, that yield the same hash value.
            if committed_value_digest_bytes != bundle.public_values.hash() &&
                committed_value_digest_bytes != bundle.public_values.blake3_hash()
            {
                return Err(SP1VerificationError::InvalidPublicValues);
            }

            prover.verify_compressed(proof, vkey).map_err(SP1VerificationError::Recursion)
        }
        SP1Proof::Plonk(proof) => prover
            .verify_plonk_bn254(
                proof,
                vkey,
                &bundle.public_values,
                &if sp1_prover::build::sp1_dev_mode() {
                    sp1_prover::build::plonk_bn254_artifacts_dev_dir()
                } else {
                    try_install_circuit_artifacts("plonk")
                },
            )
            .map_err(SP1VerificationError::Plonk),
        SP1Proof::Groth16(proof) => prover
            .verify_groth16_bn254(
                proof,
                vkey,
                &bundle.public_values,
                &if sp1_prover::build::sp1_dev_mode() {
                    sp1_prover::build::groth16_bn254_artifacts_dev_dir()
                } else {
                    try_install_circuit_artifacts("groth16")
                },
            )
            .map_err(SP1VerificationError::Groth16),
    }
}
In SP1, a proof's public values can either be hashed with SHA2 or Blake3. In SP1 V4, there is no
metadata attached to the proof about which hasher function was used for public values hashing.
Instead, when verifying the proof, the public values are hashed with SHA2 and Blake3, and
if either matches the `expected_public_values_hash`, the verification is successful.

The security for this verification in SP1 V4 derives from the fact that both SHA2 and Blake3 are
designed to be collision resistant. It is computationally infeasible to find an input i1 for
SHA256 and an input i2 for Blake3 that the same hash value. Doing so would require breaking both
algorithms simultaneously.
crates/sdk/src/prover.rs