diff --git a/recursion/gnark-ffi/go/main.go b/recursion/gnark-ffi/go/main.go index ed782400f..9a06ce48c 100644 --- a/recursion/gnark-ffi/go/main.go +++ b/recursion/gnark-ffi/go/main.go @@ -72,6 +72,21 @@ func VerifyPlonkBn254(dataDir *C.char, proof *C.char, vkeyHash *C.char, commited return nil } +//export VerifyPlonkBn254Solidity +func VerifyPlonkBn254Solidity(dataDir *C.char, proof *C.char, vkeyHash *C.char, commitedValuesDigest *C.char) *C.char { + dataDirString := C.GoString(dataDir) + proofString := C.GoString(proof) + vkeyHashString := C.GoString(vkeyHash) + commitedValuesDigestString := C.GoString(commitedValuesDigest) + + err := sp1.VerifySolidity(dataDirString, proofString, vkeyHashString, commitedValuesDigestString) + if err != nil { + return C.CString(err.Error()) + } + return nil +} + + var testMutex = &sync.Mutex{} //export TestPlonkBn254 diff --git a/recursion/gnark-ffi/go/sp1/verify.go b/recursion/gnark-ffi/go/sp1/verify.go index 27c459c99..210bef5e1 100644 --- a/recursion/gnark-ffi/go/sp1/verify.go +++ b/recursion/gnark-ffi/go/sp1/verify.go @@ -6,7 +6,9 @@ import ( "os" "github.com/consensys/gnark-crypto/ecc" + "github.com/consensys/gnark-crypto/ecc/bn254/fr" "github.com/consensys/gnark/backend/plonk" + plonk_bn254 "github.com/consensys/gnark/backend/plonk/bn254" "github.com/consensys/gnark/frontend" "github.com/succinctlabs/sp1-recursion-gnark/sp1/babybear" ) @@ -56,3 +58,60 @@ func Verify(verifyCmdDataDir string, verifyCmdProof string, verifyCmdVkeyHash st err = plonk.Verify(proof, vk, publicWitness) return err } + +func VerifySolidity(verifyCmdDataDir string, verifyCmdProof string, verifyCmdVkeyHash string, verifyCmdCommitedValuesDigest string) error { + // Sanity check the required arguments have been provided. + if verifyCmdDataDir == "" { + panic("--data is required") + } + + // Decode the proof. + proofDecodedBytes, err := hex.DecodeString(verifyCmdProof) + if err != nil { + panic(err) + } + + // Proof contains 768 bytes worth of initial elements, followed by 32 bytes x nbCommits, followed by 64 bytes x nbCommits + nbCommits := (len(proofDecodedBytes) - 768) / 32 / 3; + + proof_bn254 := plonk_bn254.UnmarshalSolidity(proofDecodedBytes, nbCommits) + + // Read the verifier key. + vkFile, err := os.Open(verifyCmdDataDir + "/" + vkPath) + if err != nil { + panic(err) + } + var vk plonk_bn254.VerifyingKey + _, err = vk.ReadFrom(vkFile) + if err != nil { + panic(err) + } + + // Compute the public witness. + circuit := Circuit{ + Vars: []frontend.Variable{}, + Felts: []babybear.Variable{}, + Exts: []babybear.ExtensionVariable{}, + VkeyHash: verifyCmdVkeyHash, + CommitedValuesDigest: verifyCmdCommitedValuesDigest, + } + witness, err := frontend.NewWitness(&circuit, ecc.BN254.ScalarField()) + if err != nil { + panic(err) + } + publicWitness, err := witness.Public() + if err != nil { + panic(err) + } + + var witness_vec fr.Vector + if value, ok := publicWitness.Vector().(fr.Vector); ok { + witness_vec = value + } else { + panic("witness is not a vector") + } + + // Verify proof. + err = plonk_bn254.Verify(&proof_bn254, &vk, witness_vec) + return err +} \ No newline at end of file diff --git a/recursion/gnark-ffi/src/ffi/native.rs b/recursion/gnark-ffi/src/ffi/native.rs index 3e733cdd3..b4553d3e6 100644 --- a/recursion/gnark-ffi/src/ffi/native.rs +++ b/recursion/gnark-ffi/src/ffi/native.rs @@ -69,6 +69,35 @@ pub fn verify_plonk_bn254( } } +pub fn verify_plonk_bn254_solidity( + data_dir: &str, + proof: &str, + vkey_hash: &str, + committed_values_digest: &str, +) -> Result<(), String> { + let data_dir = CString::new(data_dir).expect("CString::new failed"); + let proof = CString::new(proof).expect("CString::new failed"); + let vkey_hash = CString::new(vkey_hash).expect("CString::new failed"); + let committed_values_digest = + CString::new(committed_values_digest).expect("CString::new failed"); + + let err_ptr = unsafe { + VerifyPlonkBn254Solidity( + data_dir.as_ptr() as *mut c_char, + proof.as_ptr() as *mut c_char, + vkey_hash.as_ptr() as *mut c_char, + committed_values_digest.as_ptr() as *mut c_char, + ) + }; + if err_ptr.is_null() { + Ok(()) + } else { + // Safety: The error message is returned from the go code and is guaranteed to be valid. + let err = unsafe { CString::from_raw(err_ptr) }; + Err(err.into_string().unwrap()) + } +} + pub fn test_plonk_bn254(witness_json: &str, constraints_json: &str) { unsafe { let witness_json = CString::new(witness_json).expect("CString::new failed");