Skip to content

Commit

Permalink
No more full proof
Browse files Browse the repository at this point in the history
Now there is a neat separation: proof (inner receipt) and pubs (journal)
  • Loading branch information
JackPiri committed Jun 6, 2024
1 parent e12b4c3 commit 19ee2d0
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 116 deletions.
19 changes: 9 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,23 @@ A verifier for [RISC-Zero](https://github.com/risc0/risc0) STARK proofs.
This crate provides a way for deserializing the proof and the verification key (aka image id) and a function to check if the proof is correct:

```rust
use risc0_verifier::{verify, extract_pubs_from_full_proof};
use risc0_verifier::{verify};
use serde::Deserialize;

#[derive(Deserialize)]
struct Data {
image_id: [u32; 8],
full_proof: String,
vk: [u32; 8],
proof: String,
pubs: String,
}

let Data {
image_id,
full_proof,
} = serde_json::from_reader(std::fs::File::open("./resources/valid_proof_1.json").unwrap()).unwrap();
let Data { vk, proof, pubs } =
serde_json::from_reader(std::fs::File::open("./resources/valid_proof_1.json").unwrap()).unwrap();

let full_proof = <Vec<u8>>::try_from(hex::decode(full_proof).unwrap()).unwrap();
let pubs = extract_pubs_from_full_proof(&full_proof).unwrap();
let proof = <Vec<u8>>::try_from(hex::decode(proof).unwrap()).unwrap();
let pubs = <Vec<u8>>::try_from(hex::decode(pubs).unwrap()).unwrap();

assert!(verify(image_id.into(), &full_proof, pubs).is_ok());
assert!(verify(vk.into(), &proof, &pubs).is_ok());
```

## Develop
Expand Down
2 changes: 1 addition & 1 deletion resources/guest_1/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ fn main() {
// read the input
let input: u32 = env::read();

let output = input + 1;
let output = input;

// write public output to the journal
env::commit(&output);
Expand Down
2 changes: 1 addition & 1 deletion resources/guest_2/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ fn main() {
// read the input
let input: u32 = env::read();

let output = input;
let output = input + 1;

// write public output to the journal
env::commit(&output);
Expand Down
21 changes: 11 additions & 10 deletions resources/valid_proof_1.json

Large diffs are not rendered by default.

21 changes: 11 additions & 10 deletions resources/valid_proof_2.json

Large diffs are not rendered by default.

47 changes: 16 additions & 31 deletions src/deserializer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,48 +13,33 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::proof::{FullProof, PublicInputs};
use risc0_zkvm::Receipt;
use risc0_zkvm::{InnerReceipt, Journal, Receipt};
use snafu::Snafu;

/// Deserialization error
#[derive(Debug, Snafu)]
pub enum DeserializeError {
#[snafu(display("Invalid data for deserialization"))]
InvalidData,
/// Invalid proof
#[snafu(display("Invalid proof for deserialization"))]
InvalidProof,
/// Invalid public inputs
#[snafu(display("Invalid public inputs for deserialization"))]
InvalidPublicInputs,
}

pub fn deserialize(byte_stream: &[u8]) -> Result<Receipt, DeserializeError> {
bincode::deserialize(byte_stream).map_err(|_x| DeserializeError::InvalidData)
pub fn deserialize_full_proof(proof: &[u8], pubs: &[u8]) -> Result<Receipt, DeserializeError> {
let inner_receipt_des = deserialize_proof(proof)?;
let journal_des = deserialize_pubs(pubs)?;
Ok(Receipt {
inner: inner_receipt_des,
journal: journal_des,
})
}

/// Extract public inputs from full proof
pub fn extract_pubs_from_full_proof(
full_proof: FullProof,
) -> Result<PublicInputs, DeserializeError> {
let receipt = deserialize(full_proof)?;

let mut pubs: PublicInputs = [0; 32];
let len = receipt.journal.bytes.len();
if len <= 32 {
pubs[..len].copy_from_slice(&receipt.journal.bytes[..len]);
} else {
return Err(DeserializeError::InvalidPublicInputs);
}

Ok(pubs)
fn deserialize_proof(proof: &[u8]) -> Result<InnerReceipt, DeserializeError> {
bincode::deserialize(proof).map_err(|_x| DeserializeError::InvalidProof)
}

pub fn extract_pubs_from_receipt(receipt: &Receipt) -> Result<PublicInputs, DeserializeError> {
let mut pubs: PublicInputs = [0; 32];
let len = receipt.journal.bytes.len();
if len <= 32 {
pubs[..len].copy_from_slice(&receipt.journal.bytes[..len]);
} else {
return Err(DeserializeError::InvalidPublicInputs);
}

Ok(pubs)
fn deserialize_pubs(pubs: &[u8]) -> Result<Journal, DeserializeError> {
bincode::deserialize(pubs).map_err(|_x| DeserializeError::InvalidPublicInputs)
}
31 changes: 12 additions & 19 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,23 @@ mod deserializer;
mod key;
mod proof;

pub use deserializer::extract_pubs_from_full_proof;
use deserializer::{deserialize, extract_pubs_from_receipt, DeserializeError};
use deserializer::deserialize_full_proof;
pub use deserializer::DeserializeError;
pub use key::Vk;
pub use proof::{FullProof, PublicInputs};
pub use proof::{Proof, PublicInputs};
use snafu::Snafu;

/// Deserialization error.
#[derive(Debug, Snafu)]
pub enum VerifyError {
/// Invalid data (not deserializable)
/// Invalid data
#[snafu(display("Invalid data for verification: [{}]", cause))]
InvalidData {
/// Internal error
#[snafu(source)]
cause: DeserializeError,
},
/// Mismatching public inputs
#[snafu(display("Mismatching public inputs"))]
MismatchingPublicInputs,
/// Verification failure
/// Failure
#[snafu(display("Failed to verify: [{}]", cause))]
Failure {
/// Internal error
Expand All @@ -60,16 +57,12 @@ impl From<risc0_zkp::verify::VerificationError> for VerifyError {
}
}

/// Verify the given proof and public inputs `full_proof` using verification key `image_id`.
/// Verify the given proof `proof` and public inputs `pubs` using verification key `vk`.
/// Use the given verification key `vk` to verify the proof `proof` against the public inputs `pubs`.
/// Can fail if:
/// - the full proof is not serializable as a `risc0_zkvm::Receipt`
/// - the receipt is not valid for the given verification key
pub fn verify(image_id: Vk, full_proof: FullProof, pubs: PublicInputs) -> Result<(), VerifyError> {
let receipt = deserialize(full_proof)?;
let extracted_pubs = extract_pubs_from_receipt(&receipt)?;
if pubs == extracted_pubs {
receipt.verify(image_id.0).map_err(Into::into)
} else {
Err(VerifyError::MismatchingPublicInputs)
}
/// - the proof or the pubs are not serializable respectively as a `risc0_zkvm::InnerReceipt` and a `risc0_zkvm::Journal`
/// - the proof is not valid
pub fn verify(vk: Vk, proof: Proof, pubs: PublicInputs) -> Result<(), VerifyError> {
let receipt = deserialize_full_proof(proof, pubs)?;
receipt.verify(vk.0).map_err(Into::into)
}
9 changes: 6 additions & 3 deletions src/proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.

/// The full proof (a serialized `risc0_zkvm::Receipt`) containing both proof raw data and public inputs
pub type FullProof<'a> = &'a [u8];
/// The full proof (a serialized `risc0_zkvm::Receipt`) containing both proof and public inputs
pub type _FullProof<'a> = &'a [u8];

/// The proof (a serialized `risc0_zkvm::InnerReceipt`)
pub type Proof<'a> = &'a [u8];

/// The public inputs (a serialized `risc0_zkvm::Journal`)
pub type PublicInputs = [u8; 32];
pub type PublicInputs<'a> = &'a [u8];
61 changes: 30 additions & 31 deletions tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,74 +13,73 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use risc0_verifier::{extract_pubs_from_full_proof, verify, VerifyError};
use risc0_verifier::{verify, DeserializeError, VerifyError};
use rstest::rstest;
use serde::Deserialize;
use std::path::{Path, PathBuf};

fn load_data(path: &Path) -> ([u32; 8], Vec<u8>, [u8; 32]) {
fn load_data(path: &Path) -> ([u32; 8], Vec<u8>, Vec<u8>) {
#[derive(Deserialize)]
struct Data {
image_id: [u32; 8],
full_proof: String,
vk: [u32; 8],
proof: String,
pubs: String,
}

let Data {
image_id,
full_proof,
} = serde_json::from_reader(std::fs::File::open(path).unwrap()).unwrap();
let Data { vk, proof, pubs } =
serde_json::from_reader(std::fs::File::open(path).unwrap()).unwrap();

let full_proof = <Vec<u8>>::try_from(hex::decode(full_proof).unwrap()).unwrap();
let pubs = extract_pubs_from_full_proof(&full_proof).unwrap();
let proof = <Vec<u8>>::try_from(hex::decode(proof).unwrap()).unwrap();
let pubs = <Vec<u8>>::try_from(hex::decode(pubs).unwrap()).unwrap();

(image_id, full_proof, pubs)
(vk, proof, pubs)
}

#[rstest]
fn should_verify_valid_proof(#[files("./resources/valid_proof_*.json")] path: PathBuf) {
let (image_id, full_proof, pubs) = load_data(&path);
let (vk, proof, pubs) = load_data(&path);

assert!(verify(image_id.into(), &full_proof, pubs).is_ok());
assert!(verify(vk.into(), &proof, &pubs).is_ok());
}

#[test]
fn should_not_verify_invalid_proof() {
let (image_id, mut full_proof, pubs) = load_data(Path::new("./resources/valid_proof_1.json"));
let (vk, mut proof, pubs) = load_data(Path::new("./resources/valid_proof_1.json"));

full_proof[0] = full_proof.first().unwrap().wrapping_add(1);
proof[0] = proof.first().unwrap().wrapping_add(1);

assert!(matches!(
verify(image_id.into(), &full_proof, pubs),
Err(VerifyError::InvalidData { .. })
verify(vk.into(), &proof, &pubs),
Err(VerifyError::InvalidData {
cause: DeserializeError::InvalidProof
})
));
}

#[test]
fn should_not_verify_mismatching_inputs() {
let (image_id, mut full_proof, pubs) = load_data(Path::new("./resources/valid_proof_1.json"));
fn should_not_verify_invalid_pubs() {
let (vk, proof, mut pubs) = load_data(Path::new("./resources/valid_proof_1.json"));

let len = full_proof.len();
full_proof[len - 1] = full_proof.last().unwrap().wrapping_add(1);
pubs[0] = pubs.first().unwrap().wrapping_add(1);

assert!(matches!(
verify(image_id.into(), &full_proof, pubs),
Err(VerifyError::MismatchingPublicInputs { .. })
verify(vk.into(), &proof, &pubs),
Err(VerifyError::InvalidData {
cause: DeserializeError::InvalidPublicInputs
})
));
}

#[test]
fn should_not_verify_false_proof() {
let (image_id, mut full_proof, mut pubs) =
load_data(Path::new("./resources/valid_proof_1.json"));
let (vk, proof, mut pubs) = load_data(Path::new("./resources/valid_proof_1.json"));

// we know journal.bytes for that proof is 4 bytes
let journal_bytes_size = 4;
let len = full_proof.len();
full_proof[len - journal_bytes_size] = full_proof[len - journal_bytes_size].wrapping_add(1);
pubs[0] = pubs[0].wrapping_add(1);
let len = pubs.len();

pubs[len - 1] = pubs.last().unwrap().wrapping_add(1);

assert!(matches!(
verify(image_id.into(), &full_proof, pubs),
verify(vk.into(), &proof, &pubs),
Err(VerifyError::Failure { .. })
));
}

0 comments on commit 19ee2d0

Please sign in to comment.