Skip to content

Commit

Permalink
added hashing the public values
Browse files Browse the repository at this point in the history
  • Loading branch information
yuwen01 committed Oct 11, 2024
1 parent 0119dd9 commit a7739c7
Show file tree
Hide file tree
Showing 9 changed files with 67 additions and 33 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 12 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,23 @@ exclude = ["example/sp1-program"]
resolver = "2"

[workspace.dependencies]
borsh = "1.5.1"
solana-program = { git = "https://github.com/anza-xyz/agave", rev = "6e62af0f0de6a40e4e22628cbbcf63b1a6da560e" }

# workspace
sp1-solana = { path = "verifier" }
num-bigint = { version = "0.4.6" }
example-solana-contract = { path = "example/program" }

# solana
solana-program = { git = "https://github.com/anza-xyz/agave", rev = "6e62af0f0de6a40e4e22628cbbcf63b1a6da560e" }
solana-program-test = { git = "https://github.com/anza-xyz/agave", rev = "6e62af0f0de6a40e4e22628cbbcf63b1a6da560e" }
solana-sdk = { git = "https://github.com/anza-xyz/agave", rev = "6e62af0f0de6a40e4e22628cbbcf63b1a6da560e" }
solana-inline-spl = { git = "https://github.com/anza-xyz/agave", rev = "6e62af0f0de6a40e4e22628cbbcf63b1a6da560e" }
borsh = "1.5.1"

# misc
num-bigint = "0.4.6"
num-traits = "0.2.19"
tokio = "1.40.0"
sha2 = "0.10.8"


[patch.crates-io]
# See https://github.com/anza-xyz/agave/blob/master/Cargo.toml#L551-L586
Expand Down
4 changes: 3 additions & 1 deletion example/program/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ default = []
borsh.workspace = true
solana-program.workspace = true
sp1-solana.workspace = true
num-bigint.workspace = true
sha2.workspace = true

hex = "0.4.3"
hex-literal = "0.3.1"
num-traits = "0.2.19"
num-bigint.workspace = true
9 changes: 7 additions & 2 deletions example/program/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use borsh::BorshDeserialize;
use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, pubkey::Pubkey};
use sp1_solana::{verify_proof_fixture, SP1ProofFixture};
use sp1_solana::{hash_public_inputs, verify_proof_fixture, SP1ProofFixture};

