From 027f4f6ffc6867ac5d211dfeecd1da3ce037dd87 Mon Sep 17 00:00:00 2001 From: Yuwen Zhang Date: Fri, 11 Oct 2024 11:27:30 -0700 Subject: [PATCH] first round fixes --- .github/workflows/pr.yml | 3 +- Cargo.lock | 45 ++++++------ Cargo.toml | 2 +- example/README.md | 5 +- example/program/Cargo.toml | 2 +- example/program/src/lib.rs | 23 ++---- example/script/Cargo.toml | 2 +- example/script/build.rs | 12 +--- example/script/src/main.rs | 6 +- proof-fixtures/fibonacci_fixture.bin | Bin 340 -> 308 bytes verifier/Cargo.toml | 4 +- verifier/src/fixture.rs | 102 ++++++++++++--------------- 12 files changed, 87 insertions(+), 119 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 70815aa..72c9d6c 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -46,8 +46,7 @@ jobs: - name: "Update lock files" run: | cargo tree - (cd ./example/program && cargo tree) - (cd ./example/script && cargo tree) + (cd ./example/sp1-program && cargo tree) - name: "Assert no changes" run: | diff --git a/Cargo.lock b/Cargo.lock index 77cb013..f15cee8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1126,9 +1126,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.28" +version = "1.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1" +checksum = "58e804ac3194a48bb129643eb1d62fcc20d18c6b8c181704489353d13120bcd1" dependencies = [ "jobserver", "libc", @@ -2394,7 +2394,7 @@ version = "0.1.0" dependencies = [ "borsh 1.5.1", "clap", - "example-solana-contract", + "fibonacci-verifier-contract", "solana-program-test", "solana-sdk", "sp1-build", @@ -2403,20 +2403,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "example-solana-contract" -version = "0.1.0" -dependencies = [ - "borsh 1.5.1", - "hex", - "hex-literal 0.3.4", - "num-bigint 0.4.6", - "num-traits", - "sha2 0.10.8", - "solana-program", - "sp1-solana", -] - [[package]] name = "eyre" version = "0.6.12" @@ -2496,6 +2482,20 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +[[package]] +name = "fibonacci-verifier-contract" +version = "0.1.0" +dependencies = [ + "borsh 1.5.1", + "hex", + "hex-literal 0.3.4", + "num-bigint 0.4.6", + "num-traits", + "sha2 0.10.8", + "solana-program", + "sp1-solana", +] + [[package]] name = "filetime" version = "0.2.25" @@ -2822,7 +2822,8 @@ dependencies = [ [[package]] name = "groth16-solana" -version = "0.0.3" +version = "0.0.4" +source = "git+https://github.com/yuwen01/groth16-solana-patch?rev=42ea919b62f90868a4003a9a55a471aae7929a81#42ea919b62f90868a4003a9a55a471aae7929a81" dependencies = [ "ark-bn254", "ark-ec", @@ -5821,9 +5822,9 @@ dependencies = [ [[package]] name = "scc" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836f1e0f4963ef5288b539b643b35e043e76a32d0f4e47e67febf69576527f50" +checksum = "553f8299af7450cda9a52d3a370199904e7a46b5ffd1bef187c4a6af3bb6db69" dependencies = [ "sdd", ] @@ -5873,9 +5874,9 @@ dependencies = [ [[package]] name = "sdd" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a7b59a5d9b0099720b417b6325d91a52cbf5b3dcb5041d864be53eefa58abc" +checksum = "49c1eeaf4b6a87c7479688c6d52b9f1153cedd3c489300564f932b065c6eab95" [[package]] name = "sec1" diff --git a/Cargo.toml b/Cargo.toml index f3e1922..74ce08c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ resolver = "2" # workspace sp1-solana = { path = "verifier" } -example-solana-contract = { path = "example/program" } +fibonacci-verifier-contract = { path = "example/program" } # solana solana-program = { git = "https://github.com/anza-xyz/agave", rev = "6e62af0f0de6a40e4e22628cbbcf63b1a6da560e" } diff --git a/example/README.md b/example/README.md index fc9d224..49fffae 100644 --- a/example/README.md +++ b/example/README.md @@ -31,9 +31,8 @@ RUST_LOG=info cargo run --release -- --prove ## Overview: Solana Program The code in [`program`](./program) is a simple Solana contract that verifies a -`SP1ProofFixture` using the `sp1-solana` crate. It costs roughly 280,000 compute units. - -**TODO** +`SP1ProofFixture` using the `sp1-solana` crate. It costs roughly 280,000 compute units. It also +demonstrates how to verify the sp1 program vkey and the public inputs. ### Running the Solana program diff --git a/example/program/Cargo.toml b/example/program/Cargo.toml index b83d4da..edc99da 100644 --- a/example/program/Cargo.toml +++ b/example/program/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "example-solana-contract" +name = "fibonacci-verifier-contract" version = "0.1.0" edition = "2021" publish = false diff --git a/example/program/src/lib.rs b/example/program/src/lib.rs index 443a04b..f39f38f 100644 --- a/example/program/src/lib.rs +++ b/example/program/src/lib.rs @@ -1,6 +1,6 @@ use borsh::BorshDeserialize; use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, msg, pubkey::Pubkey}; -use sp1_solana::{hash_public_inputs, verify_proof_fixture, SP1ProofFixture}; +use sp1_solana::{verify_proof_fixture, SP1ProofFixture}; #[cfg(not(feature = "no-entrypoint"))] use solana_program::entrypoint; @@ -9,7 +9,6 @@ use solana_program::entrypoint; entrypoint!(process_instruction); // Derived by running `cargo prove vkey --elf ../../sp1-program/elf/riscv32im-succinct-zkvm-elf` -// TODO: const FIBONACCI_VKEY_HASH: &str = "0083e8e370d7f0d1c463337f76c9a60b62ad7cc54c89329107c92c1e62097872"; @@ -29,22 +28,14 @@ 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); - - // Verify that the provided public inputs match the proof fixture's committed values digest. - 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()); - } + assert_eq!(FIBONACCI_VKEY_HASH, hex::encode(fixture.sp1_vkey_hash)); // Print out the public values. - if let Some(sp1_public_inputs) = &fixture.sp1_public_inputs { - let mut reader = sp1_public_inputs.as_slice(); - let n = u32::deserialize(&mut reader).unwrap(); - let a = u32::deserialize(&mut reader).unwrap(); - let b = u32::deserialize(&mut reader).unwrap(); - msg!("Public values: (n: {}, a: {}, b: {})", n, a, b); - } + let mut reader = fixture.sp1_public_inputs.as_slice(); + let n = u32::deserialize(&mut reader).unwrap(); + let a = u32::deserialize(&mut reader).unwrap(); + let b = u32::deserialize(&mut reader).unwrap(); + msg!("Public values: (n: {}, a: {}, b: {})", n, a, b); Ok(()) } diff --git a/example/script/Cargo.toml b/example/script/Cargo.toml index 69340b3..119a080 100644 --- a/example/script/Cargo.toml +++ b/example/script/Cargo.toml @@ -8,7 +8,7 @@ publish = false [dependencies] borsh.workspace = true sp1-solana = { workspace = true, features = ["sp1-serialize"] } -example-solana-contract = { workspace = true } +fibonacci-verifier-contract = { workspace = true } solana-program-test = { workspace = true } solana-sdk = { workspace = true } tokio = { workspace = true } diff --git a/example/script/build.rs b/example/script/build.rs index 66dca6a..9b5ca78 100644 --- a/example/script/build.rs +++ b/example/script/build.rs @@ -1,13 +1,3 @@ -use sp1_build::build_program; - fn main() { - // build_program_with_args( - // "../sp1-program", - // BuildArgs { - // output_directory: "../../elf".to_string(), - // elf_name: "fibonacci_elf".to_string(), - // ..Default::default() - // }, - // ); - build_program("../sp1-program"); + sp1_build::build_program("../sp1-program"); } diff --git a/example/script/src/main.rs b/example/script/src/main.rs index 170db29..4a3ebae 100644 --- a/example/script/src/main.rs +++ b/example/script/src/main.rs @@ -28,9 +28,9 @@ async fn run_example_instruction(fixture: SP1ProofFixture) { // Create program test environment let (banks_client, payer, recent_blockhash) = ProgramTest::new( - "example-solana-contract", + "fibonacci-verifier-contract", program_id, - processor!(example_solana_contract::process_instruction), + processor!(fibonacci_verifier_contract::process_instruction), ) .start() .await; @@ -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(sp1_proof_with_public_values, true); + let fixture = SP1ProofFixture::from(sp1_proof_with_public_values); let fixture_file = "../../proof-fixtures/fibonacci_fixture.bin"; fixture.save(&fixture_file).unwrap(); diff --git a/proof-fixtures/fibonacci_fixture.bin b/proof-fixtures/fibonacci_fixture.bin index 053129d556460ec61608587d81c2a8c76edca281..4fd44f32d26ec9db05493dca214a38730b34d5d2 100644 GIT binary patch delta 31 kcmcb@w1sH_qiB{U+ZrAQ1_luz&XrGRq4uzHaVq>QkpYFMoV4OOtI4 UBM$=ug9s4kN-;28=4N0301S^6DgXcg diff --git a/verifier/Cargo.toml b/verifier/Cargo.toml index 455f964..861c27b 100644 --- a/verifier/Cargo.toml +++ b/verifier/Cargo.toml @@ -3,7 +3,7 @@ name = "sp1-solana" version = "0.1.0" edition = "2021" authors = ["Bhargav Annem, Yuwen Zhang"] -description = "A Groth16 verifier implementation" +description = "Verifier for SP1 Groth16proofs on Solana" license = "MIT OR Apache-2.0" repository = "https://github.com/succinctlabs/groth16-solana" readme = "README.md" @@ -19,7 +19,7 @@ ark-bn254 = "0.4.0" ark-serialize = "0.4.2" ark-ff = "0.4.2" # groth16-solana = { git = "https://github.com/sp1-patches/groth16-solana", branch = "patch-v0.0.3" } -groth16-solana = { path = "../../groth16-solana-patch" } +groth16-solana = { git = "https://github.com/yuwen01/groth16-solana-patch", rev = "42ea919b62f90868a4003a9a55a471aae7929a81" } thiserror = "1.0.63" hex = { version = "0.4.3" } diff --git a/verifier/src/fixture.rs b/verifier/src/fixture.rs index 9d32d71..c4d70a1 100644 --- a/verifier/src/fixture.rs +++ b/verifier/src/fixture.rs @@ -13,12 +13,12 @@ use sha2::{Digest, Sha256}; pub struct SP1ProofFixture { /// The proof is 256 bytes. pub proof: [u8; 256], - /// The public inputs are 63 bytes. - 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>, + pub sp1_public_inputs: Vec, + /// The vkey hash of the underlying SP1 program. + pub sp1_vkey_hash: [u8; 32], } impl SP1ProofFixture { @@ -42,24 +42,21 @@ impl SP1ProofFixture { /// Retrieves the SP1 commited values digest from the public inputs. pub fn commited_values_digest(&self) -> [u8; 32] { - self.public_inputs[31..63].try_into().unwrap() + hash_public_inputs(&self.sp1_public_inputs) } - /// Retrieves the SP1 vkey hash from the public inputs. - /// - /// This is the vkey hash of the underlying SP1 program, not the Groth16 vkey hash. - pub fn vkey_hash(&self) -> String { - // Prepend a 0 to the first 31 bytes of the public inputs. - let mut padded_vkey_hash_bytes = vec![0]; - padded_vkey_hash_bytes.extend_from_slice(&self.public_inputs[0..31]); - let vkey_hash_bytes = padded_vkey_hash_bytes.as_slice(); - - // Convert the vkey hash bytes to a hex string - hex::encode(vkey_hash_bytes) + /// Formats public values for the Groth16 verifier. + pub fn groth16_public_values(&self) -> Vec { + let committed_values_digest = self.commited_values_digest(); + [ + self.sp1_vkey_hash[1..].to_vec(), + committed_values_digest.to_vec(), + ] + .concat() } } -/// Hashes the public inputs in order to match the groth16 verifier's format +/// Hashes the public inputs in the same format as the Groth16 verifier. pub fn hash_public_inputs(public_inputs: &[u8]) -> [u8; 32] { let mut result = Sha256::digest(public_inputs); @@ -83,7 +80,7 @@ pub fn verify_proof_fixture(fixture: &SP1ProofFixture, vk: &[u8]) -> Result<(), } // Verify the proof. - verify_proof_raw(&fixture.proof, &fixture.public_inputs, vk) + verify_proof_raw(&fixture.proof, &fixture.groth16_public_values(), vk) } #[cfg(feature = "sp1-serialize")] @@ -94,11 +91,8 @@ mod sp1_serialize { use super::SP1ProofFixture; /// Convert a SP1ProofWithPublicValues to a SP1ProofFixture. - impl SP1ProofFixture { - pub fn from_sp1( - sp1_proof_with_public_values: SP1ProofWithPublicValues, - use_public_values: bool, - ) -> Self { + impl From for SP1ProofFixture { + fn from(sp1_proof_with_public_values: SP1ProofWithPublicValues) -> Self { let proof = sp1_proof_with_public_values .proof .try_as_groth_16() @@ -111,63 +105,57 @@ mod sp1_serialize { .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 - }; + // To match the standard format, the 31 byte vkey hash is left padded with a 0 byte. + let mut padded_vkey_hash = vec![0]; + padded_vkey_hash.extend_from_slice(&vkey_hash); + let vkey_hash = padded_vkey_hash; 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, + sp1_public_inputs: sp1_proof_with_public_values.public_values.to_vec(), + sp1_vkey_hash: vkey_hash.try_into().unwrap(), } } } #[cfg(feature = "sp1-serialize")] #[test] - fn test_public_inputs() { + fn test_verify_from_sp1() { + use crate::{verify_proof_fixture, GROTH16_VK_BYTES}; + + // 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(); + + let fixture = SP1ProofFixture::from(sp1_proof_with_public_values); + + assert!(verify_proof_fixture(&fixture, &GROTH16_VK_BYTES).is_ok()); + } + + #[cfg(feature = "sp1-serialize")] + #[test] + fn test_hash_public_inputs_() { 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(); - let groth16_proof = sp1_proof_with_public_values - .clone() + let proof = sp1_proof_with_public_values .proof .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]; - 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); + .expect("Failed to convert proof to Groth16 proof"); - // let commited_values_digest: &String = &groth16_proof.public_inputs[1]; + let committed_values_digest = BigUint::from_str_radix(&proof.public_inputs[1], 10) + .unwrap() + .to_bytes_be(); - // Convert the SP1ProofWithPublicValues to a SP1ProofFixture. - let fixture = SP1ProofFixture::from_sp1(sp1_proof_with_public_values, false); - - // Verify the public inputs. assert_eq!( - &fixture.commited_values_digest(), - &hash_public_inputs(fixture.sp1_public_inputs.as_ref().unwrap()) + committed_values_digest, + hash_public_inputs(&sp1_proof_with_public_values.public_values.to_vec()) ); - - // Verify the vkey hash. - assert_eq!(fixture.vkey_hash(), vkey_hash_hex); } }