Skip to content

Commit

Permalink
Merge pull request #2 from HorizenLabs/gp/public_inputs
Browse files Browse the repository at this point in the history
Explicit handling of public inputs
  • Loading branch information
JackPiri authored Jun 10, 2024
2 parents b912231 + bbac31b commit 4b814a3
Show file tree
Hide file tree
Showing 13 changed files with 153 additions and 94 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/target
/Cargo.lock
lcov.info
9 changes: 3 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,13 @@ description = "A Rust library to verify risc0 STARK proofs"
keywords = ["crypto", "no-std", "blockchain", "cryptography", "risc0"]

[dependencies]
risc0-zkvm = { version = "0.21.0", default-features = false }
risc0-zkp = "0.21.0"
risc0-zkvm = { version = "1.0.1", default-features = false }
risc0-zkp = "1.0.1"
bincode = "1.3"
snafu = { version = "0.7.5", default-features = false }
serde = "1.0.197"

[dev-dependencies]
serde_json = "1.0.114"
serde = "1.0.197"
hex = "0.4.3"
rstest = "0.20.0"

[features]
rstest = "0.20.0"
17 changes: 8 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,18 @@ This crate provides a way for deserializing the proof and the verification key (

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

let Data {
proof_raw_data,
image_id,
} = 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 proof_raw_data = <Vec<u8>>::try_from(hex::decode(proof_raw_data).unwrap()).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(&proof_raw_data, image_id.into()).is_ok());
assert!(verify(vk.into(), &proof, &pubs).is_ok());
```

## Develop
Expand Down
8 changes: 1 addition & 7 deletions resources/guest_1/main.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
#![no_main]
// If you want to try std support, also update the guest Cargo.toml file
#![no_std] // std support is experimental

use risc0_zkvm::guest::env;

risc0_zkvm::guest::entry!(main);

fn main() {
// TODO: Implement your guest code here

// 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
8 changes: 1 addition & 7 deletions resources/guest_2/main.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
#![no_main]
// If you want to try std support, also update the guest Cargo.toml file
#![no_std] // std support is experimental

use risc0_zkvm::guest::env;

risc0_zkvm::guest::entry!(main);

fn main() {
// TODO: Implement your guest code here

// 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
1 change: 1 addition & 0 deletions resources/info.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
using cargo-risczero 1.0.1
23 changes: 12 additions & 11 deletions resources/valid_proof_1.json

Large diffs are not rendered by default.

23 changes: 12 additions & 11 deletions resources/valid_proof_2.json

Large diffs are not rendered by default.

27 changes: 19 additions & 8 deletions src/deserializer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,30 @@
// See the License for the specific language governing permissions and
// limitations under the License.

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: [{:?}...{:?}]", first, last))]
InvalidData { first: Option<u8>, last: Option<u8> },
/// 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 {
first: byte_stream.first().copied(),
last: byte_stream.last().copied(),
})
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::new(inner_receipt_des, journal_des.bytes))
}

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

fn deserialize_pubs(pubs: &[u8]) -> Result<Journal, DeserializeError> {
bincode::deserialize(pubs).map_err(|_x| DeserializeError::InvalidPublicInputs)
}
40 changes: 40 additions & 0 deletions src/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,43 @@ impl From<[u32; risc0_zkp::core::digest::DIGEST_WORDS]> for Vk {
Self(value.into())
}
}

impl From<[u8; risc0_zkp::core::digest::DIGEST_BYTES]> for Vk {
fn from(value: [u8; risc0_zkp::core::digest::DIGEST_BYTES]) -> Self {
// Initialize with zeros
let mut value_u32: [u32; risc0_zkp::core::digest::DIGEST_WORDS] =
[0; risc0_zkp::core::digest::DIGEST_WORDS];
// Iterate over chunks of 4 bytes and convert them to u32
for (i, chunk) in value.chunks_exact(4).enumerate() {
let mut single_value: u32 = 0;
for (j, &byte) in chunk.iter().enumerate() {
single_value |= (byte as u32) << (8 * j);
}
value_u32[i] = single_value;
}
Self(value_u32.into())
}
}

#[cfg(test)]
mod tests {
use super::Vk;

#[test]
fn should_have_same_from_result() {
let vu32: [u32; 8] = [
1067704626, 3452143673, 166143985, 2720203724, 4153258584, 3584210768, 3821389021,
2575106175,
];
let vu8: [u8; 32] = [
0x32, 0xe1, 0xa3, 0x3f, 0x39, 0x88, 0xc3, 0xcd, 0xf1, 0x27, 0xe7, 0x09, 0xcc, 0x03,
0x23, 0xa2, 0x58, 0xb2, 0x8d, 0xf7, 0x50, 0xb7, 0xa2, 0xd5, 0xdd, 0xc4, 0xc5, 0xe3,
0x7f, 0x00, 0x7d, 0x99,
];

let vu32: Vk = vu32.into();
let vu8: Vk = vu8.into();

assert!(vu32.0.eq(&vu8.0));
}
}
22 changes: 12 additions & 10 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,23 @@ mod deserializer;
mod key;
mod proof;

use deserializer::{deserialize, DeserializeError};
use deserializer::deserialize_full_proof;
pub use deserializer::DeserializeError;
pub use key::Vk;
pub use proof::ProofRawData;
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,
},
/// Verification failure
/// Failure
#[snafu(display("Failed to verify: [{}]", cause))]
Failure {
/// Internal error
Expand All @@ -56,11 +57,12 @@ impl From<risc0_zkp::verify::VerificationError> for VerifyError {
}
}

/// Verify the given proof raw data `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 raw proof data is not serializable as a `risc0_zkvm::Receipt`
/// - the receipt is not valid for the given verification key
pub fn verify(raw_proof_data: ProofRawData, image_id: Vk) -> Result<(), VerifyError> {
let receipt = deserialize(raw_proof_data)?;
receipt.verify(image_id.0).map_err(Into::into)
/// - 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)
}
7 changes: 5 additions & 2 deletions src/proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

/// The proof raw data (a serialized `risc0_zkvm::Receipt`).
pub type ProofRawData<'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<'a> = &'a [u8];
61 changes: 38 additions & 23 deletions tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,58 +13,73 @@
// See the License for the specific language governing permissions and
// limitations under the License.

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

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

let Data {
proof_raw_data,
image_id,
} = 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 proof_raw_data = <Vec<u8>>::try_from(hex::decode(proof_raw_data).unwrap()).unwrap();
let proof = <Vec<u8>>::try_from(hex::decode(proof).unwrap()).unwrap();
let pubs = <Vec<u8>>::try_from(hex::decode(pubs).unwrap()).unwrap();

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

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

assert!(verify(&proof_raw_data, image_id_data.into()).is_ok());
assert!(verify(vk.into(), &proof, &pubs).is_ok());
}

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

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

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

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

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

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

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

let len = pubs.len();

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

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

0 comments on commit 4b814a3

Please sign in to comment.