#[cfg(not(feature = "no-entrypoint"))]
use solana_program::entrypoint;
Expand Down Expand Up @@ -29,7 +29,12 @@ pub fn process_instruction(
assert!(result.is_ok());

// Make sure that we're verifying a fibonacci program.
assert_eq!(fixture.vkey_hash(), FIBONACCI_VKEY_HASH);
assert_eq!(&fixture.vkey_hash(), FIBONACCI_VKEY_HASH);

if let Some(sp1_public_inputs) = &fixture.sp1_public_inputs {
let digest = hash_public_inputs(sp1_public_inputs);
assert_eq!(digest, fixture.commited_values_digest());
}

Ok(())
}
2 changes: 1 addition & 1 deletion example/script/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ async fn main() {

// Load the proof from the file, and convert it to a fixture.
let sp1_proof_with_public_values = SP1ProofWithPublicValues::load(&proof_file).unwrap();
let fixture = SP1ProofFixture::from(sp1_proof_with_public_values);
let fixture = SP1ProofFixture::from_sp1(sp1_proof_with_public_values, true);
let fixture_file = "../../proof-fixtures/fibonacci_fixture.bin";
fixture.save(&fixture_file).unwrap();

Expand Down
Binary file modified proof-fixtures/fibonacci_fixture.bin
Binary file not shown.
2 changes: 1 addition & 1 deletion verifier/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ categories = ["cryptography"]
[dependencies]
borsh.workspace = true
num-bigint.workspace = true
sha2.workspace = true

ark-bn254 = "0.4.0"
ark-serialize = "0.4.2"
ark-ff = "0.4.2"
sha2 = { version = "0.10.8"}
# groth16-solana = { git = "https://github.com/sp1-patches/groth16-solana", branch = "patch-v0.0.3" }
groth16-solana = { path = "../../groth16-solana-patch" }
thiserror = "1.0.63"
Expand Down
60 changes: 38 additions & 22 deletions verifier/src/fixture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use std::{

use crate::{verify_proof_raw, Error};
use borsh::{BorshDeserialize, BorshSerialize};
use num_bigint::BigUint;
use sha2::{Digest, Sha256};

/// The necessary information for a solana program to verify an SP1 Groth16 proof.
Expand All @@ -18,6 +17,8 @@ pub struct SP1ProofFixture {
pub public_inputs: [u8; 63],
/// The first 4 bytes of the Groth16 vkey hash.
pub groth16_vkey_hash: [u8; 4],
/// The public inputs of the underlying SP1 program.
pub sp1_public_inputs: Option<Vec<u8>>,
}

impl SP1ProofFixture {
Expand All @@ -40,17 +41,8 @@ impl SP1ProofFixture {
}

/// Retrieves the SP1 commited values digest from the public inputs.
pub fn commited_values_digest(&self) -> String {
// The committed values digest is the second half of the public inputs
let digest_bytes = &self.public_inputs[31..63];

// Convert the digest bytes to a BigUint
let digest_biguint = BigUint::from_bytes_be(digest_bytes);

// Convert the BigUint to a decimal string
let digest_string = digest_biguint.to_str_radix(10);

digest_string
pub fn commited_values_digest(&self) -> [u8; 32] {
self.public_inputs[31..63].try_into().unwrap()
}

/// Retrieves the SP1 vkey hash from the public inputs.
Expand All @@ -67,6 +59,16 @@ impl SP1ProofFixture {
}
}

/// Hashes the public inputs in order to match the groth16 verifier's format
pub fn hash_public_inputs(public_inputs: &[u8]) -> [u8; 32] {
let mut result = Sha256::digest(public_inputs);

// Zero out the first 3 bits.
result[0] = result[0] & 0x1F;

result.into()
}

/// Verify a proof using a [`SP1ProofFixture`].
///
/// Checks the Groth16 vkey hash in the fixture against the provided vk.
Expand All @@ -92,39 +94,51 @@ mod sp1_serialize {

use super::SP1ProofFixture;
/// Convert a SP1ProofWithPublicValues to a SP1ProofFixture.
impl From<SP1ProofWithPublicValues> for SP1ProofFixture {
fn from(sp1_proof_with_public_values: SP1ProofWithPublicValues) -> Self {
impl SP1ProofFixture {
pub fn from_sp1(
sp1_proof_with_public_values: SP1ProofWithPublicValues,
use_public_values: bool,
) -> Self {
let proof = sp1_proof_with_public_values
.proof
.try_as_groth_16()
.expect("Failed to convert proof to Groth16 proof");

let raw_proof = hex::decode(proof.raw_proof).unwrap();

// Convert public inputs to byte representations.
// Convert public inputs and vkey hash to bytes.
let vkey_hash = BigUint::from_str_radix(&proof.public_inputs[0], 10)
.unwrap()
.to_bytes_be();

let committed_values_digest = BigUint::from_str_radix(&proof.public_inputs[1], 10)
.unwrap()
.to_bytes_be();

let public_inputs = [vkey_hash.to_vec(), committed_values_digest.to_vec()].concat();

let raw_public_values = if use_public_values {
Some(sp1_proof_with_public_values.public_values.to_vec()) // TODO: get rid of this clone
} else {
None
};

SP1ProofFixture {
proof: raw_proof[..256].try_into().unwrap(),
public_inputs: public_inputs.try_into().unwrap(),
groth16_vkey_hash: proof.groth16_vkey_hash[..4].try_into().unwrap(),
sp1_public_inputs: raw_public_values,
}
}
}

#[cfg(feature = "sp1-serialize")]
#[test]
fn test_public_inputs() {
// Read the serialized SP1ProofWithPublicValues from the file.

use crate::hash_public_inputs;
use std::str::FromStr;

// Read the serialized SP1ProofWithPublicValues from the file.
let sp1_proof_with_public_values_file = "../proofs/fibonacci_proof.bin";
let sp1_proof_with_public_values =
SP1ProofWithPublicValues::load(&sp1_proof_with_public_values_file).unwrap();
Expand All @@ -135,23 +149,25 @@ mod sp1_serialize {
.try_as_groth_16()
.unwrap();

// Convert vkey_hash from base 10 to hex using the hex crate.
let vkey_hash = &groth16_proof.public_inputs[0];
// Convert vkey_hash from base 10 to hex using the hex crate
let vkey_hash_biguint = BigUint::from_str(vkey_hash).unwrap();
let mut vkey_hash_bytes = vec![0];
vkey_hash_bytes.extend_from_slice(&vkey_hash_biguint.to_bytes_be());
let vkey_hash_hex = hex::encode(vkey_hash_bytes);

let commited_values_digest = &groth16_proof.public_inputs[1];
// let commited_values_digest: &String = &groth16_proof.public_inputs[1];

// Convert the SP1ProofWithPublicValues to a SP1ProofFixture.
let fixture = SP1ProofFixture::from(sp1_proof_with_public_values);
let fixture = SP1ProofFixture::from_sp1(sp1_proof_with_public_values, false);

// Verify the public inputs.
assert_eq!(
fixture.commited_values_digest(),
commited_values_digest.to_string()
&fixture.commited_values_digest(),
&hash_public_inputs(fixture.sp1_public_inputs.as_ref().unwrap())
);

// Verify the vkey hash.
assert_eq!(fixture.vkey_hash(), vkey_hash_hex);
}
}
5 changes: 3 additions & 2 deletions verifier/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ use groth16_solana::groth16::Groth16Verifyingkey;
use thiserror::Error;

mod fixture;
pub use fixture::hash_public_inputs;
pub use fixture::verify_proof_fixture;
pub use fixture::SP1ProofFixture;

pub const GROTH16_VK_BYTES: &[u8] = include_bytes!("../vk/groth16_vk.bin");

/// Convert the endianness of a byte array, chunk by chunk.
///
/// Taken from https://github.com/anza-xyz/agave/blob/c54d840/curves/bn254/src/compression.rs#L176-L189
Expand All @@ -26,8 +29,6 @@ fn convert_endianness<const CHUNK_SIZE: usize, const ARRAY_SIZE: usize>(
reversed
}

pub const GROTH16_VK_BYTES: &[u8] = include_bytes!("../vk/groth16_vk.bin");

#[derive(Error, Debug)]
pub enum Error {
#[error("G1 compression error")]
Expand Down

0 comments on commit a7739c7

Please sign in to comment.