diff --git a/Cargo.lock b/Cargo.lock index 75669021..25d3f052 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4859,6 +4859,12 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +[[package]] +name = "itoap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9028f49264629065d057f340a86acb84867925865f73bbf8d47b4d149a7e88b8" + [[package]] name = "jobserver" version = "0.1.26" @@ -9312,6 +9318,44 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "sailfish" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7519b7521780097b0183bb4b0c7c2165b924f5f1d44c3ef765bde8c2f8008fd1" +dependencies = [ + "itoap", + "ryu", + "sailfish-macros", + "version_check", +] + +[[package]] +name = "sailfish-compiler" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "535500faca492ee8054fbffdfca6447ca97fa495e0ede9f28fa473e1a44f9d5c" +dependencies = [ + "filetime", + "home", + "memchr", + "proc-macro2", + "quote", + "serde", + "syn 2.0.17", + "toml 0.7.4", +] + +[[package]] +name = "sailfish-macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06a95a6b8a0f59bf66f430a4ed37ece23fcefcd26898399573043e56fb202be2" +dependencies = [ + "proc-macro2", + "sailfish-compiler", +] + [[package]] name = "salsa20" version = "0.10.2" @@ -14466,6 +14510,20 @@ dependencies = [ "zstd", ] +[[package]] +name = "zkutils" +version = "0.1.0" +dependencies = [ + "assert_cmd", + "clap", + "ethers", + "sailfish", + "serde", + "serde_json", + "tempfile", + "tokio", +] + [[package]] name = "zstd" version = "0.11.2+zstd.1.5.2" diff --git a/Cargo.toml b/Cargo.toml index 5c894274..9caf98a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "runtime/sydney", "runtime/brooklyn", "runtime/runtime-common", + "zkutils", ] exclude = [ "examples/cross-vm-communication/evm-to-wasm/flipper", @@ -227,6 +228,9 @@ ark-relations = { version = "0.4.0", default-features = false } ark-serialize = { version = "0.4.1", default-features = false } ark-std = { version = "0.4.0", default-features = false } +# zk utils +sailfish = "0.8.0" + # Temporary, leaks the std feature in the non-std env. Until https://github.com/AstarNetwork/frontier/pull/84 is merged. [patch."https://github.com/AstarNetwork/frontier.git"] fp-account = { git = "https://github.com/GoldenGateGGX/frontier.git", branch = "polkadot-v0.9.40" } diff --git a/zkutils/Cargo.toml b/zkutils/Cargo.toml new file mode 100644 index 00000000..0d9fc7fa --- /dev/null +++ b/zkutils/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "zkutils" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clap = { workspace = true } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } +sailfish = { workspace = true } + +[dev-dependencies] +assert_cmd = { workspace = true } +tempfile = { workspace = true } +tokio = { workspace = true, features = ["macros", "time", "parking_lot", "rt"] } +ethers = { workspace = true } diff --git a/zkutils/src/main.rs b/zkutils/src/main.rs new file mode 100644 index 00000000..dcf00843 --- /dev/null +++ b/zkutils/src/main.rs @@ -0,0 +1,32 @@ +mod solidity; + +use clap::Parser; + +#[derive(Debug, clap::Parser)] +#[clap(about, version)] +pub struct CLI { + #[clap(subcommand)] + command: Commands, +} + +#[derive(Debug, clap::Subcommand)] +pub enum Commands { + #[clap(subcommand)] + Solidity(solidity::Commands), +} + +fn main() { + let cli = CLI::parse(); + + match cli.command { + Commands::Solidity(cmd) => match cmd { + solidity::Commands::Verifier(cmd) => { + let r = cmd.run(); + match r { + Err(e) => eprintln!("{}", e), + _ => {} + } + } + }, + } +} diff --git a/zkutils/src/solidity.rs b/zkutils/src/solidity.rs new file mode 100644 index 00000000..426d8ae7 --- /dev/null +++ b/zkutils/src/solidity.rs @@ -0,0 +1,61 @@ +use sailfish::TemplateOnce; +use std::{error::Error, fs, fs::File, io::Write, path::PathBuf}; + +#[derive(Debug, clap::Subcommand)] +pub enum Commands { + Verifier(VerifierCMD), +} + +#[derive(Debug, clap::Args)] +pub struct VerifierCMD { + #[arg(required = true)] + verification_key: PathBuf, + #[arg(required = false)] + #[clap(default_value = "verifier.sol")] + output: PathBuf, +} + +impl VerifierCMD { + pub fn run(&self) -> Result<(), Box> { + let b = fs::read(self.verification_key.clone())?; + let vk: VerificationKey = serde_json::from_slice(&b)?; + + let ctx = TemplateContext { + vk_alpha_1: vk.vk_alpha_1, + vk_beta_2: vk.vk_beta_2, + vk_gamma_2: vk.vk_gamma_2, + vk_delta_2: vk.vk_delta_2, + ic: vk.ic, + }; + let rendered = ctx.render_once()?; + + let mut file = File::create(self.output.clone())?; + file.write_all(rendered.as_bytes())?; + Ok(()) + } +} + +#[derive(TemplateOnce)] +#[template(path = "verifier_groth16.sol.stpl")] +struct TemplateContext { + vk_alpha_1: [String; 3], + vk_beta_2: [[String; 2]; 3], + vk_gamma_2: [[String; 2]; 3], + vk_delta_2: [[String; 2]; 3], + ic: Vec<[String; 3]>, +} + +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct VerificationKey { + protocol: String, + curve: String, + #[serde(rename = "nPublic")] + n_public: i128, + vk_alpha_1: [String; 3], + vk_beta_2: [[String; 2]; 3], + vk_gamma_2: [[String; 2]; 3], + vk_delta_2: [[String; 2]; 3], + vk_alphabeta_12: [[[String; 2]; 3]; 2], + #[serde(rename = "IC")] + ic: Vec<[String; 3]>, +} diff --git a/zkutils/templates/verifier_groth16.sol.stpl b/zkutils/templates/verifier_groth16.sol.stpl new file mode 100644 index 00000000..0467b7d8 --- /dev/null +++ b/zkutils/templates/verifier_groth16.sol.stpl @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-3.0 +/* + Copyright 2021 0KIMS association. + + This file is generated with [snarkJS](https://github.com/iden3/snarkjs). + + snarkJS is a free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + snarkJS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License + along with snarkJS. If not, see . +*/ + +pragma solidity >=0.7.0 <0.9.0; + +/** +* @title ZKGroth16Verify Interface +* +* The interface through which solidity contracts will interact with ZKGroth16Verify +* Address : 0x0000000000000000000000000000000000008888 +*/ +interface IZKGroth16Verify { + /** + * @notice Verifies a Groth16 zkSNARK proof. + * + * @param proof_a The first element of the zkSNARK proof. + * @param proof_b The second element of the zkSNARK proof. + * @param proof_c The third element of the zkSNARK proof. + * @param vk_alpha The first element of the verification key. + * @param vk_beta The second element of the verification key. + * @param vk_gamma The third element of the verification key. + * @param vk_delta The fourth element of the verification key. + * @param vk_ic The array of the rest of the elements of the verification key. + * @param input The array of public inputs to the zkSNARK. + * + * @return valid A boolean value representing whether the proof is valid or not. + */ + function verify( + uint[2] memory proof_a, + uint[2][2] memory proof_b, + uint[2] memory proof_c, + uint[2] memory vk_alpha, + uint[2][2] memory vk_beta, + uint[2][2] memory vk_gamma, + uint[2][2] memory vk_delta, + uint[2][] memory vk_ic, + uint[] memory input + ) external view returns (bool valid); +} + +contract Groth16Verifier { + // Verification Key data + uint256[2] vk_alpha = [ + <%=vk_alpha_1[0]%>, + <%=vk_alpha_1[1]%> + ]; + + uint256[2][2] vk_beta = [ + [ + <%=vk_beta_2[0][0]%>, + <%=vk_beta_2[0][1]%> + ], + [ + <%=vk_beta_2[1][0]%>, + <%=vk_beta_2[1][1]%> + ] + ]; + uint256[2][2] vk_gamma = [ + [ + <%=vk_gamma_2[0][0]%>, + <%=vk_gamma_2[0][1]%> + ], + [ + <%=vk_gamma_2[1][0]%>, + <%=vk_gamma_2[1][1]%> + ] + ]; + uint256[2][2] vk_delta = [ + [ + <%=vk_delta_2[0][0]%>, + <%=vk_delta_2[0][1]%> + ], + [ + <%=vk_delta_2[1][0]%>, + <%=vk_delta_2[1][1]%> + ] + ]; + + uint256[2][] vk_ic = [<% for i in 0..ic.len() { %> + [ + <%=ic[i][0]%>, + <%=ic[i][1]%> + ]<% if i != ic.len()-1 {%>,<% } %><% } %> + ]; + + function verifyProof(uint[2] calldata _pA, uint[2][2] calldata _pB, uint[2] calldata _pC, uint[<%=ic.len()-1%>] calldata _pubSignals) public view returns (bool) { + uint[] memory input = new uint[](_pubSignals.length); + <% for i in 0..ic.len()-1 { %>input[<%=i%>] = _pubSignals[<%=i%>];<% } %> + + uint[2][2] memory pB; + pB[0][0] = _pB[0][1]; + pB[0][1] = _pB[0][0]; + pB[1][0] = _pB[1][1]; + pB[1][1] = _pB[1][0]; + + return IZKGroth16Verify(0x0000000000000000000000000000000000008888).verify( + _pA, + pB, + _pC, + vk_alpha, + vk_beta, + vk_gamma, + vk_delta, + vk_ic, + input + ); + } + } diff --git a/zkutils/tests/circuits/IGroth16Verifier.json b/zkutils/tests/circuits/IGroth16Verifier.json new file mode 100644 index 00000000..13d1b019 --- /dev/null +++ b/zkutils/tests/circuits/IGroth16Verifier.json @@ -0,0 +1,403 @@ +{ + "abi": [ + { + "inputs": [ + { + "internalType": "uint256[2]", + "name": "_pA", + "type": "uint256[2]" + }, + { + "internalType": "uint256[2][2]", + "name": "_pB", + "type": "uint256[2][2]" + }, + { + "internalType": "uint256[2]", + "name": "_pC", + "type": "uint256[2]" + }, + { + "internalType": "uint256[1]", + "name": "_pubSignals", + "type": "uint256[1]" + } + ], + "name": "verifyProof", + "outputs": [ + { + "internalType": "bool", + "name": "valid", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + } + ], + "bytecode": { + "object": "0x", + "sourceMap": "", + "linkReferences": {} + }, + "deployedBytecode": { + "object": "0x", + "sourceMap": "", + "linkReferences": {} + }, + "methodIdentifiers": { + "verifyProof(uint256[2],uint256[2][2],uint256[2],uint256[1])": "43753b4d" + }, + "ast": { + "absolutePath": "tests/circuits/iverifier.sol", + "id": 28, + "exportedSymbols": { + "IGroth16Verifier": [ + 27 + ] + }, + "nodeType": "SourceUnit", + "src": "798:758:0", + "nodes": [ + { + "id": 1, + "nodeType": "PragmaDirective", + "src": "798:31:0", + "nodes": [], + "literals": [ + "solidity", + ">=", + "0.7", + ".0", + "<", + "0.9", + ".0" + ] + }, + { + "id": 27, + "nodeType": "ContractDefinition", + "src": "929:627:0", + "nodes": [ + { + "id": 26, + "nodeType": "FunctionDefinition", + "src": "1361:193:0", + "nodes": [], + "documentation": { + "id": 3, + "nodeType": "StructuredDocumentation", + "src": "962:394:0", + "text": " @notice Verifies a Groth16 zkSNARK proof.\n @param _pA The first element of the zkSNARK proof.\n @param _pB The second element of the zkSNARK proof.\n @param _pC The third element of the zkSNARK proof.\n @param _pubSignals The array of public inputs to the zkSNARK.\n @return valid A boolean value representing whether the proof is valid or not." + }, + "functionSelector": "43753b4d", + "implemented": false, + "kind": "function", + "modifiers": [], + "name": "verifyProof", + "nameLocation": "1370:11:0", + "parameters": { + "id": 22, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 7, + "mutability": "mutable", + "name": "_pA", + "nameLocation": "1408:3:0", + "nodeType": "VariableDeclaration", + "scope": 26, + "src": "1391:20:0", + "stateVariable": false, + "storageLocation": "calldata", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_uint256_$2_calldata_ptr", + "typeString": "uint256[2]" + }, + "typeName": { + "baseType": { + "id": 4, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "1391:4:0", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "id": 6, + "length": { + "hexValue": "32", + "id": 5, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "1396:1:0", + "typeDescriptions": { + "typeIdentifier": "t_rational_2_by_1", + "typeString": "int_const 2" + }, + "value": "2" + }, + "nodeType": "ArrayTypeName", + "src": "1391:7:0", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_uint256_$2_storage_ptr", + "typeString": "uint256[2]" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 13, + "mutability": "mutable", + "name": "_pB", + "nameLocation": "1441:3:0", + "nodeType": "VariableDeclaration", + "scope": 26, + "src": "1421:23:0", + "stateVariable": false, + "storageLocation": "calldata", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_array$_t_uint256_$2_calldata_ptr_$2_calldata_ptr", + "typeString": "uint256[2][2]" + }, + "typeName": { + "baseType": { + "baseType": { + "id": 8, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "1421:4:0", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "id": 10, + "length": { + "hexValue": "32", + "id": 9, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "1426:1:0", + "typeDescriptions": { + "typeIdentifier": "t_rational_2_by_1", + "typeString": "int_const 2" + }, + "value": "2" + }, + "nodeType": "ArrayTypeName", + "src": "1421:7:0", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_uint256_$2_storage_ptr", + "typeString": "uint256[2]" + } + }, + "id": 12, + "length": { + "hexValue": "32", + "id": 11, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "1429:1:0", + "typeDescriptions": { + "typeIdentifier": "t_rational_2_by_1", + "typeString": "int_const 2" + }, + "value": "2" + }, + "nodeType": "ArrayTypeName", + "src": "1421:10:0", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_array$_t_uint256_$2_storage_$2_storage_ptr", + "typeString": "uint256[2][2]" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 17, + "mutability": "mutable", + "name": "_pC", + "nameLocation": "1471:3:0", + "nodeType": "VariableDeclaration", + "scope": 26, + "src": "1454:20:0", + "stateVariable": false, + "storageLocation": "calldata", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_uint256_$2_calldata_ptr", + "typeString": "uint256[2]" + }, + "typeName": { + "baseType": { + "id": 14, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "1454:4:0", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "id": 16, + "length": { + "hexValue": "32", + "id": 15, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "1459:1:0", + "typeDescriptions": { + "typeIdentifier": "t_rational_2_by_1", + "typeString": "int_const 2" + }, + "value": "2" + }, + "nodeType": "ArrayTypeName", + "src": "1454:7:0", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_uint256_$2_storage_ptr", + "typeString": "uint256[2]" + } + }, + "visibility": "internal" + }, + { + "constant": false, + "id": 21, + "mutability": "mutable", + "name": "_pubSignals", + "nameLocation": "1501:11:0", + "nodeType": "VariableDeclaration", + "scope": 26, + "src": "1484:28:0", + "stateVariable": false, + "storageLocation": "calldata", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_uint256_$1_calldata_ptr", + "typeString": "uint256[1]" + }, + "typeName": { + "baseType": { + "id": 18, + "name": "uint", + "nodeType": "ElementaryTypeName", + "src": "1484:4:0", + "typeDescriptions": { + "typeIdentifier": "t_uint256", + "typeString": "uint256" + } + }, + "id": 20, + "length": { + "hexValue": "31", + "id": 19, + "isConstant": false, + "isLValue": false, + "isPure": true, + "kind": "number", + "lValueRequested": false, + "nodeType": "Literal", + "src": "1489:1:0", + "typeDescriptions": { + "typeIdentifier": "t_rational_1_by_1", + "typeString": "int_const 1" + }, + "value": "1" + }, + "nodeType": "ArrayTypeName", + "src": "1484:7:0", + "typeDescriptions": { + "typeIdentifier": "t_array$_t_uint256_$1_storage_ptr", + "typeString": "uint256[1]" + } + }, + "visibility": "internal" + } + ], + "src": "1381:137:0" + }, + "returnParameters": { + "id": 25, + "nodeType": "ParameterList", + "parameters": [ + { + "constant": false, + "id": 24, + "mutability": "mutable", + "name": "valid", + "nameLocation": "1547:5:0", + "nodeType": "VariableDeclaration", + "scope": 26, + "src": "1542:10:0", + "stateVariable": false, + "storageLocation": "default", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + }, + "typeName": { + "id": 23, + "name": "bool", + "nodeType": "ElementaryTypeName", + "src": "1542:4:0", + "typeDescriptions": { + "typeIdentifier": "t_bool", + "typeString": "bool" + } + }, + "visibility": "internal" + } + ], + "src": "1541:12:0" + }, + "scope": 27, + "stateMutability": "view", + "virtual": false, + "visibility": "external" + } + ], + "abstract": false, + "baseContracts": [], + "canonicalName": "IGroth16Verifier", + "contractDependencies": [], + "contractKind": "interface", + "documentation": { + "id": 2, + "nodeType": "StructuredDocumentation", + "src": "831:97:0", + "text": " @title ZKGroth16Verify Interface\n The interface of snarkjs groth16 solidity verifier." + }, + "fullyImplemented": false, + "linearizedBaseContracts": [ + 27 + ], + "name": "IGroth16Verifier", + "nameLocation": "939:16:0", + "scope": 28, + "usedErrors": [] + } + ], + "license": "GPL-3.0" + }, + "id": 0 +} \ No newline at end of file diff --git a/zkutils/tests/circuits/iverifier.sol b/zkutils/tests/circuits/iverifier.sol new file mode 100644 index 00000000..f59d017e --- /dev/null +++ b/zkutils/tests/circuits/iverifier.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-3.0 +/* + Copyright 2021 0KIMS association. + + This file is generated with [snarkJS](https://github.com/iden3/snarkjs). + + snarkJS is a free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + snarkJS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public + License for more details. + + You should have received a copy of the GNU General Public License + along with snarkJS. If not, see . +*/ + +pragma solidity >=0.7.0 <0.9.0; + +/** +* @title ZKGroth16Verify Interface +* +* The interface of snarkjs groth16 solidity verifier. +*/ +interface IGroth16Verifier { + /** + * @notice Verifies a Groth16 zkSNARK proof. + * + * @param _pA The first element of the zkSNARK proof. + * @param _pB The second element of the zkSNARK proof. + * @param _pC The third element of the zkSNARK proof. + * @param _pubSignals The array of public inputs to the zkSNARK. + * + * @return valid A boolean value representing whether the proof is valid or not. + */ + function verifyProof( + uint[2] calldata _pA, + uint[2][2] calldata _pB, + uint[2] calldata _pC, + uint[1] calldata _pubSignals + ) external view returns (bool valid); +} diff --git a/zkutils/tests/circuits/verification_key.json b/zkutils/tests/circuits/verification_key.json new file mode 100644 index 00000000..14bdb2b3 --- /dev/null +++ b/zkutils/tests/circuits/verification_key.json @@ -0,0 +1,94 @@ +{ + "protocol": "groth16", + "curve": "bn128", + "nPublic": 1, + "vk_alpha_1": [ + "4786445708417484569047734683773865445840733821627574372874109551763626892996", + "13567452170662962359511035697378190173307572893903021783617017880252184547463", + "1" + ], + "vk_beta_2": [ + [ + "14310594066184049018332259151567535352975132326207903506938941044943793913703", + "19275856504056202708486139858498008379777616510161159092039265089014663688520" + ], + [ + "16462618123117127006882005499171657032820075458370882424692325397427723097121", + "19233376726693720092305756970776978350076935178014454281621934524082028583195" + ], + [ + "1", + "0" + ] + ], + "vk_gamma_2": [ + [ + "10857046999023057135944570762232829481370756359578518086990519993285655852781", + "11559732032986387107991004021392285783925812861821192530917403151452391805634" + ], + [ + "8495653923123431417604973247489272438418190587263600148770280649306958101930", + "4082367875863433681332203403145435568316851327593401208105741076214120093531" + ], + [ + "1", + "0" + ] + ], + "vk_delta_2": [ + [ + "18411448908696179195683862267692146248101362472723237234843971506292814393946", + "757803288137921027406892746314919845177514200177341309167023037552637910362" + ], + [ + "13794705780962475147891606615004213544743290806025755298807074735040395073266", + "20142487388866036983117976313747897098669421017338626369633808156118548447690" + ], + [ + "1", + "0" + ] + ], + "vk_alphabeta_12": [ + [ + [ + "20088009687937043395895260981577373361501304700534609136186158232790353014692", + "20309382159446051729067451620950179775042405696730360919440658445437685352512" + ], + [ + "16517695620032934148671112375719619443166837387461149503550911642259843122058", + "6704027029523660631573394103208176290548449053449490428279329594311561075517" + ], + [ + "12375858579402609156961739162536058310870849749947981226347215529460785322076", + "7058437959778844874605523938471070107700821222721430922589589173890441462347" + ] + ], + [ + [ + "1875906370441896357867895382913067707264609223873968772072555741009912932510", + "14673284212014687232201056935878160179882986322814632185165741534780373864868" + ], + [ + "21492383350893453912866181488711781533951683477476803411161138775012268288692", + "3751004287800111961885113337114887671312971976237160966843881387591950430282" + ], + [ + "18652078973176360033532890873154956809649570654507822360430502901138328464825", + "19774266231258724042358649639573390435423321297585008943249743208829524931628" + ] + ] + ], + "IC": [ + [ + "20769250752710047666844123000156646370107479448976213249546758926367826021215", + "13946557402815137577780627735443134736376122099557764871172525744608364348961", + "1" + ], + [ + "958800985699117902663919327090649125143253227048981711598996309327069184792", + "13048280471082016137610068167919886233845814044825613700907505364319880670805", + "1" + ] + ] +} diff --git a/zkutils/tests/evm_zk_verify_precompile.rs b/zkutils/tests/evm_zk_verify_precompile.rs new file mode 100644 index 00000000..b433e005 --- /dev/null +++ b/zkutils/tests/evm_zk_verify_precompile.rs @@ -0,0 +1,199 @@ +use assert_cmd::cargo::cargo_bin; +use ethers::{prelude::*, solc::ProjectPathsConfig}; +use std::{ + io::{BufRead, BufReader, Read}, + ops::{Deref, DerefMut}, + process::{Child, Command, Stdio}, +}; +use tempfile::tempdir; + +#[cfg(not(feature = "brooklyn"))] +const CHAIN_ID: u64 = 8886u64; +#[cfg(feature = "brooklyn")] +const CHAIN_ID: u64 = 888866u64; + +#[tokio::test] +async fn evm_zk_verify_test1() -> Result<(), Box> { + let base_path = tempdir().expect("could not create a temp dir"); + + let cmd = Command::new(cargo_bin("ggxchain-node")) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .args(["--dev"]) + .arg("-d") + .arg(base_path.path()) + .spawn()?; + + let mut child = KillChildOnDrop(cmd); + + let std_err = child.stderr.take().unwrap(); + let mut buff = BufReader::new(std_err); + + let mut b: [u8; 2048] = [0; 2048]; + buff.read_exact(&mut b)?; + let v = b.to_vec(); + + let (http, _ws) = find_ws_http(&mut v.as_slice())?; + + let temp_dir = tempdir()?; + let temp_path = temp_dir.path(); + + let zk_cmd_out = Command::new(cargo_bin("zkutils")) + .arg("solidity") + .arg("verifier") + .args([ + "tests/circuits/verification_key.json", + temp_path + .join("verifier.sol") + .to_str() + .ok_or("path join error")?, + ]) + .output()?; + assert!(zk_cmd_out.status.success()); + + let project = Project::builder() + .paths( + ProjectPathsConfig::builder() + .sources(temp_path) + .artifacts(temp_path.join("build")) + .cache(temp_path.join("cache")) + .build()?, + ) + .build()?; + let compile_output = project.compile()?; + + let provider: Provider = Provider::::try_from(http)?; // Change to correct network + // Do not include the private key in plain text in any produciton code. This is just for demonstration purposes + let wallet: LocalWallet = "0x01ab6e801c06e59ca97a14fc0a1978b27fa366fc87450e0b65459dd3515b7391" + .parse::()? + .with_chain_id(CHAIN_ID); // Change to correct network + let client = SignerMiddleware::new(provider.clone(), wallet.clone()); + + let contract = compile_output + .find_first("Groth16Verifier") + .ok_or("no contract")?; + let (abi, bytecode, _) = contract.clone().into_parts(); + + let factory = ContractFactory::new( + abi.ok_or("no abi")?.clone(), + bytecode.ok_or("no bytecode")?, + client.clone().into(), + ); + let deployer = factory.deploy(())?; + let deployed_contract = deployer.clone().legacy().send().await?; + + abigen!(Groth16Verifier, "tests/circuits/IGroth16Verifier.json",); + let groth16_verifier = Groth16Verifier::new(deployed_contract.address(), client.clone().into()); + + // ["0x27e74cfd62ff9651a900e687aff47585ce893fbdfcec332ef8a7c8f57f1d9df2", "0x2c195dd118f2a838a0098c1ad5d4adb825986afc466a360c4828c8fb766ff48a"], + // [["0x027f05b1f9b7a5c703e5fb707a6832fa1444c96e5a28ac6a6cda4f9784a7a0cf", "0x0bb52100fd0050a99070d77758cb2022bc41bb0e7791dbfff22f2fb88f751d46"], + // ["0x2b9ca698dc3d61c83bcd6bf8899a34ea0af8fbfb19f53f37dbe1a4b4f9e0c261", "0x0417d9b66984608d360c7fa22fcd9dc0d29b9adfe5f8c8f5ae68aede69bbcbe9"]], + // ["0x1a8a474fbdcb2888fa2787bc4425c37d5075f21ccf3a68d704c4173bbec24fa2", "0x2a30a953b5543795c5cb6c995ca862d7a9d1befcc7f304c342a27424528481ed"], + // ["0x110d778eaf8b8ef7ac10f8ac239a14df0eb292a8d1b71340d527b26301a9ab08"] + let p_a = [ + U256::from_str_radix( + "0x27e74cfd62ff9651a900e687aff47585ce893fbdfcec332ef8a7c8f57f1d9df2", + 16, + )?, + U256::from_str_radix( + "0x2c195dd118f2a838a0098c1ad5d4adb825986afc466a360c4828c8fb766ff48a", + 16, + )?, + ]; + let p_b = [ + [ + U256::from_str_radix( + "0x027f05b1f9b7a5c703e5fb707a6832fa1444c96e5a28ac6a6cda4f9784a7a0cf", + 16, + )?, + U256::from_str_radix( + "0x0bb52100fd0050a99070d77758cb2022bc41bb0e7791dbfff22f2fb88f751d46", + 16, + )?, + ], + [ + U256::from_str_radix( + "0x2b9ca698dc3d61c83bcd6bf8899a34ea0af8fbfb19f53f37dbe1a4b4f9e0c261", + 16, + )?, + U256::from_str_radix( + "0x0417d9b66984608d360c7fa22fcd9dc0d29b9adfe5f8c8f5ae68aede69bbcbe9", + 16, + )?, + ], + ]; + let p_c = [ + U256::from_str_radix( + "0x1a8a474fbdcb2888fa2787bc4425c37d5075f21ccf3a68d704c4173bbec24fa2", + 16, + )?, + U256::from_str_radix( + "0x2a30a953b5543795c5cb6c995ca862d7a9d1befcc7f304c342a27424528481ed", + 16, + )?, + ]; + let pub_signals = [U256::from_str_radix( + "0x110d778eaf8b8ef7ac10f8ac239a14df0eb292a8d1b71340d527b26301a9ab08", + 16, + )?]; + + let verify_call = groth16_verifier.verify_proof(p_a, p_b, p_c, pub_signals); + let valid = verify_call.call().await?; + assert!(valid, "proof should be valid"); + + child.kill()?; + let _exit_code = child.wait()?; + Ok(()) +} + +fn find_ws_http(reader: &mut impl Read) -> Result<(String, String), Box> { + let mut http: Option = None; + let mut ws: Option = None; + + let buff = BufReader::new(reader); + let lines = buff.lines().collect::, _>>()?; + + for line in lines.into_iter() { + if let Some((_, s)) = line.split_once("Running JSON-RPC HTTP server: addr=") { + let addr = s.split_once(",").ok_or("failed to split http line")?.0; + + http = Some(format!("http://{}", addr)); + } + if let Some((_, s)) = line.split_once("Running JSON-RPC WS server: addr=") { + let addr = s.split_once(",").ok_or("failed to split ws line")?.0; + + ws = Some(format!("ws://{}", addr)); + } + + if http.is_some() && ws.is_some() { + break; + } + } + + Ok(( + http.ok_or("http address not found")?, + ws.ok_or("ws address not found")?, + )) +} + +pub struct KillChildOnDrop(pub Child); + +impl Drop for KillChildOnDrop { + fn drop(&mut self) { + let _ = self.0.kill(); + } +} + +impl Deref for KillChildOnDrop { + type Target = Child; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for KillChildOnDrop { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +}